spicyapiDocs
Main content

Troubleshooting and recovery

Find your symptom, see why it happens and what to do — payments, 401 and 403, balance and limits, unavailable models, failed or stuck tasks, uploads, callbacks and streams.

Each section below starts from what you see, explains the usual cause and tells you what to do. For the full meaning of every error number, see Errors; this page is the "what now?" companion.

Find your problem

What you seeGo to
I paid but my balance didn't changePaid, but the balance didn't change
Every call returns 401 or 403Every call returns 401 or 403
40201 or 40202, or 429Balance, limits and support
A model is missing, or you get 50301 or 40004Model missing or unavailable
400 or 40901 when creating a taskInvalid input or quote conflict
The request timed out and you have no task IDSubmission timed out without a task ID
The task ended as failedThe task failed
The task stays queued or running for a long timeTask stays queued or running
An upload worked, but the task rejects itUpload succeeded but input is rejected
No callback arrived, or the result link or file is goneMissing callback or expired download
A text stream stops before the endStream ends early
In a team, you can't create a key, see prompts or accept an invitationTeam permissions
You can't issue an invoiceCan't issue an invoice
A response isn't JSON at allKeep useful evidence

Keep useful evidence

Before changing anything, write down what happened. It makes every step below faster, and it is what support needs:

  • The time, in UTC if you can.
  • The endpoint you called, for example POST /api/v1/jobs/createTask.
  • The HTTP status and the code from the response body.
  • The request_id: it is in the response body and in the X-Request-Id response header.
  • The task ID, if you got one. It starts with job_ and is also listed on the console Logs page.

Keep three situations apart, because they call for different actions: the API explicitly refused the request (you got a JSON error — nothing was created or charged), you got no response at all (a timeout or dropped connection — the task may exist; see below), or the task was accepted and failed later (you have a task ID with state: "failed").

If a response isn't JSON — for example an HTML or plain-text page — it came from our edge network before reaching the API. Note the HTTP status and the first lines of the body. A 403 page of this kind usually means the request comes from a region we can't serve; retrying won't change it.

Never put API keys, passwords, full prompts or original files into general logs or support requests.

Paid, but the balance didn't change

  • Card: the balance changes only after the payment processor's confirmation reaches us, usually within seconds. Refresh Billing and check Top-up orders.
  • Crypto: the balance is added after the network confirms the transaction, usually a few minutes. Paying less than the amount shown, or after the payment page expired, is not added automatically.
  • Manual transfer: open the order under Top-up orders and submit your bank reference or transaction hash. We check it by hand, usually within a few hours.

The full list of cases is in Account setup → Common problems. Still missing? Open a Support conversation (topic Billing) with the order ID and, for crypto, the transaction hash. Never send card numbers or passwords.

Every call returns 401 or 403

Start with a call that is free and changes nothing — reading your balance:

echo "${SPICY_API_KEY:0:9}"
curl https://api.spicyapi.ai/api/v1/chat/credit \
  -H "Authorization: Bearer $SPICY_API_KEY"

The first line should print sk-spicy-. If it prints nothing, the key isn't loaded in this terminal — set it again (see Account setup). Then read the msg of the response:

You seeCauseWhat to do
401 Missing credentials: …No key reached usThe Authorization header is missing. Check that the environment variable is set in the process that runs your code, and that a proxy or HTTP client isn't dropping the header
401 API key format is not recognized: …The value isn't a SpicyAPI keyYou pasted something else — a console sign-in value or another service's key. Copy a key that starts with sk-spicy-
401 Credentials are invalid or expiredThe key doesn't exist, was revoked or passed its expiry dateLook at the key on the API keys page. An expired key gives the same answer as a revoked one. Create a replacement if needed
40310The key is fine, but the account's email address isn't verified yetOpen the verification link we emailed you, or send a new one from the console banner, then retry. No new key is needed. See Errors
403 This account cannot call the API right nowThe key is fine, but the account is restrictedContact support with the request_id
40301This key isn't allowed to call this modelEdit the key and add the model to its allowed models, or use another key
40302Your server's address isn't on the key's IP allowlistAdd the address to the key, or remove the IP restriction
40303The service isn't available in your regionThis can't be worked around. See where we operate

