Credits and overage
Every call is priced in credits from one published table, before it runs. A plan includes credits a month and credits a minute, and both count the same credits.
| Plan | Price | Credits a month | Credits a minute | At the allocation |
|---|---|---|---|---|
| Free | Free | 1,000 | 120 | Stops |
| Build | $29/mo | 50,000 | 600 | Bills overage |
| Scale | $199/mo | 500,000 | 3,000 | Bills overage |
| Archive | $799/mo | 2,000,000 | 12,000 | Bills overage |
The allocation resets at the start of each calendar month (UTC).
What each call costs
A plain search costs 1 credit. A call that makes the index do more work costs more: a vector ranking, entity resolution, a threshold, a checked geographic filter, a relationship traversal. The price is the same on REST and over MCP, and the same whichever host answers; how many index partitions a call reaches is never billed.
| Call | Credits |
|---|---|
| search_news, top_headlines — keyword ranking or a browse | 1 |
| search_news — mode=semantic / mode=hybrid | 2 / 3 |
| the same with near, bbox, amount_min or amount_max | 1–4: 1 per 500 candidates checked (keyword or browse); +1 on a semantic or hybrid search |
| a cursor page | 1–2 to start, 1–3 after, in place of the base |
| + each entity_id | +1–2, up to 5 ids |
| + signal_percentile_min | +1 when a threshold resolves |
| + offset on /search and /top-headlines | +1 per full 1,000 rows |
| list_stories | 1, or 2 semantic and 3 hybrid unless sort=recency; +1 with a geographic, event-class or amount filter; +ids and percentile as above; +1 per full 100 rows of offset, which stops at 1,000 |
| get_story | 2 |
| get_article | 1 |
| find_related | 2 |
| resolve_entity | 1–2 |
| aggregate_news | 1, whatever the number of buckets; +1 with event_type; +ids as above |
| related_entities | 2 to 4 + 2 per edge that gets evidence, at most 16 |
| connection_path | 3–8 with max_hops=1, 3–9 with evidence=0, up to 16 |
| entity_network | 2 to 4 + 2 per edge that gets evidence, at most 16; +3 for a comparison window |
| shared_exposures | n + 3 to 2n + 3 for n entity ids; +1 with signal_stats |
| create_watch, list_watches, get_watch, delete_watch, watch_events | 1 each |
| each run of a watch | the credits the watch's /stories query takes |
| each webhook delivery attempt | 1, retries included |
| account_status, /account, MCP initialize, tools/list and notifications, any estimate=true preview | 0 |
Every priced answer says what it cost: credits_charged in the body, and the
x-credits-charged header on REST. Each tool description and each endpoint in
/openapi.json ends with its price.
What a call is charged, by outcome
| Outcome | Charged |
|---|---|
200, including a partial answer | What the call took, held inside its estimate |
404 | The operation's minimum: the lookup ran |
400, 401, 402, a credit refusal, 408, 5xx | Nothing |
| The client disconnects mid-call | The operation's minimum |
Free calls
/account, account_status,
the MCP handshake (initialize, tools/list, notifications) and any
preview cost 0 credits. They are not unlimited: each takes a message token from an
allowance equal to the plan's credits a minute, and so does a request refused before it ran.
Rate limits has the detail.
Know the price first
Add estimate=true to any endpoint, or "estimate": true to any tool call. The call does not
run and costs nothing; the answer is its credit range, the breakdown, where the key stands, and whether the call would
run now.
curl -s -G "https://api.unzoi.com/search" -H "x-api-key: $UNZOI_KEY" \
--data-urlencode "q=chip export controls" --data-urlencode "mode=hybrid" \
--data-urlencode "entity_id=organization:asml" --data-urlencode "estimate=true" {
"estimate": true,
"operation": "search_news",
"credits": { "min": 4, "max": 5 },
"breakdown": [
{ "item": "hybrid_pass", "min": 3, "max": 3 },
{ "item": "entity_id", "min": 1, "max": 2 }
],
"max_credits": null,
"credits_charged": 0,
"credits_limit": 50000,
"credits_used": 12483,
"credits_remaining": 37517,
"credits_reset_secs": 394200,
"credits_per_minute": 600,
"hard_cap": false,
"would_run": true,
"would_refuse": null
}
To cap a call instead, pass max_credits. A call whose worst case costs more is refused with
400 credit_budget_exceeded before it runs, and costs nothing. An agent working to a budget can pass it
on every call.
Watches and webhooks
A watch runs its /stories query for you, and each run is charged what that
query costs to the key that created it: 1 credit for a plain recency poll, more with entity ids, a threshold or a
derived filter. Every webhook delivery attempt costs 1 credit, retries included, so one event can cost up to 4.
A create preview (estimate=true) adds per_run, runs_per_month and
webhook_attempts_per_event_max.
| Plan | Watches | Shortest interval | Runs a month, one watch at that interval |
|---|---|---|---|
| Free | 2 | 60 minutes | ~720 |
| Build | 25 | 15 minutes | ~2,880 |
| Scale | 200 | 5 minutes | ~8,640 |
| Archive | 1,000 | 1 minute | ~43,200 |
On a key that stops at its allocation, a run that the credits left cannot cover does not run: the watch pauses with
status: paused_credits and is tried again hourly. A pending delivery is held with
delivery.status: held_credits, without counting an attempt, and resumes when credits return; a held
event that reaches its seven-day retention fails. On a key that bills overage, runs and deliveries carry on and are
billed like any call.
The headers
x-credits-charged: 2
x-credits-limit: 50000
x-credits-used: 12485
x-credits-remaining: 37515
x-credits-reset: 394200 used is the durable counters plus this replica's charged calls, so it includes the call that returned it
and matches the invoice. reset is seconds until the next calendar month. On an unlimited account the
allocation headers are absent rather than zero — a limit of 0 would read as "blocked".
The per-minute headers are on Rate limits.
Hard cap, or overage
What happens at the ceiling depends on one flag, reported by /account:
-
hard_cap: true— a call is refused, before it runs, when its worst case is more than the credits left. Only the free tier defaults to this, because there is no card on file. Checking the worst case means a key never overshoots its allocation, even with calls in flight. -
hard_cap: false— calls keep working and the credits past the allocation are billed. Paid tiers default here, so a traffic spike degrades your bill rather than your product.
Either behaviour can be set on any paid plan from the console.
Overage
| Plan | Per 1,000 credits past the allocation |
|---|---|
| Free | Not offered — this tier stops instead |
| Build | $1.00 |
| Scale | $0.60 |
| Archive | $0.40 |
Hitting the ceiling
On a hard-capped key without enough credits left for a call:
HTTP/1.1 429 Too Many Requests
retry-after: 394200
x-credits-remaining: 2
x-credits-charged: 0
{
"error": "insufficient credits",
"code": "insufficient_credits",
"detail": "this call can cost up to 5 credits and 2 are left this month; ...",
"retry_after_secs": 394200,
"credits": { "min": 4, "max": 5 },
"credits_limit": 1000,
"credits_used": 998,
"credits_remaining": 2
}
This shares a status with the per-minute limit, and the two want different
handling: switch on code. insufficient_credits will not clear for
retry-after seconds, possibly days — but a cheaper call might still fit, which credits and
credits_remaining let you decide.
Over MCP the same refusal is a tool error with the same fields, on an open session, so the agent can report it and carry on the moment the account is topped up. The exact shape is here.
How billing sees it
Credits are metered continuously and reported to Stripe against the account's credits meter. The figures
in /account, in the console and on the invoice come from the same durable
counters, and the console breaks them down by operation, with calls and credits side by side.