curl --request POST \
--url https://api.perceptron.inc/v1/chat/completions/multilook \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"context": [
{
"content": "<string>",
"role": "system"
}
],
"model": "<string>",
"prompts": [
"<string>"
],
"frequency_penalty": 0,
"max_completion_tokens": 1,
"n": 1,
"presence_penalty": 0,
"temperature": 1,
"top_k": 1,
"top_p": 0,
"vision_config": {
"enable_audio_in_video": true,
"enable_thinking": true
}
}
'import requests
url = "https://api.perceptron.inc/v1/chat/completions/multilook"
payload = {
"context": [
{
"content": "<string>",
"role": "system"
}
],
"model": "<string>",
"prompts": ["<string>"],
"frequency_penalty": 0,
"max_completion_tokens": 1,
"n": 1,
"presence_penalty": 0,
"temperature": 1,
"top_k": 1,
"top_p": 0,
"vision_config": {
"enable_audio_in_video": True,
"enable_thinking": True
}
}
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify({
context: [{content: '<string>', role: 'system'}],
model: '<string>',
prompts: ['<string>'],
frequency_penalty: 0,
max_completion_tokens: 1,
n: 1,
presence_penalty: 0,
temperature: 1,
top_k: 1,
top_p: 0,
vision_config: {enable_audio_in_video: true, enable_thinking: true}
})
};
fetch('https://api.perceptron.inc/v1/chat/completions/multilook', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.perceptron.inc/v1/chat/completions/multilook",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'context' => [
[
'content' => '<string>',
'role' => 'system'
]
],
'model' => '<string>',
'prompts' => [
'<string>'
],
'frequency_penalty' => 0,
'max_completion_tokens' => 1,
'n' => 1,
'presence_penalty' => 0,
'temperature' => 1,
'top_k' => 1,
'top_p' => 0,
'vision_config' => [
'enable_audio_in_video' => true,
'enable_thinking' => true
]
]),
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>",
"Content-Type: application/json"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://api.perceptron.inc/v1/chat/completions/multilook"
payload := strings.NewReader("{\n \"context\": [\n {\n \"content\": \"<string>\",\n \"role\": \"system\"\n }\n ],\n \"model\": \"<string>\",\n \"prompts\": [\n \"<string>\"\n ],\n \"frequency_penalty\": 0,\n \"max_completion_tokens\": 1,\n \"n\": 1,\n \"presence_penalty\": 0,\n \"temperature\": 1,\n \"top_k\": 1,\n \"top_p\": 0,\n \"vision_config\": {\n \"enable_audio_in_video\": true,\n \"enable_thinking\": true\n }\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Authorization", "Bearer <token>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://api.perceptron.inc/v1/chat/completions/multilook")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"context\": [\n {\n \"content\": \"<string>\",\n \"role\": \"system\"\n }\n ],\n \"model\": \"<string>\",\n \"prompts\": [\n \"<string>\"\n ],\n \"frequency_penalty\": 0,\n \"max_completion_tokens\": 1,\n \"n\": 1,\n \"presence_penalty\": 0,\n \"temperature\": 1,\n \"top_k\": 1,\n \"top_p\": 0,\n \"vision_config\": {\n \"enable_audio_in_video\": true,\n \"enable_thinking\": true\n }\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.perceptron.inc/v1/chat/completions/multilook")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"context\": [\n {\n \"content\": \"<string>\",\n \"role\": \"system\"\n }\n ],\n \"model\": \"<string>\",\n \"prompts\": [\n \"<string>\"\n ],\n \"frequency_penalty\": 0,\n \"max_completion_tokens\": 1,\n \"n\": 1,\n \"presence_penalty\": 0,\n \"temperature\": 1,\n \"top_k\": 1,\n \"top_p\": 0,\n \"vision_config\": {\n \"enable_audio_in_video\": true,\n \"enable_thinking\": true\n }\n}"
response = http.request(request)
puts response.read_body{
"id": "<string>",
"model": "<string>",
"object": "<string>",
"results": [
{
"prompt_index": 1,
"completions": [
{
"index": 1,
"message": {
"content": "<string>",
"role": "system",
"reasoning_content": "<string>",
"tool_calls": [
{
"function": {
"arguments": "<string>",
"name": "<string>"
},
"id": "<string>",
"type": "function"
}
]
},
"finish_reason": "stop"
}
],
"error": {
"message": "<string>",
"code": "<string>",
"type": "<string>"
},
"usage": {
"completion_tokens": 1
}
}
],
"usage": {
"completion_tokens": 1,
"prompt_tokens": 1,
"prompt_tokens_details": {
"audio_tokens": 1,
"cached_tokens": 1
},
"total_tokens": 1
}
}{
"error": {
"message": "Invalid n: 12. Expected a value between 1 and 8.",
"type": "invalid_request_error",
"param": null,
"code": null
}
}{
"error": {
"message": "Invalid API key",
"type": "authentication_error",
"param": null,
"code": null
}
}{
"error": {
"message": "Organization rate limit exceeded (150 requests/minute). Please retry after 30 seconds.",
"type": "rate_limit_error",
"param": null,
"code": "rate_limit_exceeded"
}
}{
"error": {
"message": "The server had an error while processing your request.",
"type": "server_error",
"param": null,
"code": null
}
}Multilook Chat Completions
Run several independent prompts over one shared, prefilled context.
curl --request POST \
--url https://api.perceptron.inc/v1/chat/completions/multilook \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"context": [
{
"content": "<string>",
"role": "system"
}
],
"model": "<string>",
"prompts": [
"<string>"
],
"frequency_penalty": 0,
"max_completion_tokens": 1,
"n": 1,
"presence_penalty": 0,
"temperature": 1,
"top_k": 1,
"top_p": 0,
"vision_config": {
"enable_audio_in_video": true,
"enable_thinking": true
}
}
'import requests
url = "https://api.perceptron.inc/v1/chat/completions/multilook"
payload = {
"context": [
{
"content": "<string>",
"role": "system"
}
],
"model": "<string>",
"prompts": ["<string>"],
"frequency_penalty": 0,
"max_completion_tokens": 1,
"n": 1,
"presence_penalty": 0,
"temperature": 1,
"top_k": 1,
"top_p": 0,
"vision_config": {
"enable_audio_in_video": True,
"enable_thinking": True
}
}
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify({
context: [{content: '<string>', role: 'system'}],
model: '<string>',
prompts: ['<string>'],
frequency_penalty: 0,
max_completion_tokens: 1,
n: 1,
presence_penalty: 0,
temperature: 1,
top_k: 1,
top_p: 0,
vision_config: {enable_audio_in_video: true, enable_thinking: true}
})
};
fetch('https://api.perceptron.inc/v1/chat/completions/multilook', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.perceptron.inc/v1/chat/completions/multilook",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'context' => [
[
'content' => '<string>',
'role' => 'system'
]
],
'model' => '<string>',
'prompts' => [
'<string>'
],
'frequency_penalty' => 0,
'max_completion_tokens' => 1,
'n' => 1,
'presence_penalty' => 0,
'temperature' => 1,
'top_k' => 1,
'top_p' => 0,
'vision_config' => [
'enable_audio_in_video' => true,
'enable_thinking' => true
]
]),
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>",
"Content-Type: application/json"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://api.perceptron.inc/v1/chat/completions/multilook"
payload := strings.NewReader("{\n \"context\": [\n {\n \"content\": \"<string>\",\n \"role\": \"system\"\n }\n ],\n \"model\": \"<string>\",\n \"prompts\": [\n \"<string>\"\n ],\n \"frequency_penalty\": 0,\n \"max_completion_tokens\": 1,\n \"n\": 1,\n \"presence_penalty\": 0,\n \"temperature\": 1,\n \"top_k\": 1,\n \"top_p\": 0,\n \"vision_config\": {\n \"enable_audio_in_video\": true,\n \"enable_thinking\": true\n }\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Authorization", "Bearer <token>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://api.perceptron.inc/v1/chat/completions/multilook")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"context\": [\n {\n \"content\": \"<string>\",\n \"role\": \"system\"\n }\n ],\n \"model\": \"<string>\",\n \"prompts\": [\n \"<string>\"\n ],\n \"frequency_penalty\": 0,\n \"max_completion_tokens\": 1,\n \"n\": 1,\n \"presence_penalty\": 0,\n \"temperature\": 1,\n \"top_k\": 1,\n \"top_p\": 0,\n \"vision_config\": {\n \"enable_audio_in_video\": true,\n \"enable_thinking\": true\n }\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.perceptron.inc/v1/chat/completions/multilook")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"context\": [\n {\n \"content\": \"<string>\",\n \"role\": \"system\"\n }\n ],\n \"model\": \"<string>\",\n \"prompts\": [\n \"<string>\"\n ],\n \"frequency_penalty\": 0,\n \"max_completion_tokens\": 1,\n \"n\": 1,\n \"presence_penalty\": 0,\n \"temperature\": 1,\n \"top_k\": 1,\n \"top_p\": 0,\n \"vision_config\": {\n \"enable_audio_in_video\": true,\n \"enable_thinking\": true\n }\n}"
response = http.request(request)
puts response.read_body{
"id": "<string>",
"model": "<string>",
"object": "<string>",
"results": [
{
"prompt_index": 1,
"completions": [
{
"index": 1,
"message": {
"content": "<string>",
"role": "system",
"reasoning_content": "<string>",
"tool_calls": [
{
"function": {
"arguments": "<string>",
"name": "<string>"
},
"id": "<string>",
"type": "function"
}
]
},
"finish_reason": "stop"
}
],
"error": {
"message": "<string>",
"code": "<string>",
"type": "<string>"
},
"usage": {
"completion_tokens": 1
}
}
],
"usage": {
"completion_tokens": 1,
"prompt_tokens": 1,
"prompt_tokens_details": {
"audio_tokens": 1,
"cached_tokens": 1
},
"total_tokens": 1
}
}{
"error": {
"message": "Invalid n: 12. Expected a value between 1 and 8.",
"type": "invalid_request_error",
"param": null,
"code": null
}
}{
"error": {
"message": "Invalid API key",
"type": "authentication_error",
"param": null,
"code": null
}
}{
"error": {
"message": "Organization rate limit exceeded (150 requests/minute). Please retry after 30 seconds.",
"type": "rate_limit_error",
"param": null,
"code": "rate_limit_exceeded"
}
}{
"error": {
"message": "The server had an error while processing your request.",
"type": "server_error",
"param": null,
"code": null
}
}Overview
A multilook request carries a sharedcontext and up to 16 prompts. The context uses the same message format as /v1/chat/completions, including media parts (image_url, video_url, video_frames, image_file_id, video_file_id). The context is prefilled once per call and reused across all prompts; each prompt extends it independently and produces n completions. Prompts are isolated from one another: no prompt observes another prompt’s text or completions.
Reused prefill is reported in usage.prompt_tokens_details.cached_tokens and billed at the cache-read rate. The Multilook guide covers when to use it, how billing works, and Python client code.
Request
curl https://api.perceptron.inc/v1/chat/completions/multilook \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $PERCEPTRON_API_KEY" \
-d '{
"model": "perceptron-mk1",
"context": [
{ "role": "user",
"content": [
{ "type": "video_url",
"video_url": { "url": "https://raw.githubusercontent.com/perceptron-ai-inc/perceptron/main/cookbook/_shared/assets/tutorials/isaac_frame_by_frame/surf.mp4" }
}
]
}
],
"prompts": [
"How many people are visible?",
"Describe the setting in one sentence.",
"Does anyone pick up an object? If so, what?"
],
"n": 1,
"max_completion_tokens": 128
}'
| Field | Description |
|---|---|
model | The model to invoke. |
context | Shared prefix all prompts extend. Same message format as /v1/chat/completions, including media parts (image_url, video_url, video_frames, file ids). Prefilled once per call. |
prompts | 1–16 independent sequences extending the shared prefix. Each entry is the content of an implicit final user turn: a bare string, or { "content": [ ...parts ] } with the same part types as user messages. |
n | Completions per prompt, 1–8. n > 1 requires temperature > 0. |
max_completion_tokens | Maximum completion tokens, per completion (up to 8192). |
temperature, top_p, top_k, frequency_penalty, presence_penalty | Sampling parameters, applied to all prompts. |
vision_config | Perceptron vision-model controls (e.g. enable_thinking), applied to all prompts. |
temperature defaults to 0.0, so set it explicitly whenever n > 1.
stream, response_format, and stop are not supported on this endpoint.
Structured prompts
A prompt can be a bare string or an object carrying content parts, so individual prompts can bring their own media alongside the shared context:"prompts": [
"Describe the setting in one sentence.",
{ "content": [
{ "type": "image_url", "image_url": { "url": "<image-url>" } },
{ "type": "text", "text": "Does this still frame match the video?" }
]
}
]
prompts entry sit outside the shared prefix: they are fetched, preprocessed, and prefilled per prompt, and are not reported in cached_tokens. Media intended for reuse across prompts belongs in context. See Per-prompt media in the guide for a full example.
Response
One entry per prompt, in request order:{
"id": "mlcmpl-2b9e41",
"object": "chat.completion.multilook",
"model": "perceptron-mk1",
"results": [
{ "prompt_index": 0,
"completions": [
{ "index": 0,
"message": { "role": "assistant", "content": "Two people are visible." },
"finish_reason": "stop" }
],
"usage": { "completion_tokens": 6 } },
{ "prompt_index": 1,
"completions": [
{ "index": 0,
"message": { "role": "assistant", "content": "A surfer rides a wave on an open stretch of ocean under a clear sky." },
"finish_reason": "stop" }
],
"usage": { "completion_tokens": 18 } },
{ "prompt_index": 2,
"completions": [
{ "index": 0,
"message": { "role": "assistant", "content": "No one picks up an object during the clip; the surfer keeps both hands free while riding the wave. Nothing is lifted from the water, the board, or the shore." },
"finish_reason": "stop" }
],
"usage": { "completion_tokens": 70 } }
],
"usage": {
"prompt_tokens": 35612,
"completion_tokens": 94,
"total_tokens": 35706,
"prompt_tokens_details": { "cached_tokens": 25872 }
}
}
error object in place of its completions; other prompts are unaffected. The call returns 200 if at least one prompt succeeded; if all prompts fail, the whole request returns the first error’s status. See Handling partial failures for an example and client code.
cached_tokens is the subset of prompt_tokens served from the in-request prefill. Reuse is scoped to the call: a subsequent identical call reports cached_tokens: 0. Billing rates and a worked example are in the guide’s Billing section.
Limits
| Limit | Value |
|---|---|
| Prompts per call | 1–16 |
Completions per call (prompts × n) | 64 |
| Image/video inputs per call | 256 (each video frame counts as one) |
| Request body size | 20 MB |
| Requests | 150/min (separate bucket from /v1/chat/completions) |
| Request budget | 300 s. On timeout, retry with fewer prompts, lower n, or smaller max_completion_tokens |
Authorizations
Bearer token authentication using your Perceptron API key
Body
Request body for /v1/chat/completions/multilook.
Shared prefix all prompts extend — same message format as /v1/chat/completions, except that
tool calling is not supported: tool messages and assistant tool_calls are rejected with a
400. Prefilled once and reused across all prompts within this request.
Author role of the message: system, developer, user, assistant, or tool.
- System
- Developer
- User
- Assistant
- Tool
Show child attributes
Show child attributes
The model to invoke.
Independent sequences extending the shared prefix (1 to 16 entries). Prompts are isolated from one another and never see each other's text or completions.
1 - 16 elementsOne prompt: the content of an implicit final user turn extending the shared
context. Either a bare string or a structured object carrying content parts
(the same part types as /v1/chat/completions user messages, including media).
Positive values discourage the model from repeating previously used tokens.
-2 <= x <= 2Maximum completion tokens, per completion. Must fit within the selected model's output limit.
x >= 0Sampled completions ("looks") per prompt (1 to 8). n > 1 requires temperature > 0.
1 <= x <= 8Positive values encourage the model to introduce new concepts.
-2 <= x <= 2How much the model reasons before it answers: none, minimal, low, medium or high.
One tier for the whole request, applied to every prompt. A tier other than none turns
reasoning on. vision_config.enable_thinking, when set, decides whether the model reasons;
the tier then only applies while reasoning is on.
none, minimal, low, medium, high Sampling temperature, shared across all prompts.
0 <= x <= 2Top-k sampling.
x >= 0Nucleus sampling probability.
x <= 1Perceptron vision-model controls, shared across all prompts.
Show child attributes
Show child attributes
Response
Multilook completions generated successfully. Returns a grouped response with one result per prompt; a prompt-level failure appears as an error entry in place of that prompt's completions.
Response body for /v1/chat/completions/multilook.
Always chat.completion.multilook.
One entry per prompt, in request order.
Show child attributes
Show child attributes
Call-level usage. prompt_tokens counts the shared context once per prompt (same
meaning as on /v1/chat/completions); total_tokens = prompt_tokens + completion_tokens.
Show child attributes
Show child attributes