curl --request POST \
--url https://api.perceptron.inc/v1/chat/completions \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"messages": [
{
"content": "<string>",
"role": "system"
}
],
"model": "<string>",
"frequency_penalty": 0,
"max_completion_tokens": 1,
"n": 1,
"parallel_tool_calls": true,
"presence_penalty": 0,
"regex": "<string>",
"response_format": {
"type": "text"
},
"stream": false,
"stream_options": {
"include_usage": true
},
"temperature": 1,
"tools": [
{
"function": {
"name": "<string>",
"description": "<string>",
"parameters": {},
"strict": true
},
"type": "function"
}
],
"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"
payload = {
"messages": [
{
"content": "<string>",
"role": "system"
}
],
"model": "<string>",
"frequency_penalty": 0,
"max_completion_tokens": 1,
"n": 1,
"parallel_tool_calls": True,
"presence_penalty": 0,
"regex": "<string>",
"response_format": { "type": "text" },
"stream": False,
"stream_options": { "include_usage": True },
"temperature": 1,
"tools": [
{
"function": {
"name": "<string>",
"description": "<string>",
"parameters": {},
"strict": True
},
"type": "function"
}
],
"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({
messages: [{content: '<string>', role: 'system'}],
model: '<string>',
frequency_penalty: 0,
max_completion_tokens: 1,
n: 1,
parallel_tool_calls: true,
presence_penalty: 0,
regex: '<string>',
response_format: {type: 'text'},
stream: false,
stream_options: {include_usage: true},
temperature: 1,
tools: [
{
function: {name: '<string>', description: '<string>', parameters: {}, strict: true},
type: 'function'
}
],
top_k: 1,
top_p: 0,
vision_config: {enable_audio_in_video: true, enable_thinking: true}
})
};
fetch('https://api.perceptron.inc/v1/chat/completions', 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",
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([
'messages' => [
[
'content' => '<string>',
'role' => 'system'
]
],
'model' => '<string>',
'frequency_penalty' => 0,
'max_completion_tokens' => 1,
'n' => 1,
'parallel_tool_calls' => true,
'presence_penalty' => 0,
'regex' => '<string>',
'response_format' => [
'type' => 'text'
],
'stream' => false,
'stream_options' => [
'include_usage' => true
],
'temperature' => 1,
'tools' => [
[
'function' => [
'name' => '<string>',
'description' => '<string>',
'parameters' => [
],
'strict' => true
],
'type' => 'function'
]
],
'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"
payload := strings.NewReader("{\n \"messages\": [\n {\n \"content\": \"<string>\",\n \"role\": \"system\"\n }\n ],\n \"model\": \"<string>\",\n \"frequency_penalty\": 0,\n \"max_completion_tokens\": 1,\n \"n\": 1,\n \"parallel_tool_calls\": true,\n \"presence_penalty\": 0,\n \"regex\": \"<string>\",\n \"response_format\": {\n \"type\": \"text\"\n },\n \"stream\": false,\n \"stream_options\": {\n \"include_usage\": true\n },\n \"temperature\": 1,\n \"tools\": [\n {\n \"function\": {\n \"name\": \"<string>\",\n \"description\": \"<string>\",\n \"parameters\": {},\n \"strict\": true\n },\n \"type\": \"function\"\n }\n ],\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")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"messages\": [\n {\n \"content\": \"<string>\",\n \"role\": \"system\"\n }\n ],\n \"model\": \"<string>\",\n \"frequency_penalty\": 0,\n \"max_completion_tokens\": 1,\n \"n\": 1,\n \"parallel_tool_calls\": true,\n \"presence_penalty\": 0,\n \"regex\": \"<string>\",\n \"response_format\": {\n \"type\": \"text\"\n },\n \"stream\": false,\n \"stream_options\": {\n \"include_usage\": true\n },\n \"temperature\": 1,\n \"tools\": [\n {\n \"function\": {\n \"name\": \"<string>\",\n \"description\": \"<string>\",\n \"parameters\": {},\n \"strict\": true\n },\n \"type\": \"function\"\n }\n ],\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")
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 \"messages\": [\n {\n \"content\": \"<string>\",\n \"role\": \"system\"\n }\n ],\n \"model\": \"<string>\",\n \"frequency_penalty\": 0,\n \"max_completion_tokens\": 1,\n \"n\": 1,\n \"parallel_tool_calls\": true,\n \"presence_penalty\": 0,\n \"regex\": \"<string>\",\n \"response_format\": {\n \"type\": \"text\"\n },\n \"stream\": false,\n \"stream_options\": {\n \"include_usage\": true\n },\n \"temperature\": 1,\n \"tools\": [\n {\n \"function\": {\n \"name\": \"<string>\",\n \"description\": \"<string>\",\n \"parameters\": {},\n \"strict\": true\n },\n \"type\": \"function\"\n }\n ],\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{
"choices": [
{
"index": 1,
"message": {
"content": "<string>",
"role": "system",
"reasoning_content": "<string>",
"tool_calls": [
{
"function": {
"arguments": "<string>",
"name": "<string>"
},
"id": "<string>",
"type": "function"
}
]
},
"finish_reason": "stop"
}
],
"created": 1,
"id": "<string>",
"model": "<string>",
"object": "<string>",
"usage": {
"completion_tokens": 1,
"prompt_tokens": 1,
"total_tokens": 1,
"prompt_tokens_details": {
"audio_tokens": 1
}
}
}Chat Completions
Generate a response for a text or multimodal conversation, using the OpenAI chat completions request and response format.
curl --request POST \
--url https://api.perceptron.inc/v1/chat/completions \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"messages": [
{
"content": "<string>",
"role": "system"
}
],
"model": "<string>",
"frequency_penalty": 0,
"max_completion_tokens": 1,
"n": 1,
"parallel_tool_calls": true,
"presence_penalty": 0,
"regex": "<string>",
"response_format": {
"type": "text"
},
"stream": false,
"stream_options": {
"include_usage": true
},
"temperature": 1,
"tools": [
{
"function": {
"name": "<string>",
"description": "<string>",
"parameters": {},
"strict": true
},
"type": "function"
}
],
"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"
payload = {
"messages": [
{
"content": "<string>",
"role": "system"
}
],
"model": "<string>",
"frequency_penalty": 0,
"max_completion_tokens": 1,
"n": 1,
"parallel_tool_calls": True,
"presence_penalty": 0,
"regex": "<string>",
"response_format": { "type": "text" },
"stream": False,
"stream_options": { "include_usage": True },
"temperature": 1,
"tools": [
{
"function": {
"name": "<string>",
"description": "<string>",
"parameters": {},
"strict": True
},
"type": "function"
}
],
"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({
messages: [{content: '<string>', role: 'system'}],
model: '<string>',
frequency_penalty: 0,
max_completion_tokens: 1,
n: 1,
parallel_tool_calls: true,
presence_penalty: 0,
regex: '<string>',
response_format: {type: 'text'},
stream: false,
stream_options: {include_usage: true},
temperature: 1,
tools: [
{
function: {name: '<string>', description: '<string>', parameters: {}, strict: true},
type: 'function'
}
],
top_k: 1,
top_p: 0,
vision_config: {enable_audio_in_video: true, enable_thinking: true}
})
};
fetch('https://api.perceptron.inc/v1/chat/completions', 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",
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([
'messages' => [
[
'content' => '<string>',
'role' => 'system'
]
],
'model' => '<string>',
'frequency_penalty' => 0,
'max_completion_tokens' => 1,
'n' => 1,
'parallel_tool_calls' => true,
'presence_penalty' => 0,
'regex' => '<string>',
'response_format' => [
'type' => 'text'
],
'stream' => false,
'stream_options' => [
'include_usage' => true
],
'temperature' => 1,
'tools' => [
[
'function' => [
'name' => '<string>',
'description' => '<string>',
'parameters' => [
],
'strict' => true
],
'type' => 'function'
]
],
'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"
payload := strings.NewReader("{\n \"messages\": [\n {\n \"content\": \"<string>\",\n \"role\": \"system\"\n }\n ],\n \"model\": \"<string>\",\n \"frequency_penalty\": 0,\n \"max_completion_tokens\": 1,\n \"n\": 1,\n \"parallel_tool_calls\": true,\n \"presence_penalty\": 0,\n \"regex\": \"<string>\",\n \"response_format\": {\n \"type\": \"text\"\n },\n \"stream\": false,\n \"stream_options\": {\n \"include_usage\": true\n },\n \"temperature\": 1,\n \"tools\": [\n {\n \"function\": {\n \"name\": \"<string>\",\n \"description\": \"<string>\",\n \"parameters\": {},\n \"strict\": true\n },\n \"type\": \"function\"\n }\n ],\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")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"messages\": [\n {\n \"content\": \"<string>\",\n \"role\": \"system\"\n }\n ],\n \"model\": \"<string>\",\n \"frequency_penalty\": 0,\n \"max_completion_tokens\": 1,\n \"n\": 1,\n \"parallel_tool_calls\": true,\n \"presence_penalty\": 0,\n \"regex\": \"<string>\",\n \"response_format\": {\n \"type\": \"text\"\n },\n \"stream\": false,\n \"stream_options\": {\n \"include_usage\": true\n },\n \"temperature\": 1,\n \"tools\": [\n {\n \"function\": {\n \"name\": \"<string>\",\n \"description\": \"<string>\",\n \"parameters\": {},\n \"strict\": true\n },\n \"type\": \"function\"\n }\n ],\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")
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 \"messages\": [\n {\n \"content\": \"<string>\",\n \"role\": \"system\"\n }\n ],\n \"model\": \"<string>\",\n \"frequency_penalty\": 0,\n \"max_completion_tokens\": 1,\n \"n\": 1,\n \"parallel_tool_calls\": true,\n \"presence_penalty\": 0,\n \"regex\": \"<string>\",\n \"response_format\": {\n \"type\": \"text\"\n },\n \"stream\": false,\n \"stream_options\": {\n \"include_usage\": true\n },\n \"temperature\": 1,\n \"tools\": [\n {\n \"function\": {\n \"name\": \"<string>\",\n \"description\": \"<string>\",\n \"parameters\": {},\n \"strict\": true\n },\n \"type\": \"function\"\n }\n ],\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{
"choices": [
{
"index": 1,
"message": {
"content": "<string>",
"role": "system",
"reasoning_content": "<string>",
"tool_calls": [
{
"function": {
"arguments": "<string>",
"name": "<string>"
},
"id": "<string>",
"type": "function"
}
]
},
"finish_reason": "stop"
}
],
"created": 1,
"id": "<string>",
"model": "<string>",
"object": "<string>",
"usage": {
"completion_tokens": 1,
"prompt_tokens": 1,
"total_tokens": 1,
"prompt_tokens_details": {
"audio_tokens": 1
}
}
}Overview
The Chat Completions API uses OpenAI-compatible request and response formats, with model-specific capabilities and limits. Use it to generate text-only and multimodal responses from Perceptron Mk1. Perceptron Mk1 triggers thinking and structured grounding through the typedvision_config body field.
vision_config
For perceptron-mk1, pass a top-level vision_config object alongside messages:
| Field | Values | Purpose |
|---|---|---|
annotation_format | "point" / "box" / "polygon" / "clip" | Grounded output format. clip is video-only. |
enable_thinking | true / false | Chain-of-thought reasoning. |
When to enable_thinking
- On for text Q&A, captioning, OCR, and video clipping (
annotation_format: "clip"). - Off for spatial detection (
annotation_formatin"point","box","polygon").
Example: Grounded detection
curl https://api.perceptron.inc/v1/chat/completions \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $PERCEPTRON_API_KEY" \
-d '{
"model": "perceptron-mk1",
"messages": [
{ "role": "user",
"content": [
{ "type": "image_url",
"image_url": { "url": "<image-url>" } },
{ "type": "text",
"text": "Find every worker wearing PPE." }
]
}
],
"vision_config": { "annotation_format": "box" }
}'
Example: Video clipping
curl https://api.perceptron.inc/v1/chat/completions \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $PERCEPTRON_API_KEY" \
-d '{
"model": "perceptron-mk1",
"messages": [
{ "role": "user",
"content": [
{ "type": "video_url",
"video_url": { "url": "<video-url>" } },
{ "type": "text",
"text": "Clip the moment the worker scans the package." }
]
}
],
"vision_config": { "annotation_format": "clip", "enable_thinking": true }
}'
Example: Video from pre-decoded frames
If you’ve already sampled frames client-side, pass them inline with avideo_frames content part instead of a single video_url. Each frame carries an image_url (HTTP(S) URL or base64 data URL) and a timestamp_ms offset from the start of the clip. Provide between two and 256 frames, ordered by non-decreasing timestamp_ms. For optimal performance, follow a uniform sampling strategy aligned with the Video Token Counting guide.
curl https://api.perceptron.inc/v1/chat/completions \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $PERCEPTRON_API_KEY" \
-d '{
"model": "perceptron-mk1",
"messages": [
{ "role": "user",
"content": [
{ "type": "video_frames",
"video_frames": {
"frames": [
{ "image_url": { "url": "<frame-url>" }, "timestamp_ms": 0 },
{ "image_url": { "url": "<frame-url>" }, "timestamp_ms": 500 }
]
}
},
{ "type": "text",
"text": "What changes between these frames?" }
]
}
],
"vision_config": { "enable_thinking": true }
}'
Streaming
Set"stream": true to receive Server-Sent Events (SSE). Opt into token usage with stream_options.include_usage: true. Handle a trailing usage-only chunk with choices: [] before data: [DONE]; do not assume usage arrives on the chunk carrying finish_reason.
Inspect finish_reason: stop means normal completion, while length means the output was cut off. Clients shared with tool-capable models must also handle tool_calls. Treat error events and interrupted streams as failures or incomplete output; [DONE] alone does not prove a successful answer.
curl https://api.perceptron.inc/v1/chat/completions \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $PERCEPTRON_API_KEY" \
-d '{
"model": "perceptron-mk1",
"messages": [
{ "role": "user",
"content": [
{ "type": "image_url",
"image_url": { "url": "<image-url>" } },
{ "type": "text", "text": "Describe this scene in detail." }
]
}
],
"vision_config": { "enable_thinking": true },
"stream": true,
"stream_options": { "include_usage": true }
}'
Best Practices
- Thinking pairs well with text and clipping; not with spatial detection. Turn
enable_thinkingon for text Q&A, captioning, OCR, andannotation_format: "clip". Turn it off for"point","box", and"polygon". - Leave
temperatureunset. The default is0.0(deterministic). Only set a non-zero value if you want more varied outputs. - Image format: HTTP(S) URLs and base64 data URLs are both supported. MIME types:
image/png,image/jpeg,image/webp,video/mp4,video/webm. - Inline frames vs.
video_url: Send a whole clip withvideo_url, or — if you’ve already sampled frames — pass them inline withvideo_frames(two to 256 frames,timestamp_msnon-decreasing). Inline frames give you precise control over exactly which frames the model sees; for optimal performance, sample uniformly in line with the Video Token Counting guide. - Token limits: 32K context, 8K output.
- Many questions, one input: batch them with Multilook instead of repeating the media on every call. The shared context is prefilled once, and reused tokens are billed at the cache-read rate.
Limits
| Limit | Value |
|---|---|
| Requests | 300/min |
| Request body size | 20 MB |
| Media upload | 20 GB per 48 hours |
Authorizations
Bearer token authentication using your Perceptron API key
Body
Conversation history listed in order. Supported roles: system, developer (treated as system),
user, assistant, tool.
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. Use GET /v1/models to discover available model IDs. New Perceptron requests should use
perceptron-mk1.5.
Positive values discourage the model from repeating previously used tokens.
-2 <= x <= 2Maximum number of completion tokens to generate. Must fit within the selected model's context and output limits.
x >= 0Number of completions to generate. Only 1 is supported; greater values are rejected.
1 <= x <= 1Whether the model may call several functions in one turn. Defaults to true. Ignored
without functions in tools.
Positive values encourage the model to introduce new concepts.
-2 <= x <= 2How much the model reasons before it answers: none, minimal, low, medium or high.
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 Regex pattern for constrained generation.
An object specifying the format that the model must output.
Setting to { "type": "json_schema", "json_schema": {...} } enables Structured Outputs
which ensures the model will match your supplied JSON schema.
- Option 1
- Option 2
Show child attributes
Show child attributes
Set to true for SSE streaming. When omitted, the API returns a single JSON response.
Optional streaming flags. Token usage is always reported in the final chunk of a streaming response.
Show child attributes
Show child attributes
Sampling temperature. Lower values yield deterministic replies; higher values explore more creative outputs.
0 <= x <= 2Whether the model may call the declared functions: auto (the default) or none. none is
best effort and does not guarantee a response without tool calls. Forcing a call (required
or a named function) is not supported and is rejected with a 400. Ignored without functions
in tools.
none, auto, required Functions the model may call and the caller executes. Cannot be combined with a
json_schema response_format or regex.
Functions require a model that supports tool calling: other models reject them, and tool
calls or results in messages, with a 400. Generation stops at the first turn that calls
functions, so a response carries at most one turn of tool_calls.
Show child attributes
Show child attributes
Top-k sampling. The model samples from the top k most likely tokens.
x >= 0Nucleus sampling probability. The model samples from the smallest token set whose cumulative probability exceeds this threshold.
x <= 1Perceptron vision-model controls (thinking, spatial output format, video audio). Only supported on Perceptron-owned models.
Show child attributes
Show child attributes
Response
Chat completion generated successfully.