apiToken.saleDocumentation

Connect any model

One API key, one endpoint, every available model — native Anthropic, OpenAI, and Gemini protocols plus an OpenAI-compatible API for any client. Your AI agent configures and verifies the connection.

For your agent

Read https://apitoken.sale/md/connect and follow the instructions to connect this project to apiToken.sale.

Connection help

IDE, SDK, endpoint, models, and request errors.

TelegramAI 24/7 · human when needed
02

Quick start

Connect apiToken.sale to the coding agent that reads, edits, and runs your project. Choose the stack — the exact setup appears below.

Integrations

Connect your coding agent

Choose a provider, a coding agent, and your operating system — the guide updates instantly.

Provider
Operating system
Coding agent

Claude Code · Claude Fable 5.1

Native Claude coding agent through the Anthropic Messages API.

  • Claude
  • Claude Code
  • macOS / Linux · zsh · bash
  • Claude Fable 5.1
Endpointhttps://router.apitoken.sale
  1. Run one-command setup

    The setup asks for the provider and key, fetches every model for that provider, writes a private launcher, and removes the competing auth variable.

    Terminal
    curl -fsSL https://apitoken.sale/setup/connect.sh | bash
  2. Start Claude Code

    The explicit model flag avoids inheriting a model from an old login or project setting.

    Run
    "$HOME/.local/bin/apitoken-claude-code" --model claude-fable-5-1
  3. Verify inside Claude Code

    The status screen must show apiToken.sale as the Anthropic base URL and ANTHROPIC_API_KEY as the credential source.

    Inside Claude Code
    /statusReply with exactly: connected
The full key stays in your terminal, never in project files.Get API key
03

One endpoint, every protocol

router.apitoken.sale is the single entry point for all providers. Coding agents and official SDKs get byte-faithful native APIs; any OpenAI-compatible client reaches every catalog model through one universal route. Same sk-pool key, same prepaid balance, everywhere.

Base URLhttps://router.apitoken.sale

Native APIs

Byte-faithful provider protocols — for coding agents and official SDKs that need full fidelity: thinking, tool use, prompt caching, provider betas.

  • POST/v1/messagesAnthropic
  • POST/v1/responsesOpenAI
  • POST/v1beta/models/{model}:generateContentGemini

OpenAI-compatible

One universal route for every catalog model — Claude, GPT, and Gemini — from any OpenAI-compatible client or SDK. Unsupported parameters fail closed with a clear 400.

  • POST/v1/chat/completionsAny catalog model

Unified catalog

