API Documentation
Simple integration with our API
Model Compatibility
| Model Name | Parameters | Description |
|---|---|---|
| google/gemma-4-26B-A4B-it | 26B MoE (~4B active) | Sparse MoE — frontier quality at small-model latency |
| google/gemma-4-31B-it | 31B dense | Dense Gemma 4 — highest quality text model we serve |
| openai/whisper-large-v3 | 1.5B | Speech-to-text — billed per second of audio (priced per audio hour), not per token |
| openai/whisper-large-v3-turbo | 0.8B | Faster Whisper, near-large-v3 accuracy |
| distil-whisper/distil-large-v3 | 0.75B | Distilled Whisper — cheapest speech-to-text |
Authentication
All API requests require an API key passed in the Authorization header:
Authorization: Bearer YOUR_API_KEY
Generate API keys in your dashboard under API Settings.
Text Generation API
Generate text using LLM models
Request Format
POST /v1/chat/completions
# Header: Authorization: Bearer YOUR_API_KEY
{
"model": "google/gemma-4-26B-A4B-it",
"messages": [
{
"role": "user",
"content": "What are the key differences between transformer architectures?"
}
],
"temperature": 0.7,
"max_tokens": 500
}Response Format
{
"id": "gen_abc123",
"object": "chat.completion",
"created": 1707764532,
"model": "google/gemma-4-26B-A4B-it",
"choices": [
{
"index": 0,
"message": {
"role": "assistant",
"content": "The key differences between transformer architectures..."
},
"finish_reason": "stop"
}
],
"usage": {
"prompt_tokens": 12,
"completion_tokens": 156,
"total_tokens": 168
}
}OpenAI compatibility
Point any OpenAI SDK or client at https://inference.raindragon.ai/v1 with your API key. The same key also works with Anthropic clients at https://inference.raindragon.ai (POST /v1/messages); see coding tools for per-tool setup.
Machine-readable: the public endpoints are described in an OpenAPI spec at /openapi.json, and every docs page is available as Markdown for LLMs and coding agents (this page: /api-documentation.md; index: /llms.txt).
Endpoints
POST /v1/chat/completions chat (streaming with "stream": true) POST /v1/responses OpenAI Responses API POST /v1/messages Anthropic Messages API GET /v1/models models, per-token prices, context_length POST /v1/audio/transcriptions Whisper speech-to-text POST /v1/files, /v1/batches Batch API (below)
Chat completion parameters
Supported: model, messages, max_tokens, max_completion_tokens, temperature, top_p, frequency_penalty, presence_penalty, stop, seed, stream, stream_options, n, logprobs, top_logprobs (up to 20), logit_bias, response_format, tools, tool_choice, parallel_tool_calls, user, metadata, reasoning_effort and service_tier. Sampling extensions: top_k, min_p, repetition_penalty, min_tokens, stop_token_ids.
Other fields are ignored, as OpenAI ignores unknown fields. A few server-side controls are refused with HTTP 400 unsupported_parameter instead, so you know you did not get them: prompt_logprobs, priority, chat_template, logits_processors and kv_transfer_params. best_of is not supported; use n.
Send "stream_options": {"include_usage": true} to get the usage block (including cached tokens) in the last chunk of a stream.
Reasoning (thinking) mode
Both Gemma 4 models can think before they answer. Thinking is off by default. Turn it on with any one of:
"reasoning_effort": "low" | "medium" | "high"(any value except"none")"chat_template_kwargs": {"enable_thinking": true}"reasoning": {"enabled": true}(OpenRouter style)
The reasoning text comes back in its own field, never inline in content: choices[0].message.reasoning (streaming: choices[0].delta.reasoning), mirrored as reasoning_content. Reasoning tokens are counted in usage.completion_tokens and billed at the output rate. To keep thinking on but drop the text from the response, send "reasoning": {"exclude": true}.
curl https://inference.raindragon.ai/v1/chat/completions \
-H "Authorization: Bearer $API_KEY" -H "Content-Type: application/json" \
-d '{"model": "google/gemma-4-26B-A4B-it", "reasoning_effort": "medium",
"messages": [{"role": "user", "content": "Is 1001 prime?"}]}'Function calling (tools)
OpenAI-compatible: send tools (and optionally tool_choice, parallel_tool_calls). Calls come back as message.tool_calls with finish_reason: "tool_calls"; return results as role: "tool" messages. Supported on both Gemma 4 models.
{"model": "google/gemma-4-31B-it",
"messages": [{"role": "user", "content": "Weather in Paris?"}],
"tools": [{"type": "function", "function": {"name": "get_weather",
"parameters": {"type": "object", "properties": {"city": {"type": "string"}}, "required": ["city"]}}}],
"tool_choice": "auto"}JSON mode
Send "response_format": {"type": "json_object"} to get a single valid JSON object in content. Say in the prompt what keys you want.
Prompt caching
Automatic, nothing to enable and no cache-write fee. Prompt tokens that are already in our prefix cache are billed at the cached-input price on the pricing page and reported in usage.prompt_tokens_details.cached_tokens. Keep the stable part of a prompt (system prompt, tools, earlier turns) at the start and unchanged between requests.
How it is billed. usage.prompt_tokens counts every prompt token, and usage.prompt_tokens_details.cached_tokens of them were cache hits. Cached tokens are billed at the cached-input rate, the rest of the prompt at the input rate, and completion_tokens at the output rate.
Identify the conversation. For multi-turn chat, send a stable id for the conversation in metadata.session (we also accept a top-level group field), and a stable id for your end user in user. We use them to keep each conversation's cached prefix together between turns. Without them we can only infer the conversation from the system prompt, which works poorly when many conversations share one. Both are optional and change neither the price nor the output. metadata is never sent to the model or to another provider. Use opaque ids, not personal data.
{"model": "google/gemma-4-26B-A4B-it",
"messages": [...],
"user": "user-8f3a",
"metadata": {"session": "conv-2c91"}}Cached prefixes are not guaranteed to stay: one that has not been used for a while can be evicted, and the next request then pays the input rate for it.
Audio Transcription API
Transcribe audio to text using Whisper v3 Turbo. Supports mp3, mp4, wav, webm, flac, and ogg.
cURL
curl -X POST "https://inference.raindragon.ai/v1/audio/transcriptions" \ -H "Authorization: Bearer YOUR_API_KEY" \ -F "file=@recording.mp3" \ -F "model=openai/whisper-large-v3-turbo"
Batch API
OpenAI-compatible asynchronous jobs at 15% off the normal per-token price (Anthropic Message Batches at /v1/messages/batches run the same way at the same discount). Upload a JSONL file of /v1/chat/completions requests, create a batch with a completion window (default 24h), and download the results when it completes. Batch requests run on spare capacity, so they never slow down interactive traffic; lines not finished inside the window are reported as batch_expired in the error file and are not billed. Streaming is not supported in a batch.
Endpoints
POST /v1/files multipart: purpose=batch, file=@requests.jsonl
POST /v1/batches {"input_file_id", "endpoint": "/v1/chat/completions", "completion_window": "24h"}
GET /v1/batches list your batches
GET /v1/batches/{id} status, request_counts, output_file_id, error_file_id
POST /v1/batches/{id}/cancel cancel; requests not yet started are not billed
GET /v1/files/{id}/content download input, output or error JSONLInput line
{"custom_id": "req-1", "method": "POST", "url": "/v1/chat/completions",
"body": {"model": "google/gemma-4-26B-A4B-it", "messages": [{"role": "user", "content": "Hello"}], "max_tokens": 256}}Python (OpenAI SDK)
from openai import OpenAI
client = OpenAI(base_url="https://inference.raindragon.ai/v1", api_key="YOUR_API_KEY")
f = client.files.create(file=open("requests.jsonl", "rb"), purpose="batch")
b = client.batches.create(input_file_id=f.id, endpoint="/v1/chat/completions",
completion_window="24h")
b = client.batches.retrieve(b.id) # poll until b.status == "completed"
print(client.files.content(b.output_file_id).text)Flex processing
Set "service_tier": "flex" on a normal /v1/chat/completions request (streaming included) to run it on spare capacity at a lower price: the model's normal per-token prices (uncached input, cached input and output, counted exactly as for any request) times the flex multiplier, currently 0.5 (half price). The response echoes "service_tier": "flex" (on every chunk when streaming) and an X-Service-Tier: flex header.
Latency. A flex request may wait for capacity before it starts, up to about 90 seconds, and runs at a lower priority than standard traffic, so it can generate more slowly. Use a client timeout of several minutes, not the default 60 s. Omitting service_tier, or sending "auto" or "default", is the standard tier, unchanged.
429 resource_unavailable. If no spare capacity opens up within the wait, the request fails with HTTP 429 and error.code = "resource_unavailable", with a Retry-After header. It is not billed and is never sent to another provider. Retry with exponential backoff (honour Retry-After), or resend with the standard tier if you need the answer now. A flex request that cannot be served by our own engine is refused the same way rather than routed elsewhere.
from openai import OpenAI
client = OpenAI(base_url="https://inference.raindragon.ai/v1", api_key="YOUR_API_KEY",
timeout=600)
r = client.chat.completions.create(model="google/gemma-4-26B-A4B-it", service_tier="flex",
messages=[{"role": "user", "content": "Hello"}])
print(r.service_tier) # "flex"Rate limits
Inference has no fixed per-key limit. Text generation (/v1/chat/completions, /v1/completions, /v1/messages, /v1/responses) and /v1/audio/transcriptions have no requests-per-minute or tokens-per-minute cap per API key. The limit is live capacity: when our engines are full, a text request is refused with HTTP 429 rather than left waiting in a long queue.
429 server_overloaded. The body has error.type = "rate_limit_error" and error.code = "server_overloaded", and the response has a Retry-After header (1 to 30 seconds). A refused request is not billed. Wait for Retry-After, then retry.
Context length. The maximum context (prompt plus output) for each model is the context_length field of GET /v1/models, which is the authoritative value. Currently google/gemma-4-26B-A4B-it is 204,800 tokens and google/gemma-4-31B-it is 65,536 tokens.
Other endpoints
| Endpoints | Limit |
|---|---|
/v1/cache/..., /v1/scores/..., /v1/billing/costs | 100 requests per minute per API key |
| Management API | 60 requests per minute per management key |
| Sign-in, sign-up and password reset | 5 requests per minute per IP |
Going over one of these returns HTTP 429 with a Retry-After header. Planning a large launch or a sustained high volume? Contact us so we can reserve capacity.
Multiple choices (n)
Set n to get several independent completions of one prompt, up to 16 per request (the current limit is billing.max_n in GET /api/models; a larger n is refused with HTTP 400). The prompt is billed once. Every choice's output is billed at the output rate: usage.completion_tokens is the sum over all choices. For more candidates, send several requests. Server-side best-of selection (best_of) is not supported.
Errors
Errors carry an object with message, type and code (and param where one field is at fault). Most responses put it under error, as OpenAI does. Request-validation and authentication errors put it under detail, next to a request_id. Quote the request_id when you contact us. Refused requests are not billed.
| Status | Code | Meaning and what to do |
|---|---|---|
| 400 | unsupported_parameter | A refused field or a value out of range (see param). Fix the request; do not retry. |
| 401 | (none) | Missing or invalid API key. |
| 402 | (none) | No payment method or credit on the account. Add one at /setup-payment. |
| 404 | model_not_found | Unknown model id. List models with GET /v1/models. |
| 429 | server_overloaded | Our engines are full. Wait for Retry-After, then retry (see rate limits). |
| 429 | resource_unavailable | A flex request found no spare capacity. Retry later, or resend on the standard tier. |
| 503 | auth_unavailable, db_unavailable, db_pool_exhausted, upstream_unavailable | Temporarily busy (type: "server_busy"); the request was not processed. Retry after Retry-After. |
| 500 | (none) | Unexpected server error. Retry with backoff and send us the request_id if it repeats. |
| in stream | stream_interrupted | A stream with tool calls or n > 1 failed after part of the reply was sent. It cannot be resumed; resend the request. |
Example Code
cURL
curl -X POST "https://inference.raindragon.ai/v1/chat/completions" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_API_KEY" \
-d '{
"model": "google/gemma-4-26B-A4B-it",
"messages": [
{
"role": "user",
"content": "What are the key differences between transformer architectures?"
}
],
"temperature": 0.7,
"max_tokens": 500
}'Python
import requests
import json
api_key = "YOUR_API_KEY" # Store securely in environment variables
headers = {
"Authorization": f"Bearer {api_key}",
"Content-Type": "application/json"
}
payload = {
"model": "google/gemma-4-26B-A4B-it",
"messages": [
{
"role": "user",
"content": "Explain quantum computing."
}
],
"temperature": 0.7,
"max_tokens": 500
}
response = requests.post(
"https://inference.raindragon.ai/v1/chat/completions",
headers=headers,
json=payload
)
result = response.json()
print(result["choices"][0]["message"]["content"])JavaScript/TypeScript
const API_KEY = 'YOUR_API_KEY';
fetch('https://inference.raindragon.ai/v1/chat/completions', {
method: 'POST',
headers: {
'Authorization': `Bearer ${API_KEY}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({
model: 'google/gemma-4-26B-A4B-it',
messages: [
{
role: 'user',
content: 'Explain quantum computing.'
}
],
temperature: 0.7,
max_tokens: 500
})
})
.then(response => response.json())
.then(data => console.log(data.choices[0].message.content))
.catch(error => console.error('Error:', error));Fill-or-Kill Requests
A normal request has no deadline: if capacity is busy it waits, and you only find out that the answer is late. A fill-or-kill (FOK) request states a time to live (TTL). We either start processing it on our own engine within the TTL, or reject it immediately with HTTP 429, so your code can go elsewhere while the answer is still worth having. A FOK request is never queued waiting for a slot, and it is never forwarded to another provider as a substitute for starting it ourselves.
It is opt-in per request; requests that do not ask for it are unaffected. Use it when a fast no is worth more than a slow yes:
- agents with a per-turn time budget that can fall back to another model or provider;
- latency-sensitive interactive calls (autocomplete, routing, classification) where a late answer is useless;
- routers that fan out across providers and want an immediate, machine-readable refusal.
The TTL is a promise about when generation starts, not when it finishes. Total latency still depends on prompt and output length.
Request: body form
Add a fill_or_kill object to a normal /v1/chat/completions body. It is removed by the gateway before the request reaches the model.
POST /v1/chat/completions
{
"model": "google/gemma-4-26B-A4B-it",
"messages": [{"role": "user", "content": "Summarise this ticket in one line."}],
"max_tokens": 200,
"fill_or_kill": {
"ttl_ms": 2000,
"bid": {"type": "request", "amount_usd": 0.02}
}
}| Field | Meaning |
|---|---|
| ttl_ms | Milliseconds within which generation must start. Default 2000 if omitted. Also accepted: ttl_s, ttl_seconds, or ttl (all in seconds). |
| deadline | Optional absolute deadline as a Unix timestamp, in seconds or milliseconds (values above 1011 are read as milliseconds). Useful when you have already spent part of your budget. When present, the deadline is what admission checks against. A deadline already in the past is rejected with 429. |
| bid | Required. What the request is worth to you, in USD. Either a whole-request price or per-million-token rates (below). A bare number, e.g. "bid": 0.02, is shorthand for a request price. Zero is allowed; negative is not. |
Per-million-token bid (any subset of the three rates; at least one is required):
"bid": {
"type": "tokens",
"input_usd_per_mtok": 0.50,
"cached_usd_per_mtok": 0.10,
"output_usd_per_mtok": 1.50
}type can be omitted when only one shape's fields are present. If both a request price and per-token rates are present without a type, the request is rejected with 400 rather than guessed at, since the two shapes differ by about six orders of magnitude. The only supported currency is usd.
Absolute deadline instead of a TTL:
"fill_or_kill": {
"deadline": 1791043200500,
"bid": 0.02
}Request: header form
For SDKs or proxies that cannot add body fields, the same request can be expressed with headers. Headers are only read when the body carries no fill_or_kill object.
| Header | Meaning |
|---|---|
| X-Fill-Or-Kill | Set to 1 (or true/yes/on) to make the request fill-or-kill. X-Fok is an alias. |
| X-Fok-Ttl-Ms | TTL in milliseconds. |
| X-Fok-Deadline | Absolute deadline, Unix seconds or milliseconds. |
| X-Fok-Bid-Usd | Whole-request bid in USD. |
| X-Fok-Bid-Input-Per-Mtok | Per-token bid: uncached input, USD per million tokens. |
| X-Fok-Bid-Output-Per-Mtok | Per-token bid: output, USD per million tokens. |
| X-Fok-Bid-Cached-Per-Mtok | Per-token bid: cached input, USD per million tokens. |
A bid header is required, exactly as in the body form.
cURL (body form)
curl -sS -i "https://inference.raindragon.ai/v1/chat/completions" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_API_KEY" \
-d '{
"model": "google/gemma-4-26B-A4B-it",
"messages": [{"role": "user", "content": "Summarise this ticket in one line."}],
"max_tokens": 200,
"fill_or_kill": {"ttl_ms": 2000, "bid": {"type": "request", "amount_usd": 0.02}}
}'cURL (header form)
curl -sS -i "https://inference.raindragon.ai/v1/chat/completions" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "X-Fill-Or-Kill: 1" \
-H "X-Fok-Ttl-Ms: 2000" \
-H "X-Fok-Bid-Usd: 0.02" \
-d '{
"model": "google/gemma-4-26B-A4B-it",
"messages": [{"role": "user", "content": "Summarise this ticket in one line."}],
"max_tokens": 200
}'Python (OpenAI SDK)
Use extra_body or extra_headers. Set max_retries=0 on the client (or per call with client.with_options(max_retries=0)): the OpenAI SDK automatically retries 429 responses with backoff, which turns an immediate rejection into a slow one.
import openai
from openai import OpenAI
client = OpenAI(
base_url="https://inference.raindragon.ai/v1",
api_key="YOUR_API_KEY",
max_retries=0, # IMPORTANT: the SDK retries 429s by default; a FOK reject
# is an answer, not a transient error.
)
try:
resp = client.chat.completions.create(
model="google/gemma-4-26B-A4B-it",
messages=[{"role": "user", "content": "Summarise this ticket in one line."}],
max_tokens=200,
# Option A: body field
extra_body={"fill_or_kill": {
"ttl_ms": 2000,
"bid": {"type": "request", "amount_usd": 0.02},
}},
# Option B (instead of A): headers
# extra_headers={"X-Fill-Or-Kill": "1",
# "X-Fok-Ttl-Ms": "2000",
# "X-Fok-Bid-Usd": "0.02"},
)
print(resp.choices[0].message.content)
except openai.RateLimitError as e:
body = e.body if isinstance(e.body, dict) else {}
detail = body.get("detail", body)
if isinstance(detail, dict) and detail.get("type") == "fill_or_kill_rejected":
# Not started within the TTL. Nothing ran, nothing was billed.
print("rejected:", detail["reason"], "est. start ms:", detail["estimated_start_ms"])
# ...send it somewhere else, or retry later on your own schedule.
else:
raise # an ordinary rate limit, not a fill-or-kill rejection# Need the X-Fill-Or-Kill / X-Fok-Request-Id response headers? Use with_raw_response:
raw = client.chat.completions.with_raw_response.create(
model="google/gemma-4-26B-A4B-it",
messages=[{"role": "user", "content": "Hi"}],
extra_body={"fill_or_kill": {"ttl_ms": 2000, "bid": 0.02}},
)
print(raw.headers.get("X-Fill-Or-Kill")) # "accepted"
resp = raw.parse()Response: accepted
A normal chat completion, with two extra response headers. If X-Fill-Or-Kill: accepted is absent, the request was served as an ordinary (non-FOK) request, for example because the feature is switched off.
HTTP/1.1 200 OK
X-Fill-Or-Kill: accepted
X-Fok-Request-Id: 3f2c9a0e8b7d4c1fa6e5d4c3b2a19087
{ ...a normal chat.completion... }With "stream": true, the first event on the stream is an acknowledgement chunk with an empty delta and an x_fok object, sent as soon as the request is admitted. Standard SDKs treat it like any empty chunk (so delta.content is None/null on it); read x_fok from the raw event if you want it. The model's output follows afterwards, so do not expect text immediately after the acknowledgement.
data: {"id": "fok-3f2c9a0e8b7d", "object": "chat.completion.chunk", "created": 1791043200,
"model": "google/gemma-4-26B-A4B-it",
"choices": [{"index": 0, "delta": {}, "finish_reason": null}],
"x_fok": {
"accepted": true,
"status": "generating",
"request_id": "3f2c9a0e8b7d4c1fa6e5d4c3b2a19087",
"ttl_ms": 2000,
"estimated_start_ms": 0,
"bid": {"type": "request", "amount_usd": 0.02, "currency": "usd"},
"reason": "running 3, queue 0, est start 0ms within 2000ms",
"billing": "metered_rate_not_bid",
"preemptable": true
}}
data: {...normal chunks...}An accepted FOK request runs at lower scheduling priority than ordinary traffic ("preemptable": true). If the engine becomes full while it is generating, it may be paused and recomputed rather than an ordinary request. You still get the full answer, but time after the start can be longer than for a normal request. In rare cases a failure after generation has started can be completed by a fallback provider; the start promise has already been kept at that point.
Response: rejected (HTTP 429)
Returned immediately, before the request reaches the model. A rejected request is not billed. There is no Retry-After header; use estimated_start_ms (our estimate of how long you would have waited, or null if unknown) to decide whether and when to retry. Identify a FOK rejection by detail.type == "fill_or_kill_rejected"; other 429s (account rate limits, server overload) have different bodies.
HTTP/1.1 429 Too Many Requests
Content-Type: application/json
{
"detail": {
"message": "Fill-or-kill request rejected: cannot start within the requested TTL.",
"type": "fill_or_kill_rejected",
"code": "capacity",
"request_id": "3f2c9a0e8b7d4c1fa6e5d4c3b2a19087",
"ttl_ms": 2000,
"reason": "queue depth 5 > 2",
"estimated_start_ms": 3120,
"engine": {"running": 18.0, "waiting": 5.0, "ttft_mean_s": 1.9}
},
"request_id": "req_..."
}detail.request_id is the FOK decision id (the same value as X-Fok-Request-Id on an accept); the outer request_id is the gateway's request id. reason is human-readable and may change; branch on type and code. Reasons you may see include: the deadline already passed, recent time-to-first-token too high, queue too deep, too many running requests, estimated start later than your TTL, the prompt does not fit our engine (context length or free KV cache), the engine is unhealthy, or being outbid (see below).
Response: invalid request (HTTP 400)
A fill_or_kill block we cannot read is an error, never silently served as a normal request:
HTTP/1.1 400 Bad Request
{
"detail": {
"message": "bid carries both a request price and per-token rates; set bid.type to say which one applies",
"type": "invalid_request_error",
"param": "fill_or_kill",
"code": "invalid_fill_or_kill"
},
"request_id": "req_..."
}Causes include:
fill_or_killis not an object (includingtrue), or carries nobid;- a TTL, deadline or bid value that is not a finite, non-negative number;
- a bid with both a request price and per-token rates and no
type, or atypethat contradicts the fields present; bid.typeother thanrequestortokens;- a currency other than
usd.
How the bid works today
Accepted FOK requests are billed at our normal metered rates, not at your bid. The bid is a statement of what the request is worth to you, not a price quote: bidding high does not raise your bill, and bidding low (or zero) does not lower it. Every bid is recorded alongside the decision.
When there is spare capacity, which is the usual case, the bid has no effect at all: admission depends only on load and your TTL. When capacity is contested (requests are queueing, the engine is close to its running limit, or the KV cache is nearly full), FOK requests that pass the load checks enter a short auction:
- your request is held for up to 250 ms (never more than half of your remaining TTL, minus a 150 ms reserve) so that other FOK bids arriving in that window can compete;
- bids are ranked by our estimate of the value of your bid per unit of engine resource the request would use (from your bid, the prompt length and predicted output length), not by the raw bid amount, so a large prompt needs a proportionally larger bid;
- the top-ranked bids take the available slots (one slot when the signal is queueing; otherwise up to four, capped by the headroom left below the fill-or-kill limit on running requests, so a request that passed admission always gets at least one); the rest are rejected with 429 and a reason beginning
outbid.
The auction only decides which FOK requests start. It does not change what an accepted request is charged, and it never affects ordinary (non-FOK) traffic. If there is no time left for an auction, or anything in it fails, the request is admitted on the load checks alone.
Limits
| Limit | Value |
|---|---|
| Minimum TTL | 50 ms. Smaller values are raised to this (not rejected), except for an absolute deadline already in the past, which is rejected. |
| Maximum TTL | 120,000 ms (120 s). Larger values are clamped down, not rejected. |
| Default TTL | 2,000 ms when neither a TTL nor a deadline is given. |
| Bid | Required; non-negative; USD only. |
| n > 1 | Each of the n choices needs its own engine slot, so a large n is more likely to be rejected. |
| Endpoint | /v1/chat/completions only. |
The returned ttl_ms (in the 429 body and in x_fok) is the TTL after clamping, so you can see whether yours was adjusted. If our load metrics cannot be read at all, FOK requests are admitted rather than rejected.