None of these improve with retries.

Balance, limits and support

  • 40201 — the available balance doesn't cover the task's estimated price. Add funds (see Account setup), then send the same request again with the same Idempotency-Key. A promotional grant only pays for the models it covers.
  • 40202 — a spend limit was reached; msg says which. A key cap (This key has reached its …) is raised under API keys → Edit, or resets on its own (daily at 00:00 UTC, monthly on the 1st, lifetime never). A team limit (Workspace … budget has been reached) is raised on the Team page by an owner, admin or budget manager. The platform cap (Platform quota for today is exhausted…) can't be raised; retry after 00:00 UTC. See Spend caps.
  • 429 — too many requests. Wait for the number of seconds in Retry-After and slow down; polling with webhooks instead of tight loops helps most. If the message is Too many pending uploads…, waiting won't help: finish the uploads you already started. See Rate limits.

Contacting support: open a conversation under Support in the console, or email support@spicyapi.ai. Include the evidence from above: time, endpoint, code, request_id or task ID, what you expected and what happened, and the structure of your request with private values removed. Never send an API key.

Model missing or unavailable

  • The model isn't in your list, or you get 404: call GET /api/v1/models with your API key and use a model whose enabled and available are both true, copying its ID exactly. A model page on the website doesn't prove your key may call it right now. See Models.
  • 50301 — the model can't take tasks at the moment. Retry a few times with growing pauses, then stop; repeated requests can't open an unavailable model. Refresh the catalog, and ask the person paying before switching to another model.
  • 40004 — the model is working, but not with this combination of settings. The msg names the setting in parentheses. Change that value (for example a resolution or aspect ratio) instead of retrying.
  • 40301 — the model exists, but this key isn't allowed to use it; edit the key's allowed models.

Invalid input or quote conflict

  • 400 — compare your input with the model's current inputSchema in the catalog: field names, types, allowed values and conditional requirements. Remove fields the model doesn't list. Check again after switching models, because each model has its own schema.
  • 40003 — an uploaded file doesn't match what you declared when requesting its upload URL. Request a new upload URL with the right contentType and size, and upload again.
  • 413 — the request body is too large. Upload media through the media flow instead of embedding it.
  • 40901 — the quote expired, the request changed after quoting, or the price changed. Get a new quote, show the new amount to the person paying, and confirm again. See Quotes.
  • Other 409 — handle by operation. A key already used for a different payload needs a new, correct Idempotency-Key for the new request, not an endless retry. See Errors → When 409 is worth retrying.

Submission timed out without a task ID

A timeout doesn't tell you whether the task was created. Don't just send it again with a new key — that can create a second task and a second charge.

Resend exactly the same request

Use the same body and the same Idempotency-Key, with the same API key, within 24 hours. If the first attempt was accepted, you get the original task back and are charged once. If it wasn't, this creates it now. See Idempotency.

Read the task

Once you have the task ID, call recordInfo. Its state, cost and settled tell you the outcome.

Only then decide on a new submission

If the original failed, a new task with a new key is a new charge at current prices.

Client timeouts, closed browser tabs and disconnected streams don't cancel an accepted task, and an accepted task can't be canceled.

The task failed

Failed or expired tasks normally release the full hold. Some content refusals are charged at the accepted quote only when the model page explicitly discloses this exception. Check settled and cost for the final charge.

  • Don't retry unchanged: invalid_request, unsupported_combination, content_rejected (the selected model declined the content — change the prompt or the model).
  • Worth retrying after a pause: rate_limited, upstream_unavailable, upstream_failed.
  • Fix first: invalid_asset (upload the file again), timeout (try a shorter duration or lower resolution), generation_failed (the same inputs usually fail the same way).

