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_thinking": true,
"internal_tools": {
"focus": 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_thinking": True,
"internal_tools": { "focus": 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_thinking: true, internal_tools: {focus: 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_thinking' => true,
'internal_tools' => [
'focus' => 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_thinking\": true,\n \"internal_tools\": {\n \"focus\": true\n }\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_thinking\": true,\n \"internal_tools\": {\n \"focus\": true\n }\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_thinking\": true,\n \"internal_tools\": {\n \"focus\": true\n }\n }\n}"
response = http.request(request)
puts response.read_body{
"id": "<string>",
"model": "<string>",
"object": "<string>",
"results": [
{
"prompt_index": 1,
"completions": [
{
"index": 1,
"message": {
"role": "system",
"content": "<string>",
"reasoning_content": "<string>"
},
"finish_reason": "stop"
}
],
"error": {
"message": "<string>",
"code": "<string>",
"type": "<string>"
},
"usage": {
"completion_tokens": 1
}
}
],
"usage": {
"completion_tokens": 1,
"prompt_tokens": 1,
"prompt_tokens_details": {
"cached_tokens": 1
},
"total_tokens": 1
}
}{
"error": {
"code": null,
"message": "Invalid n: 12. Expected a value between 1 and 8.",
"param": null,
"type": "invalid_request_error"
}
}{
"error": {
"code": null,
"message": "Invalid API key",
"param": null,
"type": "authentication_error"
}
}{
"error": {
"code": "rate_limit_exceeded",
"message": "Organization rate limit exceeded (300 requests/minute). Please retry after 30 seconds.",
"param": null,
"type": "rate_limit_error"
}
}{
"error": {
"code": null,
"message": "The server had an error while processing your request.",
"param": null,
"type": "server_error"
}
}Multilook Chat Completions
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_thinking": true,
"internal_tools": {
"focus": 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_thinking": True,
"internal_tools": { "focus": 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_thinking: true, internal_tools: {focus: 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_thinking' => true,
'internal_tools' => [
'focus' => 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_thinking\": true,\n \"internal_tools\": {\n \"focus\": true\n }\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_thinking\": true,\n \"internal_tools\": {\n \"focus\": true\n }\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_thinking\": true,\n \"internal_tools\": {\n \"focus\": true\n }\n }\n}"
response = http.request(request)
puts response.read_body{
"id": "<string>",
"model": "<string>",
"object": "<string>",
"results": [
{
"prompt_index": 1,
"completions": [
{
"index": 1,
"message": {
"role": "system",
"content": "<string>",
"reasoning_content": "<string>"
},
"finish_reason": "stop"
}
],
"error": {
"message": "<string>",
"code": "<string>",
"type": "<string>"
},
"usage": {
"completion_tokens": 1
}
}
],
"usage": {
"completion_tokens": 1,
"prompt_tokens": 1,
"prompt_tokens_details": {
"cached_tokens": 1
},
"total_tokens": 1
}
}{
"error": {
"code": null,
"message": "Invalid n: 12. Expected a value between 1 and 8.",
"param": null,
"type": "invalid_request_error"
}
}{
"error": {
"code": null,
"message": "Invalid API key",
"param": null,
"type": "authentication_error"
}
}{
"error": {
"code": "rate_limit_exceeded",
"message": "Organization rate limit exceeded (300 requests/minute). Please retry after 30 seconds.",
"param": null,
"type": "rate_limit_error"
}
}{
"error": {
"code": null,
"message": "The server had an error while processing your request.",
"param": null,
"type": "server_error"
}
}Overview
A multilook request carries a sharedcontext — the same message format as /v1/chat/completions, including media parts (image_url, video_url, video_frames, image_file_id, video_file_id) — and up to 16 prompts. 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.
Prefilled once and reused across all prompts within this request.
Author role of the message as defined by the OpenAI Chat Completions spec.
- Option 1
- Option 2
- Option 3
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.
One 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.
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 <= 2Sampling 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