Skip to content
unzoi docs
Search and navigation
Start here
REST API
MCP
Limits and plans
Agent clients
SDKs
Guides

Authentication

One key, two headers that both work, and the same rules on REST and MCP.

The header

Either of these authenticates a request. They are equivalent; pick whichever your HTTP client makes easier.

x-api-key: nai_...
Authorization: Bearer nai_...

The same two work on https://api.unzoi.com/mcp. MCP clients that let you set arbitrary headers usually use x-api-key; ones that only offer a bearer field use Authorization.

curl -s "https://api.unzoi.com/search?q=inflation" -H "x-api-key: $UNZOI_KEY"
curl -s "https://api.unzoi.com/search?q=inflation" -H "Authorization: Bearer $UNZOI_KEY"

What does not need a key

The documentation the API serves is public, deliberately. An agent deciding whether this API is worth signing up for has to be able to read what it does before it has a key:

Everything else answers 401 without a key.

Handling keys

A key is shown once, at creation. Only its hash is stored, so a lost key is rotated, not recovered — rotation issues the new key before revoking the old one, so there is no window where neither works.

  • Never put a key in a browser. There is no CORS-credentialed mode and no per-origin restriction: a key in front-end JavaScript is a key anyone can read and spend. Proxy through your own server.
  • One key per deployment, not per developer. Every key on a tenant shares the plan and its credits, so separate keys buy you revocation granularity and usage attribution, not separate budgets.
  • Short-lived keys exist. The console can issue a key with a TTL of up to 24 hours — useful for a demo, a CI run, or handing an agent a credential you do not intend it to keep.

How a request is refused

Four things can turn a request away, and they mean different things.

Status Meaning
200 Success
400 Invalid request, charged nothing. Bounds, enums, dates and offsets are validated BEFORE any index work, so this never means a partially-served query. `credit_budget_exceeded`: the call's worst case is more than `max_credits`.
401 Missing or invalid API key. `code` is `unauthorized`.
402 Account suspended (subscription canceled or payment failed). `code` is `suspended`.
429 Refused before running, and charged nothing. `credit_rate_limited`: this minute's credits are spent. `message_rate_limited`: too many free calls or refused requests this minute. `insufficient_credits`: the key stops at its monthly allocation and does not have this call's worst case left. `retry-after` says how long to wait.
503 The index could not be read for this request. This is NOT an empty result set and NOT a statement that nothing matched — retry rather than caching it as a negative. `coverage` says which part was unreadable.

Order matters: the key is checked first, then suspension, then the call is priced and checked against the max_credits you passed, the credits left this month and the credits a minute. A 429 therefore never means "your key is wrong", and a 401 never means "you are over budget".

On MCP, two of these move

An MCP client that receives an HTTP error status for a tool call concludes the server has gone away, and stops. That is the correct reading for a missing key — a client with no credential cannot open a session at all — but it is the wrong reading for an account that simply needs paying.

So the MCP endpoint splits them:

Condition REST MCP
Missing or invalid key 401 401 — the session never opens
This minute's credits spent 429 + retry-after A tool error, credit_rate_limited — transient, retry the call
Too many messages this minute 429 + retry-after 429 + retry-after — transient, send the same message again
Account suspended 402 A tool error on an open session
Not enough credits left this month, on a key that stops there 429 A tool error on an open session

In the last two cases initialize and tools/list still succeed, and the refusal arrives on the call that actually costs something, carrying a machine-readable code the agent can act on. MCP errors shows the exact shape.

What every response tells you

Authenticated responses carry your standing as headers, so backing off correctly never needs a second request.

Header Meaning
x-credits-charged Credits this call cost, equal to `credits_charged` in the body. 0 for a preview.
x-credits-limit Credits included this calendar month. Absent when unlimited.
x-credits-per-minute Credits per minute allowed by this plan.
x-credits-per-minute-remaining Credits left in the current minute, after this call.
x-credits-per-minute-reset Seconds until this minute's credits refill.
x-credits-remaining Credits left this month.
x-credits-reset Seconds until the monthly allocation resets.
x-credits-used Credits used this month, including this call.
x-plan-history-days How far back this plan may query. Absent means the full archive.

Limits and plans covers what to do with each of them, and GET /account returns the same figures as a body.

Keys are issued and revoked at console.unzoi.com.