errorMessage explains the reason for people; it may be the model's own reason in English. Branch your code on errorCode only. The full table is in Errors → Task failure codes. A retry is a new task and is billed again if it succeeds.

Task stays queued or running

Generation takes time — a long video can take several minutes. Every task has a server-side deadline, returned as deadlineAt in recordInfo:

  • Before deadlineAt: keep waiting for the callback, or poll recordInfo with growing pauses (not every second).
  • After the deadline: the task ends on its own as expired with errorCode: "timeout", and its hold is returned in full. You don't need to cancel anything — and accepted tasks can't be canceled.

If a task stays running well past its deadlineAt, contact support with the task ID.

Upload succeeded but input is rejected

  • You skipped the commit. A successful PUT of the file is not enough: call files/{fileId}/commit, then pass the returned spicy:// URI in the model's input field. Never pass the uploadUrl itself.
  • The file doesn't match the ticket. Check the declared Content-Type, size and format; a mismatch returns 40003.
  • It has expired. An uploaded file can be used until the expiresAt in its commit response — 1 day by default, or sooner if your account deletes uploads when the job ends. Retrying a task may therefore need a fresh upload.
  • It belongs to another account. A spicy:// URI only works with keys of the account that uploaded it.

See Media for the full upload flow.

Missing callback or expired download

No callback arrived:

  1. Check that callBackUrl is an https:// address reachable from the internet. Plain http:// is not accepted.
  2. Check that your receiver verifies the signature over the original request bytes, before any JSON parsing, and answers with a 2xx quickly. Slow work belongs in your own queue. If the signing key was rotated recently, the receiver must use the new one: there is no overlap period. See Webhooks.
  3. Look at the delivery history under Webhooks in the console, where you can also retry a delivery.
  4. Meanwhile, read the task with recordInfo — polling is always a valid fallback. See Webhooks.

Callbacks can arrive more than once or late, so deduplicate before fulfilling an order.

The download link or file is gone:

  • A download link lasts 20 minutes. If the result is still retained, just request a new link; refreshing an old link doesn't work.
  • If the output is still being copied to storage, wait a few seconds and ask again.
  • Results are kept for about 14 days at most, and less if your account uses a shorter retention setting. Once contentState is expired or purged, the file can't be recovered — run the model again. See Retention.

Stream ends early

Check the HTTP status and Content-Type before parsing the stream for your protocol. Network chunks are not complete events, and a closed connection is not a success marker: look for the protocol's final event, any error event, and your own application deadline. Don't assume a disconnected stream was free or can be resent at no cost — valid usage recorded before the disconnect may be billed. Keep the request_id and check your usage records. See Text and streaming.

Team permissions

  • You can't create a key. The scope picker at the top of the console's left sidebar is set to a team where your role is Budget manager or Read-only. Creating keys needs the Owner, Admin or Developer role. Switch to your Personal scope, or ask a team admin for the Developer role.
  • You can see a task but not its prompt or result. Budget managers and Read-only members see status, cost and usage only.
  • You can't accept an invitation. Sign in with the exact email address the invitation was sent to. Invitations expire after 7 days; ask for a new one if yours has.
  • A team key stopped with 40201. Team keys spend the team owner's balance, so the owner needs to add funds.

See Console and teams for roles and team limits.

Can't issue an invoice

  • Your details are incomplete. An invoice needs a name (your company or your own), address line 1, city and country, entered once at the top of Billing → Invoices.
  • A character can't be printed. Only Latin, Greek and Cyrillic characters can appear on an invoice. Write a Japanese or Korean company name in its registered romanized form.
  • It already exists. Each paid top-up and each closed month can be invoiced once; download the existing document. To correct details, choose Reissue on it.
  • The month isn't available. Only months that have ended and in which you spent something can have a usage statement.

See Billing → Invoices and usage statements.

Errors · Production integration · Idempotency · Media · Webhooks · Text and streaming · Account setup

On this page