curl --request POST \
--url https://api.opper.ai/v3/compat/v1/messages/count_tokens \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"max_tokens": 123,
"messages": [
{
"content": "<unknown>",
"role": "<string>"
}
],
"model": "<string>"
}
'import requests
url = "https://api.opper.ai/v3/compat/v1/messages/count_tokens"
payload = {
"max_tokens": 123,
"messages": [
{
"content": "<unknown>",
"role": "<string>"
}
],
"model": "<string>"
}
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({
max_tokens: 123,
messages: [{content: '<unknown>', role: '<string>'}],
model: '<string>'
})
};
fetch('https://api.opper.ai/v3/compat/v1/messages/count_tokens', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));{
"input_tokens": 123
}{
"error": {
"code": "<string>",
"message": "<string>",
"details": "<unknown>"
},
"meta": {}
}{
"error": {
"code": "<string>",
"message": "<string>",
"details": "<unknown>"
},
"meta": {}
}Count Tokens
Anthropic Messages token-counting compatible endpoint. Returns the number of input tokens a Messages request would consume, without creating a message. Mirrors Anthropic’s POST /v1/messages/count_tokens (same request body as the Messages endpoint, minus the required max_tokens).
The model resolves exactly as it does on the Messages endpoint, so every model a message accepts can be counted, and one a message would reject fails the same way (404 for an unknown model, 403 for one the allowlist or an entitlement denies). Claude models are counted exactly by their own backend, in the model’s region. Every other model, and a dynamic route, gets Opper’s estimate in the same shape: characters / 4 over the system prompt, messages and tool declarations, plus 1 (within about 20% on English prose), with a flat amount per image and 3,000 tokens per PDF page. The X-Opper-Count-Source response header says which.
curl --request POST \
--url https://api.opper.ai/v3/compat/v1/messages/count_tokens \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"max_tokens": 123,
"messages": [
{
"content": "<unknown>",
"role": "<string>"
}
],
"model": "<string>"
}
'import requests
url = "https://api.opper.ai/v3/compat/v1/messages/count_tokens"
payload = {
"max_tokens": 123,
"messages": [
{
"content": "<unknown>",
"role": "<string>"
}
],
"model": "<string>"
}
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({
max_tokens: 123,
messages: [{content: '<unknown>', role: '<string>'}],
model: '<string>'
})
};
fetch('https://api.opper.ai/v3/compat/v1/messages/count_tokens', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));{
"input_tokens": 123
}{
"error": {
"code": "<string>",
"message": "<string>",
"details": "<unknown>"
},
"meta": {}
}{
"error": {
"code": "<string>",
"message": "<string>",
"details": "<unknown>"
},
"meta": {}
}Authorizations
API key authentication. Pass your API key as a Bearer token.
Headers
Function name for tracing and project-level guardrail function-scope filtering.
Parent span ID for distributed tracing context.
Comma-separated key:value usage-attribution tags (e.g. tenant:acme,project:demo, max 8). Recorded on the generation's billing/metrics rows; group spend by any key via GET /v2/analytics/usage?group_by=. Header-borne twin of the /v3/session URL prefix tags (which win per key when both are present); opper.-prefixed keys and session_id are reserved. Malformed values return 400.
Body
Show child attributes
Show child attributes
Opt in to top-level automatic prompt caching: {"type":"ephemeral"} (optionally "ttl":"1h"). Places one moving breakpoint on the largest cacheable prefix. Explicit per-block cache_control on system/message/tool content takes precedence. Caching is off unless set. Cache writes cost 1.25x input and reads 0.1x
Output configuration. A format of {"type":"json_schema" + "schema":{...}} constrains the response to that JSON Schema. An effort of low|medium|high sets adaptive-thinking effort. Sending a format here and a top-level output_format is a 400.
Structured output; superseded by output_config.format. Accepts either the Messages API shape ({"type":"json_schema" + "schema":{...}}) or the OpenAI-style envelope ({"type":"json_schema" + "json_schema":{"name":... + "schema":{...}}}).
Any value
Any value
Any value
Show child attributes
Show child attributes
Response
Successful response
The number of input tokens the request would consume.