Quotes and protocol compatibility
Get the exact price of a request before paying, submit it at that price, recover from a lost response, and know how the text and video compatibility APIs are priced.
Quote an exact request
A quote tells you the price of one exact request before you pay. It is free, creates no task and reserves no funds. You then submit the same request with the quote attached, so it is accepted only at the price you were shown.
The commands below are for a macOS or Linux terminal, with SPICY_API_KEY set as in the Quickstart. MODEL_ID_FROM_CATALOG is a placeholder: use a model value from your live catalog (GET /api/v1/models), and take every input field from that model's schema.
Save the request you intend to send
Create a file named request.json with exactly the model, input and optional callBackUrl you will submit:
{
"model": "MODEL_ID_FROM_CATALOG",
"input": { "prompt": "A cinematic night portrait" }
}Ask for the price
curl https://api.spicyapi.ai/api/v1/jobs/quote \
-H "Authorization: Bearer $SPICY_API_KEY" \
-H "Content-Type: application/json" \
--data-binary @request.json{
"code": 200,
"msg": "success",
"data": {
"quoteId": "QUOTE_ID_FROM_RESPONSE",
"model": "MODEL_ID_FROM_CATALOG",
"estimatedCost": "0.008",
"maxCharge": "0.008",
"currency": "USD",
"quantity": "1",
"unit": "per_image",
"expiresAt": "2026-09-17T10:05:00Z"
},
"request_id": "req_01k3m8x9q2z4v7n5p6r8s0t1w2"
}Amounts are decimal strings in US dollars. maxCharge is the most this request can cost; today it always equals estimatedCost. quantity and unit show what is being counted, for example seconds of video. Show the amount and expiresAt to whoever approves the spend.
A quote runs the same checks as a real submission, so it can already fail: 400 (the input does not match the model's schema), 40201 (not enough balance), 40202 (a spend cap on the key or the platform quota) or 403 (this key may not use the model).
Submit the same request with the quote attached
Within five minutes, send the unchanged request to createTask with two more fields: quoteId from the quote, and expectedCost set to the quoted estimatedCost. This step costs money.
{
"model": "MODEL_ID_FROM_CATALOG",
"input": { "prompt": "A cinematic night portrait" },
"quoteId": "QUOTE_ID_FROM_RESPONSE",
"expectedCost": "0.008"
}SPICY_IDEMPOTENCY_KEY="$(uuidgen)" # once per new task; reuse it only to resend this same request
curl https://api.spicyapi.ai/api/v1/jobs/createTask \
-H "Authorization: Bearer $SPICY_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $SPICY_IDEMPOTENCY_KEY" \
--data-binary @submit.jsonThe response is the normal 202 Accepted task response described in the Quickstart. The quote does not replace the Idempotency-Key: send both.
| The quote is tied to | It stops working when |
|---|---|
| Your account and the API key that requested it | You submit with a different key |
The exact model, input and callBackUrl | Any of them changes, even one character of the prompt. Field order and spacing do not matter |
| The price at that moment | Five minutes pass, or the price changes |
40901 means the quote no longer describes the request
Changed inputs, another API key, an expired quote, a changed price or an expectedCost that differs from the quote all surface the same way: acceptance fails with 40901, and nothing is charged. Quote and confirm again — do not remove quoteId and expectedCost to push the request through unpriced.
POST /api/v1/jobs/stream, the native text stream, accepts a quote the same way; see Text and streaming.
Recovering from a lost response
If a submission timed out or the connection dropped, you do not know whether it was accepted. For createTask, retry and POST /v1/videos:
- Resend the original request with its original
Idempotency-Key, using the same API key. You get the original task back, in its current state, and are not charged twice. - Do not create another task with a fresh key. A new key means a new generation and a new charge.
- A replay of an already accepted request stays valid after the quote expires. Expiry gates acceptance, not the task acceptance created.
- A
409does not mean "not created". Either the request changed (a new piece of work that needs a new key), or it was sent with a different API key from the same account (resend with the original key). See Idempotency. - Read
costandsettledfromrecordInfofor the final charge.
Text and streaming requests cannot be recovered this way
The answer to a text request (/api/v1/jobs/stream, and the text endpoints under /v1 and /v1beta) is sent to you as it is generated and is not stored. Resending an already accepted key returns an error instead of the answer: 400 with the same body, 409 with a different one. Nothing more is charged, but the lost reply cannot be fetched. To get an answer again, send the request with a new key and pay for it. See Which endpoints read the header.
An estimate is not a settlement
estimatedCost is what we expect to charge. An end-of-text marker on a stream only says the bytes stopped arriving. Neither one proves the charge is final. For tasks, settled: true alongside cost in recordInfo does. Text calls name their task in the X-Spicy-Task-Id response header: read it with recordInfo, find it in GET /api/v1/jobs (whose items also carry the call's requestId) or under Console → Logs, and treat cost as final once settled is true.
Quotes in the official tools
The CLI and the MCP server quote and ask a person before every billable call, so you do not have to build this step yourself:
- CLI:
spicyapi tasks quoteprices a request;spicyapi tasks createquotes again, prints the amount and asks before creating. See Costs, confirmation and safety. - MCP: the assistant can quote freely, but creating a task asks you through your AI client, with the price. See Costs, confirmation and safety.
- SDK:
quoteTaskreturns the quote; pass itsquoteIdandestimatedCosttocreateTaskorrun.
An agent must not confirm on the user's behalf
The confirmation exists so a person decides. An agent that answers it itself, or a client set to auto-approve, has removed the only spending boundary on that path.
Text and video compatibility
SpicyAPI also speaks the OpenAI, Anthropic and Google Gemini formats for text models, and the OpenAI Videos format for video. The full endpoint list is in the API reference; per-protocol requests, streams and errors are in Text and streaming.
Base URL. The OpenAI format uses https://api.spicyapi.ai/v1. The Anthropic and Google GenAI SDKs append /v1 or /v1beta themselves, so set their base URL to https://api.spicyapi.ai.
Credential. One API key for all of them, in the Authorization: Bearer header. The x-api-key header used by the Anthropic SDK and the x-goog-api-key header used by the Google GenAI SDK are accepted too. A ?key= in the URL never is.
Model. Select an exact model from the live catalog. Whether tools, image messages and generation options work depends on that model's schema, not on the protocol you chose.
Response shape. Each protocol returns its own envelope, not the platform {code, msg, data, request_id} one.
Reasoning fields
Chat models that expose reasoning text may return an optional string in message.reasoning_content, or delta.reasoning_content when streaming. When continuing after a tool call, you can preserve it in the corresponding assistant message where the live model schema supports it.
usage.completion_tokens_details.reasoning_tokens describes the reasoning portion already included in completion_tokens; do not add it again. Not every model returns reasoning text or these token details.
Two shapes worth knowing before you build
- Responses requires the full conversation in
input.previous_response_idis unsupported. - Videos accepts JSON with
model,prompt,input_reference,secondsandsize, which are mapped to the model's own fields. The returned videoidis also a task ID thatrecordInfoaccepts. Its content endpoint redirects with HTTP302to a short-lived signed URL.
The compatibility APIs price at acceptance
quoteId and expectedCost apply to jobs/createTask and jobs/stream only. /v1 and /v1beta use the price in force when the request is accepted and reject those two fields, so there is no per-call price confirmation on that path. When a workflow must confirm a charge limit, use the native task API; to bound everything a key can spend, set its daily, monthly and lifetime caps under Authentication.
For a streaming chat example with the official OpenAI, Anthropic and Google clients, see Use an official SDK. The CLI and MCP server submit and track native tasks; they do not stream chat tokens.