Every enabled model under namespaced IDs — anthropic/*, openai/*, google/*, kimi/*. Bare native IDs keep working while they are unambiguous.

  • GET/v1/modelsAny catalog model

Already integrated? The per-provider endpoints api.apitoken.sale, openai.api.apitoken.sale/v1 and gemini.api.apitoken.sale remain fully supported with the same key and balance — the unified router is the recommended entry for new integrations.

Claude · Native API · cURL

Anthropic Messages API on the unified endpoint — byte-faithful protocol with your sk-pool key in x-api-key. SDKs add anthropic-version automatically.

  • Claude
  • x-api-key · anthropic-version
Endpointhttps://router.apitoken.sale
  1. Store the key in the environment

    Keep the key server-side: an environment variable or a secret manager, never a browser bundle. On Windows PowerShell use $env:APITOKEN_API_KEY instead.

    Terminal
    export APITOKEN_API_KEY="sk-pool-•••"
  2. Send the first request

    Every available Claude model answers on this route. Discover the current list with GET /v1/models on the same endpoint instead of hardcoding IDs.

    HTTP
    curl https://router.apitoken.sale/v1/messages \  -H "x-api-key: $APITOKEN_API_KEY" \  -H "anthropic-version: 2023-06-01" \  -H "Content-Type: application/json" \  -d {    "model": "claude-fable-5-1",    "max_tokens": 1024,    "messages": [{"role": "user", "content": "Reply with exactly: connected"}]  }
  3. Read the balance and the spend

    The same key reads its own account: GET https://router.apitoken.sale/usage returns the live balance and the spend over any window — from and to in RFC3339 UTC (to is exclusive), group_by one of model, provider, day, day,provider, day,model, api_key. Defaults: the last 30 days grouped by model. One report covers every provider in the catalog. cost is what you were charged, list_cost is the provider list price for the same traffic; each also comes as an exact integer *_nano string — parse money as a string, never as a float. The window may not exceed 366 days. GET https://router.apitoken.sale/balance returns the balance alone.

    HTTP
    curl -sG "https://router.apitoken.sale/usage" \  -H "x-api-key: $APITOKEN_API_KEY" \  --data-urlencode "from=2026-08-01T00:00:00Z" \  --data-urlencode "to=2026-09-01T00:00:00Z" \  --data-urlencode "group_by=model"
One sk-pool key works on every lane of the unified endpoint. Never ship it in client-side code.Get API key
04

Balance and usage API

The same key that spends the money reads what it spent. One route returns the live balance and the exact spend over any window, across every provider — no dashboard, no second credential.

ParameterWhat it does
fromStart of the window, inclusive. RFC3339 UTC — 2026-08-01T00:00:00Z, an explicit numeric offset, or a bare 2026-08-01. Defaults to to minus 30 days.
toEnd of the window, **exclusive**. Same forms. Defaults to now; a future value is clamped to now.
group_bymodel (default), provider, day, day,provider, day,model or api_key. Order inside the list does not matter, and model,provider is simply an alias of model — a model row already names its provider.

One request

Send your sk-pool- key in x-api-key (or as Authorization: Bearer). Every entry host serves this route.

HTTP · Request
curl -sG "https://router.apitoken.sale/usage" \  -H "x-api-key: $APITOKEN_API_KEY" \  --data-urlencode "from=2026-08-01T00:00:00Z" \  --data-urlencode "to=2026-09-01T00:00:00Z" \  --data-urlencode "group_by=model"

The answer

The response echoes the exact interval it used, then the live balance, the totals and one row per group.

JSON · Response
{  "account": "acct_…",  "from": "2026-08-01T00:00:00Z",  "to": "2026-09-01T00:00:00Z",  "group_by": "model",  "balance": {    "balance": "$12.345678",    "balance_nano": 12345678000,    "reserved_nano": 0,    "spent": "$3.210000",    "status": "active"  },  "totals": {    "requests": 1804,    "cost": "$3.210000",    "cost_nano": "3210000000",    "list_cost": "$6.420000",    "input_tokens": 9000000,    "output_tokens": 250000,    "cache_read_tokens": 4100000  },  "data": [    {      "provider": "anthropic",      "model": "claude-opus-4-8",      "requests": 1200,      "cost": "$2.100000",      "cost_nano": "2100000000",      "list_cost": "$4.200000",      "input_tokens": 7000000,      "output_tokens": 180000    }  ]}

Two prices, both exact. cost is what you were charged; list_cost is the provider list price for the same traffic — the difference is your discount. Each also comes as an integer *_nano string. Read money as a string, never as a float.

Explicit UTC only. Local time without an offset is rejected: guessing a zone would silently move the boundaries of a money report. The window must be positive and at most 366 days, otherwise the answer is 400 in the host protocol error envelope (Anthropic, OpenAI or Google of that host), with the reason in error.message.

Slices that add up. Every slice of one window sums to the same total, because all of them are read from a single consistent snapshot. Token counts live in the model, provider and day,model slices; the plain day slices carry money and request counts. day,model — the per-model daily series — is capped at 92 days and answers 400 beyond that instead of silently dropping rows. Keys are masked. GET /balance returns the balance alone.

05

Models & pricing

All providers, every available model, and exact per-1M-token rates — official list price vs. what you actually pay at the flat 50% discount.

Claude · Anthropic Messages API

router.apitoken.salePOST /v1/messages
x-api-key
ModelContextMax outputInputCached inputCache writeOutput
Claude Fable 5.1Latestclaude-fable-5-11M128K$5$10$0.125$0.25$6.25$12.5$25$50
Claude Opus 5claude-opus-51M128K$2.5$5$0.25$0.5$3.125$6.25$12.5$25
Claude Fable 5claude-fable-51M128K$5$10$0.5$1$6.25$12.5$25$50
Claude Opus 4.8claude-opus-4-81M128K$2.5$5$0.25$0.5$3.125$6.25$12.5$25
Claude Opus 4.7claude-opus-4-71M128K$2.5$5$0.25$0.5$3.125$6.25$12.5$25
Claude Sonnet 5claude-sonnet-51M128K$1$2$0.1$0.2$1.25$2.5$5$10
Claude Sonnet 4.6claude-sonnet-4-6200K128K$1.5$3$0.15$0.3$1.875$3.75$7.5$15
Claude Haiku 4.5claude-haiku-4-5200K64K$0.5$1$0.05$0.1$0.625$1.25$2.5$5
Cache reads bill at the cached-input rate (10% of input). Cache writes: 1.25× input for a 5-minute TTL, 2× input for a 1-hour TTL — send the anthropic-beta: extended-cache-ttl-2025-04-11 header yourself.

GPT · OpenAI Responses API

router.apitoken.salePOST /v1/responses · POST /v1/chat/completions
Authorization: Bearer
ModelContextMax outputInputCached inputCache writeOutput
GPT-6 AstraLatestgpt-6-astra872K128K$5$10$0.5$1$6.25$12.5$25$50
GPT-5.6 Solgpt-5.6-sol1.05M128K$2$4$0.2$0.4$2.5$5$10$20
GPT-5.6 Terragpt-5.6-terra1.05M128K$1$2$0.1$0.2$1.25$2.5$6$12
GPT-5.6 Lunagpt-5.6-luna1.05M128K$0.1$0.2$0.01$0.02$0.125$0.25$0.6$1.2
GPT-5.5gpt-5.51.05M128K$2.5$5$0.25$0.5$2.5$5$15$30
GPT-5.4gpt-5.41.05M128K$1.25$2.5$0.125$0.25$1.25$2.5$7.5$15
GPT Image 2gpt-image-2per request1 image$2.5$5$0.625$1.25$0$0$15$30
GPT-6 Astra is the latest GPT model: 872K maximum context and 128K output. Caching is automatic — repeated prefixes bill at the cached-input rate with no opt-in. gpt-5.6 is an alias of gpt-5.6-sol.

Gemini · Google Gemini API

router.apitoken.salePOST /v1beta/models/{model}:generateContent
x-goog-api-key
ModelContextMax outputInputCached inputCache writeOutput
Gemini 3.8 FlashLatestgemini-3.8-flash1M64K$0.375$0.75$0.0375$0.075$1.875$3.75
Gemini 3.7 Flashgemini-3.7-flash1M64K$0.375$0.75$0.0375$0.075$1.875$3.75
Gemini 3.6 Flashgemini-3.6-flash1M64K$0.375$0.75$0.0375$0.075$1.875$3.75
Gemini 3.5 Flashgemini-3.5-flash1M64K$0.75$1.5$0.075$0.15$4.5$9
Gemini 3 Flash Previewgemini-3-flash-preview1M64K$0.25$0.5$0.025$0.05$1.5$3
Gemini 3.1 Pro Previewgemini-3.1-pro-preview1M64K$1$2$0.1$0.2$6$12
Gemini 3.1 Flash-Litegemini-3.1-flash-lite1M64K$0.125$0.25$0.0125$0.025$0.75$1.5
Gemini 2.5 Flashgemini-2.5-flash1M64K$0.15$0.3$0.015$0.03$1.25$2.5
Gemini 2.5 Flash-Litegemini-2.5-flash-lite1M64K$0.05$0.1$0.005$0.01$0.2$0.4
Cached input bills at the cached-input rate (10% of input). Gemini Batch uses the same standard Google token tariff and your normal account discount: there is no separate Batch discount or completion-time SLA. Batch is asynchronous and accepted jobs may remain queued during temporary capacity pressure. gemini-3.1-pro-preview switches to long-context rates above 200K input tokens.

Kimi · Anthropic Messages API

router.apitoken.salePOST /v1/messages
x-api-key
ModelContextMax outputInputCached inputCache writeOutput
Kimi K3Latestk31Mnot published$1.5$3$0.15$0.3$1.5$3$7.5$15
Kimi K3 (256K)k3-256k256Knot published$1.5$3$0.15$0.3$1.5$3$7.5$15
Kimi for Codingkimi-for-coding256Knot published$0.475$0.95$0.095$0.19$0.475$0.95$2$4
Kimi for Coding HighSpeedkimi-for-coding-highspeed256Knot published$0.95$1.9$0.19$0.38$0.95$1.9$4$8
Cached input bills at the cached-input rate (10% of input). Kimi publishes no separate cache-write rate — a write is a cache miss and bills at the input rate. k3[1m] is an alias of k3 for clients that spell the 1M window that way; k3-256k is the same model and the same rates with a 256K window.
Example · Claude Opus 4.82M input × $10 = $20400K output × $50 = $20Official total = $40
You pay$20
A $100 top-up buys $200 of official API usage on any model — every dollar converts at the same rate.
  • Rates are per 1M tokens; billing is metered per token at official provider rates, then the flat 50% discount is subtracted.
  • Thinking and reasoning tokens bill as output on both providers.
  • GPT requests above 272K input tokens bill at official long-context rates: 2× input and 1.5× output on the whole request.
  • Gemini rates follow the official Google standard paid tier.
  • The live enabled set is always available at GET /v1/models on the unified endpoint — one aggregated catalog for all providers.
06

Gemini Batch API

Run many independent Gemini requests asynchronously, poll one durable operation, and retrieve every generated response or per-item error. The same native API works through the unified router and direct Gemini endpoint.

Use the recommended https://router.apitoken.sale or the direct https://gemini.api.apitoken.sale. Only the hostname changes: paths, bodies and x-goog-api-key authentication are identical. Keep the key server-side.

Endpoints

MethodPathPurpose
POST/v1beta/models/{model}:batchGenerateContentCreate an asynchronous operation
GET/v1beta/batches/{id}Poll state and read item results
GET/v1beta/batches?pageSize={n}&pageToken={token}List this account’s operations
POST/v1beta/batches/{id}:cancelRequest cancellation; already dispatched items may finish
DELETE/v1beta/batches/{id}Delete a terminal operation
POST/upload/v1beta/filesStart or continue a resumable JSONL upload
GET/v1beta/filesList account-scoped gateway files
GET/v1beta/files/{id}Read file metadata
GET/v1beta/files/{id}:downloadStream or range-download an active file
DELETE/v1beta/files/{id}Delete a file not referenced by live work

1. Create an inline Batch

Save the returned name (batches/batch-…). Idempotency-Key is optional but recommended: an exact replay returns the same operation; the same key with different content returns 409. Without it, every POST creates a new job.

Bash · curl
export APITOKEN_API_KEY="sk-pool-…"export GEMINI_BASE="https://router.apitoken.sale"# Direct alternative:# export GEMINI_BASE="https://gemini.api.apitoken.sale"OPERATION=$(curl -fsS   "$GEMINI_BASE/v1beta/models/gemini-3.6-flash:batchGenerateContent"   -H "x-goog-api-key: $APITOKEN_API_KEY"   -H "content-type: application/json"   -H "Idempotency-Key: product-summary-2026-09-01"   -d '{    "batch": {      "displayName": "Product summaries",      "inputConfig": {        "requests": {          "requests": [            {"request":{"contents":[{"role":"user","parts":[{"text":"Summarize product A."}]}]},"metadata":{"key":"product-a"}},            {"request":{"contents":[{"role":"user","parts":[{"text":"Summarize product B."}]}]},"metadata":{"key":"product-b"}}          ]        }      }    }  }')BATCH_NAME=$(printf '%s' "$OPERATION" | jq -r .name)echo "$BATCH_NAME"

2. Poll and read every item

Poll with a delay until done is true. Then inspect the job state, string-valued batchStats, and every inlinedResponses entry. Each entry contains either response or error; a terminal job may include item-level failures.

Bash · poll
while :; do  OPERATION=$(curl -fsS     "$GEMINI_BASE/v1beta/$BATCH_NAME"     -H "x-goog-api-key: $APITOKEN_API_KEY")  [ "$(printf '%s' "$OPERATION" | jq -r .done)" = "true" ] && break  sleep 5done# Official nested inline results: each entry contains either .response or .error.printf '%s' "$OPERATION" | jq '.response.inlinedResponses.inlinedResponses[]'

3. Parse the terminal operation

Treat counters as decimal strings and parse them with BigInt. Do not treat done=true alone as proof that every item succeeded.

TypeScript
const operation = await response.json();if (!operation.done) {  console.log("Still running:", operation.metadata?.state);  return;}if (operation.error) throw new Error(operation.error.message);const stats = operation.metadata?.batchStats ?? {};const total = BigInt(stats.requestCount ?? "0");const failed = BigInt(stats.failedRequestCount ?? "0");for (const [index, item] of     (operation.response?.inlinedResponses?.inlinedResponses ?? []).entries()) {  if (item.error) {    console.error(index, item.error.status, item.error.message);  } else {    console.log(index, item.response?.candidates ?? []);  }}

4. List, cancel and delete resources

Use the complete batches/{id} name returned by create. Cancellation is best effort for already dispatched items; delete a Batch only after terminal state. Files are account-scoped and cannot be deleted while referenced by live work.

Bash · lifecycle
# List Batches (save nextPageToken for the next page).curl -fsS "$GEMINI_BASE/v1beta/batches?pageSize=20"   -H "x-goog-api-key: $APITOKEN_API_KEY"# Read one operation. BATCH_NAME is the complete batches/{id} from create.curl -fsS "$GEMINI_BASE/v1beta/$BATCH_NAME"   -H "x-goog-api-key: $APITOKEN_API_KEY"# Request cancellation. Items already dispatched may still finish.curl -fsS -X POST "$GEMINI_BASE/v1beta/$BATCH_NAME:cancel"   -H "x-goog-api-key: $APITOKEN_API_KEY"   -H "content-type: application/json" -d '{}'# Delete only after the operation is terminal.curl -fsS -X DELETE "$GEMINI_BASE/v1beta/$BATCH_NAME"   -H "x-goog-api-key: $APITOKEN_API_KEY"# Files list supports pageSize, but currently has no pageToken.curl -fsS "$GEMINI_BASE/v1beta/files?pageSize=20"   -H "x-goog-api-key: $APITOKEN_API_KEY"FILE_ID="file-…" # ID without the files/ prefixcurl -fsS "$GEMINI_BASE/v1beta/files/$FILE_ID"   -H "x-goog-api-key: $APITOKEN_API_KEY"# Public download supports active files up to 20 MiB.curl -fsS "$GEMINI_BASE/v1beta/files/$FILE_ID:download"   -H "x-goog-api-key: $APITOKEN_API_KEY" -o downloaded.bin# Deletion fails while a live Batch references the file.curl -fsS -X DELETE "$GEMINI_BASE/v1beta/files/$FILE_ID"   -H "x-goog-api-key: $APITOKEN_API_KEY"

Large input with JSONL

A nonempty line must be one JSON object with a unique correlation key and a request object. Do not wrap lines in an array. Blank lines and CRLF are accepted. Upload the file to this gateway, then pass the returned files/{id} as inputConfig.fileName.

JSONL input format

key is required for file input and may be up to 512 bytes. Results remain in input order; keep the key in your own input-to-output mapping.

JSONL
{"key":"product-a","request":{"contents":[{"role":"user","parts":[{"text":"Summarize product A."}]}]}}{"key":"product-b","request":{"contents":[{"role":"user","parts":[{"text":"Summarize product B."}]}]}}

Resumable upload and fileName create

The returned upload URL is relative. Non-final upload chunks are exactly 8 MiB and use exact offsets; after an ambiguous response issue command=query before retrying. Full output downloads stream without buffering, and Range supports resumable bounded reads.

Bash · upload
FILE=batch-input.jsonlSIZE=$(wc -c < "$FILE" | tr -d ' ')# 1. Start a resumable upload.curl -fsS -D upload.headers -o /dev/null   -X POST "$GEMINI_BASE/upload/v1beta/files"   -H "x-goog-api-key: $APITOKEN_API_KEY"   -H "x-goog-upload-protocol: resumable"   -H "x-goog-upload-command: start"   -H "x-goog-upload-file-name: batch-input.jsonl"   -H "x-goog-upload-header-content-type: application/jsonl"   -H "x-goog-upload-header-content-length: $SIZE"UPLOAD_PATH=$(awk 'BEGIN{IGNORECASE=1} /^x-goog-upload-url:/{gsub("\r",""); print $2}' upload.headers)# 2. Upload and finalize this file in one chunk (up to 8 MiB).# For larger files, repeat command=upload with exact 8 MiB chunks and# increasing x-goog-upload-offset, then use "upload, finalize" on the last chunk.curl -fsS "$GEMINI_BASE$UPLOAD_PATH"   -X POST   -H "x-goog-api-key: $APITOKEN_API_KEY"   -H "x-goog-upload-protocol: resumable"   -H "x-goog-upload-command: upload, finalize"   -H "x-goog-upload-offset: 0"   -H "content-length: $SIZE"   --data-binary "@$FILE" > uploaded-file.jsonFILE_NAME=$(jq -r .file.name uploaded-file.json)# 3. Use the returned files/{id} as Batch JSONL input.curl -fsS   "$GEMINI_BASE/v1beta/models/gemini-3.6-flash:batchGenerateContent"   -H "x-goog-api-key: $APITOKEN_API_KEY"   -H "content-type: application/json"   -d "{"batch":{"displayName":"JSONL summaries","inputConfig":{"fileName":"$FILE_NAME"}}}"

Limits and retention

LimitValue
Inline create body20 MiB
Items per BatchNo product ceiling. One client job stays one job.
Nonterminal Batches per account100
JSONL line20 MiB
Upload chunk8 MiB
Uploaded file TTL48 hours
Queue deadline48 hours
Terminal result retention42 days
List page1–1,000

The account must have a positive prepaid balance. Accepted work is durable and may remain queued during temporary capacity pressure. Ultra subscriptions use up to 20 durable Batch slots; every other plan uses 2, with a random durable 2–5 second interval between starts on one subscription. Standard Gemini token pricing and your normal account discount apply; there is no separate Batch discount or completion-time SLA.

How this differs from Google Batch

AreaapiToken.sale behavior
ExecutionAsynchronous non-streaming gateway jobs; not Vertex AI Batch, GCS or BigQuery.
PricingNormal Gemini tariff and your account discount. No separate Batch discount or completion SLA.
Inline schemaOfficial InlinedRequests wrapping (requests.requests[]) and a raw array are both accepted. Proto-JSON snake_case aliases are accepted.
FilesFiles belong to your apiToken.sale account, not to a Google Cloud project. Foreign Google files are invisible.
fileDataSynchronous fileData is 400 INVALID_ARGUMENT / FILE_URI_UNSUPPORTED: the official Gemini API accepts it; send inlineData instead. Batch JSONL uses inputConfig.fileName.
File outputFile-input jobs publish an ordered encrypted JSONL responsesFile before done=true; inline jobs retain inlinedResponses.
TimestampsOperation timestamps are RFC 3339 UTC (google-datetime).
UnsupportedNo webhooks, embedding/update Batch methods, image-output models, Google IAM or stock Vertex resource semantics.

Batch troubleshooting

StatusWhat to do
400 INVALID_ARGUMENTFix malformed JSON, both/neither input forms, invalid JSONL, unsupported model or field. Do not retry unchanged.
400 INVALID_ARGUMENT / API_KEY_INVALIDMissing, invalid or revoked key: the Gemini lane answers 400 with `error.details` reason API_KEY_INVALID, mirroring the official Google API (not 401). Send an active sk-pool key in x-goog-api-key and never retry a revoked key.
402 FAILED_PRECONDITIONPaid Batch create requires a positive prepaid balance. HTTP 402 FAILED_PRECONDITION is engine-account money, not RESOURCE_EXHAUSTED and not 429.
404 NOT_FOUNDWrong, deleted, expired or foreign-account job/file; verify the full resource name and account.
409 ABORTEDIdempotency key reused with changed content, or upload offset mismatch.
FAILED_PRECONDITIONWait/cancel before deleting a live Batch; a live Batch reference also blocks file deletion.
429 / 503Temporary quota, capacity or authority condition. Poll/retry with capped exponential backoff and jitter.
07

Sending files to Gemini

Synchronous generateContent requires inline bytes: files/… from any Google project is invisible to this gateway. For large Gemini Batch input, upload an account-scoped JSONL file to this gateway and pass its returned name as inputConfig.fileName. Uploaded files expire after 48 hours.

InputSupportedHow to send it
Images (PNG, JPEG, WebP)Yesinline_data with mime_type and base64. Up to ~23 MB of source file.
PDF documentsYesinline_data with application/pdf. The model reads the text inside.
Text filesYesinline_data with text/plain, or simply paste into the text part.
AudioAll text modelsAny text model, as inline audio/wav.
Files API (file_uri / fileData)One modelBatch JSONL input only: upload to this gateway and pass the returned files/{id} as inputConfig.fileName. fileData embedding is not supported. For synchronous generateContent, inline the bytes.
cachedContentNoNot available. Send the content inline.

Inline request

Replace the upload plus reference with a single request. Base64 adds about a third to the size, so the request body stays under our 32 MB limit for a source file up to roughly 23 MB.

JSON · Request
{  "contents": [    {      "role": "user",      "parts": [        { "text": "Describe this file" },        { "inline_data": { "mime_type": "image/png", "data": "<base64>" } }      ]    }  ]}

When we refuse. An input this gateway cannot accept is rejected before dispatch, as 400 INVALID_ARGUMENT with a stable ErrorInfo reason — FILE_URI_UNSUPPORTED, CACHED_CONTENT_UNSUPPORTED, AUDIO_INPUT_UNSUPPORTED. The message follows {field} cannot be honoured on this endpoint. The official Gemini API accepts it; {workaround}. FILE_URI_UNSUPPORTED applies to synchronous generateContent and foreign Google files; Batch may resolve only files uploaded to this gateway under the same account. Those refusals are final: changing the request is the fix, retrying is not. Every error also carries an x-request-id header — quote it to support and we can find your exact request.

08

Common response codes

On the unified endpoint every protocol keeps its own error envelope: Anthropic lanes (including the KIMI lane on kimi/*) return Anthropic's JSON (gateway limitations add `error.details.error_code` / `param`), OpenAI lanes return {"error":{"message","type","param","code"}}, and the Gemini lane returns {"error":{"code","message","status","details"}}. Treat 401 and 402 as account-state failures; retry only transient 429 and 5xx. The full exact-text catalog is /docs/errors.

StatusMeaningWhat to do
400Malformed request, unsupported_parameter, or documented_limitationFix the body. If error.code or error.details.error_code is documented_limitation or unsupported_parameter, omit or relocate the named field — the official API of that protocol accepts it; this endpoint does not. Do not retry unchanged.
401API key is missing, invalid, or revokedSend an active sk-pool key in x-api-key. If it was revoked, create a replacement; do not retry the same key. On the native Gemini lane the same failure arrives as 400 INVALID_ARGUMENT with `error.details` reason `API_KEY_INVALID` — mirroring the official Google API; treat it exactly like a 401.
402Available prepaid balance is too lowTop up the account, confirm the balance is available, then retry. Backoff alone will not resolve a 402. Anthropic lanes: 402 billing_error. OpenAI lanes: 402 insufficient_quota (not 429 — SDKs retry 429). Gemini native: 402 FAILED_PRECONDITION.
429Rate limit or temporary upstream capacity limitHonor Retry-After when present; retry with capped exponential backoff and jitter.
5xxTemporary gateway or provider upstream failureRetry with bounded exponential backoff. Keep the request ID and avoid unbounded duplicate attempts.

Full error catalog with exact response text →

09

Prompt caching

Claude requests get a 5-minute prompt cache automatically. You can set an explicit breakpoint or select the 1-hour TTL; every later read bills at 10% of the input price.

Claude — native Messages

On /v1/messages, a non-null cache_control breakpoint passes through unchanged. If the field is absent or null everywhere, we insert a top-level 5-minute cache automatically. For 1 hour, add ttl: "1h" to the breakpoint and send anthropic-beta: extended-cache-ttl-2025-04-11.

JSON · Request
{  "model": "claude-opus-4-8",  "max_tokens": 1024,  "system": [    {      "type": "text",      "text": "Long stable instructions…",      "cache_control": { "type": "ephemeral" }    }  ],  "messages": [{ "role": "user", "content": "Short varying question" }]}

Claude — OpenAI-compatible Chat

For an anthropic/* model on /v1/chat/completions, send cache_control as a top-level custom extension. If it is absent or null, the translated Claude request gets the automatic 5-minute cache. For 1 hour, send the same beta header. In an OpenAI SDK, put the extension in extra_body and the beta in extra_headers. Cache hits appear in usage.prompt_tokens_details.cached_tokens.

JSON · Request
{  "model": "anthropic/claude-opus-4-8",  "messages": [    { "role": "system", "content": "Long stable instructions…" },    { "role": "user", "content": "Short varying question" }  ],  "max_completion_tokens": 1024,  "cache_control": { "type": "ephemeral", "ttl": "1h" }}

Claude — OpenAI-compatible Responses

For an anthropic/* model on /v1/responses, use the same top-level custom extension and beta header. Omission gives the same automatic 5-minute cache. In an OpenAI SDK, use extra_body and extra_headers. Cache hits appear in usage.input_tokens_details.cached_tokens.

JSON · Request
{  "model": "anthropic/claude-opus-4-8",  "instructions": "Long stable instructions…",  "input": "Short varying question",  "max_output_tokens": 1024,  "cache_control": { "type": "ephemeral", "ttl": "1h" }}

GPT — automatic

No opt-in: repeated prefixes are cached server-side. usage.prompt_tokens_details.cached_tokens (Chat Completions) or input_tokens_details.cached_tokens (Responses) bills at 10% of input automatically.

JSON · Response
{  "usage": {    "prompt_tokens": 5120,    "prompt_tokens_details": { "cached_tokens": 4096 },    "completion_tokens": 128  }}
10

Next steps

Everything referenced from this page, in one place.