spicyapiDocs
Main content

Authentication

Key format, per-key restrictions, which tasks a key can see, and what to do the moment a key leaks.

Every API call that creates, reads or bills anything carries your API key in the Authorization header. (The public model catalogue used for evaluation is the one exception: it needs no key.)

Authorization: Bearer sk-spicy-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

The native API (/api/v1/*) accepts bearer keys only — no cookies, no query parameters.

The text and video compatibility endpoints (/v1, /v1beta) take the same key and also accept the x-api-key header used by Anthropic SDKs and the x-goog-api-key header used by Google GenAI SDKs, so the official SDKs work with nothing changed but the base URL — see Text and streaming. Those two headers do nothing on /api/v1, and no endpoint accepts a key in the URL query string.

API keys and the console are separate

Only /api/v1, /v1 and /v1beta accept API keys. The web console uses its own browser session. So a console login cannot be used as an API key, and a leaked API key cannot sign in to your console. Sending the wrong kind of credential always returns 401, never a permissions error.

What a key looks like

A fixed sk-spicy- prefix followed by 48 lowercase hexadecimal characters.

PartPurpose
sk-spicy-Fixed prefix, so the key is recognisable in logs, repositories and GitHub secret scanning
Next 8 charactersLookup segment: it lets us find the key's record quickly. The whole key is still verified
Remaining 40The rest of the secret. We store only a peppered SHA-256 hash of the whole key — we cannot read it back either

The prefix is deliberate: being scanned and notified beats finding out when someone else has been spending your balance.

A key is shown exactly once

Copy it, or click Save as a file, before you close the dialog. Lose it and you create a new one. We cannot recover it — and that is the point. Recoverable would mean stored in the clear.

Create a key

  1. Open Console → API keys and click Create key.
  2. Name it after the service that will use it (production backend, staging worker). You will need to recognise it when revoking.
  3. Optionally open Advanced to set spend caps, an allowed-model list, an IP allowlist and an expiry date. All of them can be changed later.
  4. Copy the key or click Save as a file, and confirm that you have stored it. Once the dialog closes, the key cannot be shown again.

Creating keys requires the owner, admin or developer role in the team, and the key belongs to the team selected at the top left of the console: its usage and caps count against that team. Each team can have up to 50 active keys. Your account also needs a verified email and a balance before the key can do paid work; see Account setup.

Where to keep the key

  • On your computer, for testing: export SPICY_API_KEY="…" in the terminal (it lasts until you close the window), or a .env file that your code loads. Add .env to .gitignore before your first commit.
  • On a server or hosting platform: use its secrets or environment-variable settings, and read SPICY_API_KEY from the environment in your code.
  • Never paste the key into source code, a chat, a screenshot, a support ticket or an AI assistant's prompt.

Never ship a key to the client

Anything in a browser or an app can be read out of it

Bundling, obfuscation, splitting the string across files — none of that is protection. Anyone with devtools or a decompiler has your key, and a leaked key runs until your balance or its caps are used up.

When a frontend needs generation, proxy it through your own backend: the browser calls you, you call us. You get to enforce your own per-user quotas at that layer anyway.

The same applies to mobile binaries, desktop clients, public notebooks, CI logs, and anything that ends up in a curl -v dump pasted into a support ticket.

Your own tools in your own browser are a different case. The API accepts calls from browsers, so tools you run for yourself, such as the SillyTavern extension or a roleplay app where you paste your own key, can call it directly. The rule above is about putting a key in something other people load. Give each such tool its own key with a daily cap, so you can see what it spends and revoke it on its own.

Per-key restrictions

Configure these in the console, when creating the key or later. They are enforced server-side.

RestrictionWhat it buys youBusiness code when hit
Daily spend capNew keys have no cap unless you set one. Add your own amount to put a ceiling on a misconfiguration or a leak. Rolls over every UTC day40202
Monthly budgetWhat this key may spend in the current calendar month, counted in UTC40202
Lifetime spend capTotal a key may ever spend. Useful for keys handed to a partner40202
ExpiryThe key stops working at the end of the chosen day, UTC401
IP allowlistOnly the listed addresses or CIDR ranges may call. Recommended for server-side integrations40302
Model allowlistFor example, staging keys limited to cheap models. Left unrestricted, the key can also call models added later40301

Each key's card shows the caps it currently has, and you can change them there at any time.

Caps count money that is on hold for running tasks as well as money already spent, so a key can reach its cap while tasks are still in flight. The msg says which cap was reached. A 40202 whose msg mentions the platform quota is not about your key: creating another key will not help, so retry later.

The IP allowlist is matched against your real public address as seen by our edge network, not the address of the last proxy. Your egress address is the public IP your server's requests come from: most hosting platforms list it in their network settings, and from your own machine curl https://ifconfig.me shows it.

Tasks belong to the key that created them

A task is visible through the API only to the API key that created it. With any other key, even one from the same team:

What you do with another keyResult
Read the task (recordInfo), retry it, purge it or ask for a download link404, the same as a task that does not exist
List tasks (GET /api/v1/jobs) or read usage (GET /api/v1/usage)Only that key's own tasks and spend
Resend a submission with the same Idempotency-Key409, without the original task ID

A 409 after switching keys does not mean the task was not created

If a submission timed out under one key and you resend it under another, the 409 hides a task that may already be running and will be charged. Do not treat it as "not created" and resubmit with a new idempotency key: that pays for a second generation. Resend with the key that made the first attempt, or look the task up in the console.

Two things are not tied to the key. Webhooks for a task are delivered even after the key that created it is revoked. And every task in the team, whichever key created it, is listed under Console → Logs, where each task's detail shows the Key used.

Rotation

  • One key per environment (production / staging / local), so you can revoke one without touching the others.
  • Planned rotation: create the new key and send only new tasks with it. Keep the old key active until every task it created has reached a terminal state (at most that task's deadlineAt) and you have collected the results, then revoke it. Use this sequence only when the old key is still trusted.
  • Revoking takes effect immediately and cannot be undone. There is no cache to wait out and no way to re-enable the key; you create a new one.

After a leak

Revoke it now

In Console → API keys, open the key's actions and choose Revoke. It is instant.

Do not wait for your own in-flight tasks: from now on they can no longer be read through the API with that key. Tasks already accepted keep running and are still charged. Collect their results from your webhooks or from Console → Logs. A leak is not the moment to wait.

Create a replacement

Create a new key and update your server configuration. Do not leave a leaked key active while preparing its replacement.

Check what it was used for

Open Console → Logs for the exposure window and look for tasks you did not make: unfamiliar models, unusual volume, or spend you cannot account for. Each task's detail shows the Key used, and Export CSV includes the key's ID for every task. The API keys page also shows when each key was last used.

Tighten the replacement

Give the replacement an appropriate IP allowlist and daily cap, and remove the original exposure from your code, logs or deployment configuration.

If unexpected spend occurred, contact us through Console → Support with the task IDs or request_id values.

What a failure looks like

{
  "code": 401,
  "msg": "Credentials are invalid or expired",
  "request_id": "req_01k3m8x9q2z4v7n5p6r8s0t1w2"
}

The HTTP status is 401 as well. Every authentication failure returns code: 401, and msg tells you which kind it is:

msg starts withWhat happenedWhat to do
Missing credentialsNo key reached usCheck that the header is spelled Authorization: Bearer … and that SPICY_API_KEY is set in the process that sends the request (a variable set in one terminal window does not exist in another)
API key format is not recognizedSomething was sent, but it is not a SpicyAPI keyKeys start with sk-spicy-. Look for a missing Bearer prefix, a missing or extra character, quotes, or a line break copied with the key
Credentials are invalid or expiredA key of the right shape that does not exist, was revoked or has expiredThese cases are deliberately indistinguishable, so nobody can probe which keys exist. Check the key's status under Console → API keys, or create a new one

Branch on code, not on the text: the wording can change and follows your error language. More on telling them apart in Errors.

When the account itself cannot call the API (email not verified, account suspended) you get 403 instead. Swapping keys will not help with that one.

On this page