API Documentation

Simple integration with our API

Model Compatibility

Model NameParametersDescription
google/gemma-4-26B-A4B-it26B MoE (~4B active)Sparse MoE — frontier quality at small-model latency
google/gemma-4-31B-it31B denseDense Gemma 4 — highest quality text model we serve
openai/whisper-large-v31.5BSpeech-to-text — billed per second of audio (priced per audio hour), not per token
openai/whisper-large-v3-turbo0.8BFaster Whisper, near-large-v3 accuracy
distil-whisper/distil-large-v30.75BDistilled 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 JSONL

Input 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

EndpointsLimit
/v1/cache/..., /v1/scores/..., /v1/billing/costs100 requests per minute per API key
Management API60 requests per minute per management key
Sign-in, sign-up and password reset5 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.

StatusCodeMeaning and what to do
400unsupported_parameterA 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.
404model_not_foundUnknown model id. List models with GET /v1/models.
429server_overloadedOur engines are full. Wait for Retry-After, then retry (see rate limits).
429resource_unavailableA flex request found no spare capacity. Retry later, or resend on the standard tier.
503auth_unavailable, db_unavailable, db_pool_exhausted, upstream_unavailableTemporarily 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 streamstream_interruptedA 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}
  }
}
FieldMeaning
ttl_msMilliseconds within which generation must start. Default 2000 if omitted. Also accepted: ttl_s, ttl_seconds, or ttl (all in seconds).
deadlineOptional 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.
bidRequired. 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.

HeaderMeaning
X-Fill-Or-KillSet to 1 (or true/yes/on) to make the request fill-or-kill. X-Fok is an alias.
X-Fok-Ttl-MsTTL in milliseconds.
X-Fok-DeadlineAbsolute deadline, Unix seconds or milliseconds.
X-Fok-Bid-UsdWhole-request bid in USD.
X-Fok-Bid-Input-Per-MtokPer-token bid: uncached input, USD per million tokens.
X-Fok-Bid-Output-Per-MtokPer-token bid: output, USD per million tokens.
X-Fok-Bid-Cached-Per-MtokPer-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_kill is not an object (including true), or carries no bid;
  • 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 a type that contradicts the fields present;
  • bid.type other than request or tokens;
  • 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

LimitValue
Minimum TTL50 ms. Smaller values are raised to this (not rejected), except for an absolute deadline already in the past, which is rejected.
Maximum TTL120,000 ms (120 s). Larger values are clamped down, not rejected.
Default TTL2,000 ms when neither a TTL nor a deadline is given.
BidRequired; non-negative; USD only.
n > 1Each 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.