APIMODELSAPIMODELS
Documentação da API

apimodels.app

Grátis · apenas chave API

Per-request billing: request IDs and billed amounts

Every call has a request ID and one final billed amount. LLM requests return the ID in the x-apimodels-request-id response header and the amount in the response body under usage.cost (on streams, in the final chunk that carries usage); non-streaming requests also carry x-apimodels-cost. For image, video and audio tasks the ID is the task_id returned at creation, and the creation response tells you how much was put on hold. GET /v1/records/{id} with any of these IDs returns exactly what that request took from your balance. Built for customers who resell us with our written permission (Terms, section 6; write to business@apimodels.app) or need to reconcile each of their own charges against ours.

Resumo rápido

Endpoint
GET /api/v1/records/{task_id}
Autenticação
Authorization: Bearer <API_KEY>
Preços
Free (the lookup is never charged)
Response headers
x-apimodels-request-id · x-apimodels-cost
Response body
usage.cost · reservedCredits
Covers
Every LLM, image, video and audio call
Moeda
USD

Where the request ID comes from

Every successful response from the LLM endpoints (/v1/chat/completions, /v1/messages, the native Gemini /v1beta, /v1/responses and /v1/completions) carries the x-apimodels-request-id header; its value is the task_id of that request. Non-streaming responses also carry x-apimodels-cost: the amount actually deducted, in USD, after every discount on your account, written only once the deduction has completed. Streaming responses carry the ID only, because the amount is known when the last chunk settles; look the record up with the ID.

HTTP/2 200
content-type: application/json
x-apimodels-request-id: cmumilhfd0013mg012q3qobr9
x-apimodels-cost: 0.0001
access-control-expose-headers: x-apimodels-request-id, x-apimodels-cost

usage.cost in the response body

OpenAI and Anthropic SDKs usually hide response headers, so the same amount is also written into the response body as usage.cost. In a non-streaming response usage.cost.charged is the amount deducted, the same number as x-apimodels-cost.

{
  "id": "chatcmpl-…",
  "choices": [ … ],
  "usage": {
    "prompt_tokens": 165,
    "completion_tokens": 6,
    "cost": { "currency": "USD", "amount": 0.0001, "discount": 1, "charged": 0.0001 }
  }
}

Streaming responses put usage.cost in the final chunk that carries usage: the usage chunk you get with stream_options.include_usage in the OpenAI format, message_delta in the Anthropic format (/v1/messages), and response.usage inside response.completed on /v1/responses. That chunk goes out before the deduction is written, so charged is the amount due under the same formula; only a request whose balance ran short ends up deducted for less, and the record lookup is authoritative. message_start carries input usage only and has no cost.

# OpenAI 格式(stream_options.include_usage = true):最后的用量帧
data: {"object":"chat.completion.chunk","choices":[],"usage":{"prompt_tokens":165,"completion_tokens":6,"cost":{"currency":"USD","amount":0.0001,"discount":1,"charged":0.0001}}}

data: [DONE]

# Anthropic 格式(/v1/messages):message_delta
event: message_delta
data: {"type":"message_delta","delta":{"stop_reason":"end_turn"},"usage":{"input_tokens":165,"output_tokens":6,"cost":{"currency":"USD","amount":0.0001,"discount":1,"charged":0.0001}}}
FieldRequiredTypeDescription
usage.cost.currency—stringAlways "USD"
usage.cost.amount—numberWhat this request costs at list price, before any discount
usage.cost.discount—numberYour account discount as a multiplier: 1 = none, 0.95 = 5% off
usage.cost.charged—numberThe charge after the discount. Non-streaming: the amount deducted; streaming: the amount due (see above)

usage.cost is returned by /v1/chat/completions, /v1/messages, /v1/responses and /v1/completions; native Gemini /v1beta responses do not include it, so use the record lookup there.

One exception: when a non-streaming LLM request runs past about 95 seconds, the gateway sends the response headers early to keep the connection alive, so the two headers cannot carry values; the same two values are then added to the response body under an apimodels field. The OpenAI and Anthropic SDKs ignore the unknown field.

{
  "id": "chatcmpl-…",
  "choices": [ … ],
  "usage": { … },
  "apimodels": { "request_id": "cmumilhfd0013mg012q3qobr9", "cost": 0.0001 }
}

For image, video and audio tasks the ID is the data.taskId returned at creation. The creation response also carries reservedCredits: the amount put on hold from your balance when the task was accepted, which is the most this task can cost. On completion it settles at the actual amount and the rest is released; a failed task releases all of it.

{
  "code": 200,
  "msg": "success",
  "data": {
    "taskId": "cmum4dq3z00dwlm01wugboo5z",
    "state": "pending",
    "model": "seedance-2.0",
    "reservedCredits": 0.46,
    "currency": "USD"
  }
}

Once the task finishes, both the GET ?task_id= result and the callback (callback_url) carry credits (the final amount deducted) and currency in data, equal to what the record lookup returns.

{
  "code": 200,
  "msg": "success",
  "data": {
    "taskId": "cmum4dq3z00dwlm01wugboo5z",
    "state": "completed",
    "credits": 0.338,
    "currency": "USD",
    "resultUrls": ["https://r2.apimodels.app/videos/cmum4dq3z00dwlm01wugboo5z.mp4"]
  }
}

When calling from a browser, both headers are exposed through Access-Control-Expose-Headers, so response.headers.get() can read them.

Look up a record

GET /api/v1/records/{task_id}
Authorization: Bearer <API_KEY>

