Docs·Public Data API
Public Data API
Authentication and rate limits
Every endpoint is public, read-only and serves stored data only — never a provider call; a key is optional and changes the rate limit and nothing else.
Authentication#
| Question | Answer |
|---|---|
| Which endpoints need credentials? | None. Every endpoint under /api/v1 is public and read-only. |
| Which accept an optional credential? | All of them, as an X-API-Key request header. It changes the rate limit and nothing else — not the data, not the fields, not which endpoints answer. |
| How is a key obtained? | By asking. Keys are issued by hand and held in this deployment’s configuration — there is no self-service portal. A key that was not issued for the environment you are calling is not recognised there. |
| What happens to an unrecognised key? | The request is served, and rate-limited as anonymous. The response says so: x-thehood-api-key-status: unrecognised. Nothing is rejected and no data changes. |
| Are cookies or bearer tokens accepted? | No. CORS advertises access-control-allow-origin: * with no allow-credentials, and the only request headers accepted on a preflight are content-type, if-none-match and x-api-key. |
| Is any endpoint write-capable? | No. Every route exports GET and OPTIONS only. |
Stored data only#
| The API | Rule |
|---|---|
| Serves | Only data already stored in The Hood’s own database — the indexer’s views, read over a read-only connection |
| Never calls | A provider on your behalf — no RPC node, price feed, explorer or social API is reached by any /api/v1 request. Chain head is never read |
| Never fetches on a miss | Data we have not stored is refused with API_V1_DATA_NOT_STORED, naming what is stored. It is not fetched, back-filled or proxied |
| Never exposes | Our provider keys, endpoints or their error text — in a body, a header or an error |
X-API-Key | A key for this API’s rate limit. It unlocks no provider and no extra data |
What is public, and what is not#
Rate limits and keys#
| Tier | Identified by | Requests per minute |
|---|---|---|
anonymous | The first entry of X-Forwarded-For, else X-Real-IP, else one shared bucket | 60 |
free | An X-API-Key header carrying a key issued for this deployment. An unrecognised value is counted as anonymous, in the caller’s own IP bucket. | 600 |
pro, business | An issued key configured onto that tier | 600 |
The counter lives in memory per function instance, so the effective limit is the figure above multiplied by however many instances are serving, and it resets when an instance is recycled. That is stated rather than hidden: a limit that is quietly ten times what it claims is a limit nobody can reason about. It still does the job it exists for, which is stopping one client opening a hundred concurrent connections against the reader from a single instance.
| Header | Value |
|---|---|
x-ratelimit-limit | The tier’s limit |
x-ratelimit-remaining | Requests left in this window, floored at 0 |
x-ratelimit-reset | Unix seconds at which the window ends |
x-thehood-api-tier | anonymous or the keyed tier |
x-thehood-api-key-status | absent (no header sent), recognised, or unrecognised (sent, not honoured, counted as anonymous) |
A refused request is a 429 with code API_V1_RATE_LIMITED, and its details carry path, tier, limit, remaining, resetAt, resetAtIso, retryAfterSeconds — the exact wait, in the body — plus apiKeyStatus, apiKeyLabel and apiKeyFingerprint, the first 8 hex characters of the key’s SHA-256. The key itself is never echoed, in the body or anywhere else; hash your own copy to confirm which one was used.