Models endpoints
Request and response reference for Messages, Chat Completions, Responses, Gemini and model listing on Half Off Dev, with the headers each accepts.
These are the vendors' own endpoints, answered at https://halfoff.dev. Bodies follow each vendor's schema and pass through untouched, so this reference covers the parameters you'll reach for most and links out to the vendor docs for everything else. Behavior shared by all of them, like failover and caching, is explained in How the gateway works.
Any endpoint accepts your key as Authorization: Bearer, x-api-key, x-goog-api-key or a ?key= query parameter. Errors use the format of the endpoint you called; see Errors.
Response headers
x-halfoff-request-idstringId of the call, such as req_6f1c0b9e2d4a47a1b8c3d5e7, also listed in Logs. Sent on successful responses and on 429 capacity and 502 errors.
x-halfoff-groupstringThe channel group that answered.
x-halfoff-channelstringThat group's channel label, URL-encoded.
x-halfoff-queued-msintegerMilliseconds spent waiting for capacity. Absent when there was no wait.
retry-afterintegerSeconds to wait. Sent with 429 responses from key limits or busy capacity.
List models#
/v1/modelsReturns every model you can call right now, with its context window, prices and the channel groups that serve it. No key needed. The shape is compatible with models.list() in both the OpenAI and Anthropic SDKs. Prices are what Half Off Dev charges, half the official list price. Field details are in Models & pricing.
Response fields
dataarrayOne entry per model.
has_morebooleanAlways false: you get the full list in one response.
curl https://halfoff.dev/v1/models{
"object": "list",
"data": [
{
"id": "claude-sonnet-5.5",
"object": "model",
"type": "model",
"display_name": "Claude Sonnet 5.5",
"created": 1700000000,
"created_at": "2025-01-01T00:00:00Z",
"owned_by": "Anthropic",
"context_length": 1000000,
"pricing": {
"input": 1,
"output": 5,
"cache_read": 0.05,
"cache_write": 1.25,
"unit": "USD per 1M tokens"
},
"groups": ["claude-value"]
}
],
"has_more": false,
"first_id": "claude-fable-5.1",
"last_id": "gpt-5.6-luna"
}Create a message#
/v1/messagesThe Anthropic Messages API. Set stream for server-sent events. Extended thinking, tool use and prompt caching work exactly as Anthropic documents them. Guide: Anthropic Messages.
Headers
x-api-keystringRequiredYour Half Off Dev key. Authorization: Bearer is accepted too.
anthropic-versionstringDefault 2023-06-01Passed through to the model.
anthropic-betastringPassed through unchanged.
x-halfoff-groupstringChannel group for this call, for example claude-value. Ignored if the key is bound to a group.
x-session-idstringA stable conversation id, so every turn goes to the channel that holds its cache.
Body parameters
modelstringRequiredA model id from GET /v1/models, for example claude-sonnet-5.5. Variants like claude-sonnet-5-5 resolve to the same model.
messagesarrayRequiredThe conversation so far, alternating user and assistant turns.
max_tokensintegerRequiredMost tokens the model may generate.
systemstring or arraySystem prompt. Text blocks accept cache_control.
streambooleanDefault falseStream the answer as server-sent events.
thinkingobjectExtended thinking settings.
output_configobjectOutput settings such as effort and format.
toolsarrayTools the model may call.
tool_choiceobjectHow the model picks a tool.
metadataobjectRequest metadata.
curl https://halfoff.dev/v1/messages \
-H "x-api-key: $HALFOFF_API_KEY" \
-H "anthropic-version: 2023-06-01" \
-H "content-type: application/json" \
-d '{
"model": "claude-sonnet-5.5",
"max_tokens": 512,
"messages": [{"role": "user", "content": "Suggest a name for a budget tracking app."}]
}'{
"id": "msg_01XFDUDYJgAACzvnptvVoYEL",
"type": "message",
"role": "assistant",
"model": "claude-sonnet-5-5",
"content": [
{"type": "text", "text": "How about \"Pennywise\"?"}
],
"stop_reason": "end_turn",
"stop_sequence": null,
"usage": {
"input_tokens": 16,
"cache_creation_input_tokens": 0,
"cache_read_input_tokens": 0,
"output_tokens": 9
}
}Count message tokens#
/v1/messages/count_tokensEstimates how many input tokens a Messages request would use, without running it. Free, and it works even with a zero balance. The number is approximate: good for context-window checks and budgeting, while the actual charge comes from a real call's usage. See Anthropic's token counting guide.
Headers
x-api-keystringRequiredYour Half Off Dev key. Authorization: Bearer is accepted too.
x-halfoff-groupstringChannel group to check the model against. Ignored if the key is bound to a group.
Body parameters
modelstringRequiredModel id, same as for POST /v1/messages.
messagesarrayRequiredMessages to count.
systemstring or arraySystem prompt to include.
toolsarrayTool definitions to include.
curl https://halfoff.dev/v1/messages/count_tokens \
-H "x-api-key: $HALFOFF_API_KEY" \
-H "anthropic-version: 2023-06-01" \
-H "content-type: application/json" \
-d '{"model": "claude-sonnet-5.5",
"messages": [{"role": "user", "content": "How long is this prompt?"}]}'{
"input_tokens": 15
}Create a chat completion#
/v1/chat/completionsThe OpenAI Chat Completions API. Every model in GET /v1/models answers in this format, Claude and Gemini included. Streaming requests always get stream_options.include_usage: true, so the final chunk carries usage. Guide: OpenAI Chat Completions.
Headers
AuthorizationstringRequiredBearer and your Half Off Dev key.
openai-betastringPassed through unchanged.
x-halfoff-groupstringChannel group for this call, for example gpt-value. Ignored if the key is bound to a group.
x-session-idstringA stable conversation id, so every turn goes to the channel that holds its cache.
Body parameters
modelstringRequiredA model id from GET /v1/models, for example gpt-6.1-sol.
messagesarrayRequiredThe conversation so far: system or developer, user, assistant and tool messages.
streambooleanDefault falseStream server-sent events, ending with data: [DONE].
max_completion_tokensintegerCap on generated tokens, reasoning tokens included.
reasoning_effortstringHow hard a reasoning model thinks. Accepted values depend on the model.
toolsarrayFunctions the model may call.
tool_choicestring or objectHow the model picks a function.
response_formatobjectAsk for JSON output or a JSON Schema.
prompt_cache_keystringRaises cache hit rates; also used as the conversation id for routing.
userstringYour end user's id. Used as the conversation id when nothing more specific is sent.
curl https://halfoff.dev/v1/chat/completions \
-H "Authorization: Bearer $HALFOFF_API_KEY" \
-H "content-type: application/json" \
-d '{
"model": "gpt-6.1-sol",
"messages": [{"role": "user", "content": "Translate \"good morning\" into French."}]
}'{
"id": "chatcmpl-B9MHDbslfkBeAs8l4bebGdFOJ6PeG",
"object": "chat.completion",
"created": 1791700000,
"model": "gpt-6.1-sol",
"choices": [
{
"index": 0,
"message": {"role": "assistant", "content": "Bonjour."},
"finish_reason": "stop"
}
],
"usage": {
"prompt_tokens": 15,
"completion_tokens": 3,
"total_tokens": 18,
"prompt_tokens_details": {"cached_tokens": 0},
"completion_tokens_details": {"reasoning_tokens": 0}
}
}Create a response#
/v1/responsesThe OpenAI Responses API, which Codex CLI uses. GPT models only; any other model returns 404. Nothing is stored between calls, so send the full conversation in input each time. Guide: OpenAI Chat Completions.
Headers
AuthorizationstringRequiredBearer and your Half Off Dev key.
openai-betastringPassed through unchanged.
x-halfoff-groupstringChannel group for this call, for example gpt-value. Ignored if the key is bound to a group.
x-session-idstringA stable conversation id, so every turn goes to the channel that holds its cache.
Body parameters
modelstringRequiredA GPT model id from GET /v1/models, for example gpt-6.1-sol.
inputstring or arrayRequiredA prompt, or the conversation as a list of input items.
instructionsstringSystem instructions. The response echoes them back, or null.
streambooleanDefault falseStream typed server-sent events, ending with response.completed, which carries usage.
max_output_tokensintegerCap on generated tokens, reasoning tokens included.
reasoningobjectReasoning settings such as effort. Accepted values depend on the model.
toolsarrayFunctions and other tools the model may call.
storebooleanSend false: responses aren't kept for later retrieval.
prompt_cache_keystringRaises cache hit rates; also used as the conversation id for routing.
curl https://halfoff.dev/v1/responses \
-H "Authorization: Bearer $HALFOFF_API_KEY" \
-H "content-type: application/json" \
-d '{
"model": "gpt-6.1-sol",
"input": "Give me a one-line git tip.",
"store": false
}'Generate content#
/v1beta/models/{model}:generateContentThe Gemini API. Guide: Gemini.
Path parameters
modelstringRequiredModel id, for example gemini-3.1-pro. Google's spellings such as gemini-3.1-pro-preview resolve to the same model.
Headers
x-goog-api-keystringRequiredYour Half Off Dev key. Authorization: Bearer or a ?key= query parameter also work.
x-halfoff-groupstringChannel group for this call, for example gemini-value. Ignored if the key is bound to a group.
x-session-idstringA stable conversation id, so every turn goes to the channel that holds its cache.
Body parameters
contentsarrayRequiredThe conversation so far, as user and model turns built from parts.
systemInstructionobjectSystem instruction, as a content object with text parts.
generationConfigobjectOutput settings: maxOutputTokens, temperature, responseMimeType, thinkingConfig and more.
toolsarrayFunction declarations and other tools.
toolConfigobjectHow the model picks functions.
safetySettingsarraySafety thresholds per category.
curl "https://halfoff.dev/v1beta/models/gemini-3.1-pro:generateContent" \
-H "x-goog-api-key: $HALFOFF_API_KEY" \
-H "content-type: application/json" \
-d '{"contents": [{"role": "user", "parts": [{"text": "What is a haiku?"}]}]}'{
"candidates": [
{
"content": {"role": "model", "parts": [{"text": "A haiku is a three-line Japanese poem of 5, 7 and 5 syllables."}]},
"finishReason": "STOP",
"index": 0
}
],
"usageMetadata": {
"promptTokenCount": 5,
"candidatesTokenCount": 18,
"thoughtsTokenCount": 84,
"totalTokenCount": 107
},
"responseId": "mFHoaNC1Lo3HqtsP"
}Stream generated content#
/v1beta/models/{model}:streamGenerateContentThe same request as generateContent, delivered as a stream. Add alt=sse for server-sent events, one partial response per event; the SDKs add it for you. Without it you get a single JSON array streamed piece by piece. See Streaming.
Query parameters
altstringsse for server-sent events. Recommended.
Path parameter, headers and body are identical to Generate content.
curl -N "https://halfoff.dev/v1beta/models/gemini-3-flash:streamGenerateContent?alt=sse" \
-H "x-goog-api-key: $HALFOFF_API_KEY" \
-H "content-type: application/json" \
-d '{"contents": [{"role": "user", "parts": [{"text": "List three primary colors."}]}]}'data: {"candidates":[{"content":{"role":"model","parts":[{"text":"Red, yellow"}]}}],"usageMetadata":{"promptTokenCount":6,"candidatesTokenCount":3,"totalTokenCount":9}}
data: {"candidates":[{"content":{"role":"model","parts":[{"text":" and blue."}]},"finishReason":"STOP"}],"usageMetadata":{"promptTokenCount":6,"candidatesTokenCount":6,"totalTokenCount":12}}Count Gemini tokens#
/v1beta/models/{model}:countTokensEstimates the tokens in contents without running the model. Free, and it works even with a zero balance. The number is approximate. Gemini reference.
Body parameters
contentsarrayRequiredContent to count, shaped as for generateContent.
Path parameter and headers are identical to Generate content.
curl "https://halfoff.dev/v1beta/models/gemini-3.1-pro:countTokens" \
-H "x-goog-api-key: $HALFOFF_API_KEY" \
-H "content-type: application/json" \
-d '{"contents": [{"role": "user", "parts": [{"text": "How long is this prompt?"}]}]}'{
"totalTokens": 7
}List models in Gemini format#
/v1beta/modelsLists the models served through the Gemini endpoints, shaped the way the Gemini API shapes them. No key needed. The Google Gen AI SDKs call it from models.list(). Prices appear only in GET /v1/models.
curl https://halfoff.dev/v1beta/models{
"models": [
{
"name": "models/gemini-3.1-pro",
"displayName": "Gemini 3.1 Pro",
"inputTokenLimit": 1048576,
"supportedGenerationMethods": ["generateContent", "streamGenerateContent", "countTokens"]
}
]
}