API reliability
How Narwhal reports errors, limits requests, asks you to retry, changes the API and keeps data fresh.
This page is a reference for building a client that keeps working: the error format, the headers on every response, the rate limits, when to retry, how the API changes and how fresh the data is. The full pages are Errors and credits, Versioning and deprecation and Data freshness. Live health is on the status page.
Errors
REST errors use application/. MCP tool errors carry the same fields in structured content. Every problem has type, title, status, detail, code and request_. Branch on status first and code second. detail is text for people and can change.
This request has no key. The response is a real one from the live API.
curl -i https://api.narwhalapi.com/v1/economics/usa/cpiHTTP/2 401
content-type: application/problem+json
www-authenticate: Bearer realm="narwhal"
x-request-id: c6b42dc9-cb11-4ae8-9afa-c2bdf80eb62c
{
"type": "https://narwhalapi.com/problems/invalid-api-key",
"title": "Authentication required",
"status": 401,
"detail": "Provide a valid Narwhal API key as a Bearer token. No key? An AI agent can get one for its user at https://narwhalapi.com/auth.md, or sign up at https://narwhalapi.com/signup.",
"code": "invalid_api_key",
"request_id": "c6b42dc9-cb11-4ae8-9afa-c2bdf80eb62c"
}| Status | Code | What to do |
|---|---|---|
400 | invalid_ | Fix the request. The country, period or another input is invalid. |
401 | invalid_ | The Bearer key is missing, malformed, unknown or inactive. Do not retry with the same key. |
402 | insufficient_ | Your balance does not cover the call. The call did not run and cost nothing. Add credit at the top_up_url in the response. |
404 | series_ | Nothing matches the request. It may exist after a supported official release is published. |
409 | cursor_ | The pagination cursor expired. Start again without it. |
429 | rate_ | A limit was reached. Wait for Retry-After. The call costs nothing. |
500 | internal_ | Narwhal could not complete the request. Retry with backoff. |
503 | database_ and others | Narwhal cannot safely answer right now. Retry with backoff. |
The complete list of codes is on Errors and credits. When you ask support for help, send the endpoint, the UTC time, the status, the code and the request_id. Never send the API key.
Response headers
| Header | Meaning |
|---|---|
X-Request-ID | The id of the request. It is also request_id in a problem body. |
X-Narwhal-Cost | What the call took from your balance, in US dollars with six decimals, for example 0.002000. Errors cost nothing. |
X-Narwhal-Balance | Your free, bonus and paid credit added together after the call, in US dollars with six decimals. |
X-RateLimit-Limit | Requests a minute your account can make. |
X-RateLimit-Remaining | Requests left in the bucket. |
X-RateLimit-Reset | When the bucket refills, as a UTC timestamp. |
X-RateLimit-Short-Limit, X-RateLimit-Short-Remaining, X-RateLimit-Short-Reset | The same three values for the per-account bucket. They are sent on every authenticated response. |
Retry-After | Seconds to wait. Sent with a 429. |
Rate limits
Each limit is a token bucket. It refills at the per-minute rate and holds at most the burst size. A request for several ids counts as one request.
| Who | Requests a minute | Burst |
|---|---|---|
| An account with free credit only | 50 | 8 |
| An account that holds paid credit | 500 | 84 |
| An account made through agent sign-up that is not yet verified | 10 | 5 |
| One network address, shared by every client behind it | 120 | 20 |
Clients that share an address can be throttled together by the network limit. Free credit renews on the first day of each month.
An account can also run at most 2 history reads at the same time. These are the paged history, bars and observations operations over REST and MCP. A read that cannot get a slot within 2 seconds is refused with 429 rate_ and Retry-After: 1, before it is charged.
Retries
- Do not retry a
400until the request is corrected. - Do not retry a
401with the same key. - Do not retry a
402until you have added credit. - Retry a
429only afterRetry-AfterorX-RateLimit-Reset. - Retry idempotent requests after a
500or503with bounded exponential backoff and jitter. - A
409 cursor_expiredmeans start the pagination again without the cursor.
A 503 is a refusal to return uncertain data. Narwhal sends it instead of a guess, and the call costs nothing.
Versioning and deprecation
- The REST API version is in the path:
/v1. There is no version header. MCP at/mcpnegotiates its own protocol version. SDK versions are separate from the API version. - Inside
/v1, Narwhal can add endpoints, MCP tools, data sets, countries, optional inputs, optional or nullable response fields and new enum values at any time without notice. Ignore fields you do not know and accept enum values you do not know. - Removing or renaming a field is a breaking change. So is changing its type, meaning, unit, precision or whether it is required, and changing a route, method, authentication, pricing or default behaviour. A breaking change ships as a new path, a new operation or a new major version.
- If a breaking change or removal affects an endpoint or field your account called in the last 90 days, Narwhal tells you at least 3 months before it takes effect, by email and on the changelog. Something no account called in the last 90 days can be removed with shorter notice, announced on the changelog.
- No removal date is set today.
Every change is listed in the changelog, which has an RSS feed. The full policy is on Versioning and deprecation.
Data freshness
Fresh means the latest complete official release that Narwhal has validated and published. A request reads Narwhal's stored current publication and does not call the official source, so a slow or down source does not slow your request.
A scheduled worker checks each source, validates the release, stores it, and only then makes the whole release the one customers get. An incomplete or invalid release is never mixed in. If a source check fails, Narwhal keeps the last accepted publication and retries on schedule. If it cannot safely serve a publication, the request fails with a documented 503.
| Field | Meaning |
|---|---|
period | The month, quarter or year the official observation represents. |
released_ | The official publication date. It is not Narwhal's retrieval time. |
as_ | In history responses, when the source last updated the newest observation on the page, in whole seconds, UTC. |
timestamp | For FX and spot prices, the UTC time the price belongs to. |
stale | On spot, FX and prediction event responses. For spot and FX, true means the value is out of date while its market is open: the source marks it stale, or it is more than 10 minutes old counting only open-market time. A closed market never makes a value stale. Prediction events are stale after 3 minutes without a venue check. |
Official agencies can revise a period, so a later accepted publication can replace a stored value. Headline economics figures may be provisional first estimates that the response does not mark as provisional. Narwhal does not promise a fixed delay between a source release and publication during the beta. More on Data freshness.
Was this page helpful?