Chat completions
POST /v1/chat/completions is the main conversation endpoint and matches the OpenAI contract exactly: the same model + messages body, the same response. SSE streaming, tool calling, image (vision) input, and audio input are supported. Every chat model listed by GET /v1/models works with this endpoint.
/v1/chat/completionsBearer authGenerate a chat completion
Send a conversation to an LLM and get a response. Supports all chat models: GPT-4o, Claude, Llama, Gemini, and 350+ more.
Two modes:
- Non-streaming (default): Returns the full response as JSON.
Compatible with
openai-python,openai-node, and any OpenAI SDK. - Streaming (
"stream": true): Returns Server-Sent Events (SSE). Tokens arrive as they're generated, perfect for real-time chat UIs.
Response headers:
X-Hormouz-Admission-Latency-Ms: Time spent on auth + billing holdX-Hormouz-Upstream-Latency-Ms: Time waiting for the AI providerX-Hormouz-Proxy-Latency-Ms: Total proxy overhead
Parameters
| Field | in | Type | Description |
|---|---|---|---|
Idempotency-Key | header | string | Optional deduplication token (any string up to 200 characters). How it works:
When to use: Add this header when you want to safely retry a request after a network timeout without risking a duplicate operation. |
Request bodyrequired
| Field | Type | Description | |||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
modelrequired | string | Model identifier from the catalogue. Use Examples: Example: | |||||||||||||||
messagesrequired | array of object | Conversation history as an array of message objects. Each message
has a Roles:
| |||||||||||||||
rolerequired | string | Who sent this message. Allowed values: | |||||||||||||||
contentrequired | string | array of object | Message content. For text-only messages, a simple string. For multimodal inputs, an array of content parts. Three
supported part types: Vision (image) example:
Audio (transcription / analysis) example:
Audio is supported on | |||||||||||||||
stream | boolean | When Recommended for chat interfaces: tokens appear as they're generated instead of waiting for the full response. The chunk before Measured timing (Iran → gateway → upstream, production):
Default: | |||||||||||||||
sorted_by | string | Which upstream provider to prefer when the model is served by more than one. Most models are hosted by several providers whose price and speed differ substantially — on a single popular model the input price ranges more than 8x and the slowest provider takes several times longer than the fastest. By default the gateway lets the upstream router choose. Set this to steer that choice:
Ranking is by median (p50) over a rolling 5-minute window of live traffic, so it follows current conditions including temporary provider discounts. This is a preference, not a guarantee. If the preferred option is unavailable or rate-limited, the request is served by the next one rather than failing. It has no effect on models with a single provider. Setting it changes how the request is billed. A sorted request goes only through the standard multi-provider route and bills at the model's standard list price: any discounted route the model normally takes (shown as a discount badge on its model page) is skipped for that request. Any value other than the three above is rejected with
Models where this applies are marked Allowed values: Example: | |||||||||||||||
max_tokens | integer | Maximum number of tokens to generate in the response. Higher values allow longer responses but cost more. Default: | |||||||||||||||
temperature | number (double) | Controls randomness (0.0 to 2.0).
| |||||||||||||||
top_p | number (double) | Nucleus sampling threshold (0.0 to 1.0). Alternative to temperature; usually set one or the other, not both. | |||||||||||||||
tools | array of object | Tool/function definitions for the model to call. Two flavours coexist freely in the same array:
Not supported (returns
| |||||||||||||||
tool_choice | string | object | Controls tool calling behavior:
| |||||||||||||||
stop | string | array of string | Stop sequence(s). Generation stops when any of these strings appear in the output. Up to 4 sequences. |
Example
Responses
200headers: X-Hormouz-Admission-Latency-Ms, X-Hormouz-Upstream-Latency-Ms, X-Hormouz-Proxy-Latency-Ms, X-Suggest-Stream, X-Client-Timeout-HintSuccess. Response format depends on the stream parameter:
stream: false(default) →application/jsonwith full responsestream: true→text/event-streamwith SSE chunks
400The request body or query parameters are invalid. Check the
error.code and error.message fields for details. Codes the
gateway emits at 400:
BAD_REQUEST: generic malformed JSON / missing required field.INVALID_TOOLS: thetoolsarray,pluginsfield, or model's:onlinesuffix uses an unsupported shape. The response names the offender and links to the supported form. See the dedicatedInvalidToolsresponse below for the three triggers and migration hints.UNSUPPORTED_TOOL: a built-in tool (file_search,code_interpreter,computer_use,mcp) whose pricing dimension is not yet wired into the gateway hold estimator.INVALID_AUDIO_FORMAT:input_audio.formatvalue is not in the supported set. The envelope lists every supported codec name. The gateway accepts both short names (wav,webm) and full MIME aliases (audio/wav,audio/webm); anything else fails fast at admission. See API guide §8b.AUDIO_NOT_SUPPORTED_ON_RESPONSES:input_audiocontent part sent toPOST /v1/responses. The Responses API has no audio content type today; resend toPOST /v1/chat/completionsinstead.
401Authentication failed. Either the Authorization header is missing,
the key format is invalid, or the key doesn't match any active account.
{
"error": {
"code": "UNAUTHORIZED",
"message": "Authentication required",
"status": 401
}
}402Your credit balance is too low to cover the estimated cost of this request. Contact your admin for a top-up.
{
"error": {
"code": "INSUFFICIENT_CREDITS",
"message": "Insufficient credit balance. Add credit at https://app.hormouz.net/billing to continue.",
"status": 402
}
}409Idempotency key conflict: you sent the same Idempotency-Key with
a different request body.
429Too many requests. Back off and retry after a short delay.
Check the Retry-After header for guidance.
499Your HTTP client cancelled the request before the gateway finished. No charge: the hold has been released. The most common cause is a 30 s default client timeout against a model whose p95 latency is 45-60 s; bump your client timeout to 120 s.
Retries with the same Idempotency-Key will replay this 499
forever (the prior outcome is "you cancelled"). For an actual
re-attempt, mint a fresh Idempotency-Key. See
docs/CUSTOMER_ERROR_HANDLING.md §3 for the full contract.
{
"error": {
"code": "CLIENT_CLOSED",
"message": "Client disconnected before response",
"status": 499
}
}500Unexpected server error. Please retry with exponential backoff. If the issue persists, contact your admin.
502The upstream AI provider is temporarily
unavailable. Retry with exponential backoff (1s, 2s, 4s) using the
same Idempotency-Key: if the upstream actually completed
the work and only the response failed, the gateway replays the
cached success without a second charge. Holds are released
automatically on upstream failures.
503The upstream AI provider is temporarily
unavailable. Retry with exponential backoff (1s, 2s, 4s) using the
same Idempotency-Key: if the upstream actually completed
the work and only the response failed, the gateway replays the
cached success without a second charge. Holds are released
automatically on upstream failures.
504The gateway's 120 s upstream deadline elapsed before the upstream
provider produced a response. Retry with the same
Idempotency-Key so a delayed-but-eventually-successful upstream
call can be replayed instead of re-charged. Consider switching
to a faster model or capping max_tokens if this recurs.
{
"error": {
"code": "UPSTREAM_TIMEOUT",
"message": "Upstream did not respond before timeout",
"status": 504
}
}FAQ
- How do I use GPT-4o or Claude from Iran?
Through Hormouz: set
base_urltohttps://api.hormouz.net/v1and pick amodelsuch asopenai/gpt-4ooranthropic/claude-sonnet-4. Stable access from inside Iran with local billing.- Is tool / function calling supported?
Yes, with the exact OpenAI contract (
tools,tool_choice). Server-side tools such asweb_searchadditionally work on every model.