Example: call, then reconcile that one request

cURL

curl https://api.apimodels.app/v1/records/cmumilhfd0013mg012q3qobr9 \
  -H "Authorization: Bearer $API_KEY"

Python

import requests, os

# 1) make the call and keep the request id from the response headers
r = requests.post(
    "https://api.apimodels.app/v1/chat/completions",
    headers={"Authorization": f"Bearer {os.environ['API_KEY']}"},
    json={"model": "claude-haiku-5-5", "messages": [{"role": "user", "content": "Hi"}]},
)
request_id = r.headers["x-apimodels-request-id"]
print("charged now:", r.headers.get("x-apimodels-cost"))   # non-streaming only

# 2) any time later: the authoritative billed amount for that one request
rec = requests.get(
    f"https://api.apimodels.app/v1/records/{request_id}",
    headers={"Authorization": f"Bearer {os.environ['API_KEY']}"},
).json()["data"]
print(rec["settled"], rec["credits"], rec["currency"])

Node.js

const headers = { Authorization: "Bearer " + process.env.API_KEY };

// 1) make the call and keep the request id from the response headers
const r = await fetch("https://api.apimodels.app/v1/chat/completions", {
  method: "POST",
  headers: { ...headers, "Content-Type": "application/json" },
  body: JSON.stringify({ model: "claude-haiku-5-5", messages: [{ role: "user", content: "Hi" }] }),
});
const requestId = r.headers.get("x-apimodels-request-id");
console.log("charged now:", r.headers.get("x-apimodels-cost")); // non-streaming only

// 2) any time later: the authoritative billed amount for that one request
const rec = await fetch("https://api.apimodels.app/v1/records/" + requestId, { headers });
const { data } = await rec.json();
console.log(data.settled, data.credits, data.currency);

Resposta

{
  "code": 200,
  "msg": "success",
  "data": {
    "task_id": "cmumilhfd0013mg012q3qobr9",
    "model": "claude-haiku-5-5",
    "type": "LANGUAGE_MODEL",
    "state": "completed",
    "settled": true,
    "credits": 0.0001,
    "currency": "USD",
    "usage": {
      "input_tokens": 165,
      "output_tokens": 6,
      "cached_input_tokens": 149,
      "cache_creation_tokens": 0
    },
    "created_at": 1790676571513,
    "completed_at": 1790676572799
  }
}
FieldRequiredTypeDescription
data.task_id—stringThe request ID, identical to the header value or the taskId returned at creation
data.model—stringPublic model name, e.g. claude-haiku-5-5
data.type—stringLANGUAGE_MODEL / TEXT_TO_IMAGE / IMAGE_TO_VIDEO / … as on the call record
data.state—stringpending (still running) / completed / failed
data.settled—booleantrue once the amount is final; while false, credits is null — poll again later
data.credits—number | nullUSD actually taken from the balance, after account discounts and per-model prices; 0 for failed requests
data.currency—stringAlways "USD"
data.usage—objectLLM only: input_tokens, output_tokens, cached_input_tokens, cache_creation_tokens; reasoning tokens are inside output_tokens
data.created_at—numberCreation time, Unix milliseconds
data.completed_at—number | nullCompletion time, Unix milliseconds; null until done

What the amount means

  • credits is what finally left the balance, not list price times usage: account discounts and negotiated per-model prices are already applied.
  • Failed requests are always 0. Upstream errors, requests rejected by our validation, and timeouts are not charged.
  • Streaming LLM calls settle on the last chunk; video models billed by output length settle once the clip exists. Until then settled is false.
  • x-apimodels-cost, usage.cost.charged in a non-streaming response and the record’s credits are the same number; the record is the source of truth, the header and body just save you a lookup.
  • usage.cost.charged is roughly amount × discount. For LLM calls both amount and charged are rounded to 4 decimal places with a $0.0001 minimum per request; on accounts with negotiated per-model prices, charged is the number to use.
  • reservedCredits is a ceiling, not the bill: while held, that part of your balance cannot be used by other requests, and whatever is not charged returns to your available balance at settlement.
  • Amounts are kept to 6 decimal places (one millionth of a dollar).

Erros

  • 401 — Chave API ausente ou inválida
  • 404 — Record not found. A request ID that belongs to another account also returns 404; its existence is never confirmed
  • 500 — Erro interno do servidor

Perguntas frequentes

How do I find out what one API call cost me?

Read x-apimodels-request-id from the response headers (LLM) or keep the task_id returned at creation (image, video, audio), then call GET https://api.apimodels.app/v1/records/{id} with your API key. data.credits is the USD amount actually deducted after all discounts; failed requests are 0. Non-streaming LLM responses also carry the amount directly in x-apimodels-cost.

Why is credits null and settled false?

The request has not finished settling. Streaming LLM calls settle on the last chunk, and some video models settle on the actual output length. Poll the record again after completion; once settled is true the amount is final and will not change.

Do streaming responses include the cost header?

No. Streaming responses carry only x-apimodels-request-id, because the amount is known when the stream ends. Query GET /v1/records/{id} after the final chunk. Non-streaming responses carry both headers; a non-streaming LLM request that runs past about 95 seconds gets the same two values in an apimodels field in the response body instead, because the headers were already sent to keep the connection alive.

Can I look up another account's request?

No. A request ID that belongs to a different account returns 404, exactly like a missing one; the API never confirms whether such an ID exists. The lookup itself is free and is not counted as a call.