The header
Send x-api-key on every request
The key goes in the x-api-key request header. This is the supported form on every route, including POST /v1/batch.
curl "https://www.monocrawl.com/v1/bluesky/profile?handle=bsky.app" \ -H "x-api-key: mn_your_key_here"
A key is a server-side credential. The /v1 API is not configured for cross-origin browser calls. Never ship a key in client code: call it from your server, an edge function or a scheduled job, and keep the key there.
A missing key and an unrecognised key both answer 401 UNAUTHORIZED; the details.reason field separates the two — missing_api_key or invalid_api_key. Neither costs credits. Repeated invalid-key attempts can instead receive 429 with reason invalid_key_attempts.
{
"success": false,
"error": {
"type": "UNAUTHORIZED",
"message": "API key is invalid or has been revoked.",
"status": 401,
"doc_url": "https://www.monocrawl.com/docs/errors#unauthorized",
"details": { "reason": "invalid_api_key" }
},
"credits_used": 0,
"request_id": "req_9c07b1e4da62f8a03d"
}Bearer
Authorization: Bearer, for header-restricted clients
Some clients only expose an Authorization field. The same key works there as a Bearer token on GET /v1, GET /sandbox and /mcp. POST /v1/batch requires x-api-key.
curl "https://www.monocrawl.com/v1/bluesky/profile?handle=bsky.app" \ -H "Authorization: Bearer mn_your_key_here"
Never in the URL
Keys are refused in the query string — URLs land in proxy logs, browser history and referrers, which is exactly where a credential must not live. A request relying only on ?api_key= answers 401 with reason query_string_key_rejected, so the mistake is caught loudly, not silently accepted.
Batch exception
Use x-api-key for POST /v1/batch. Bearer authentication is supported on GET /v1, GET /sandbox and the MCP server, not on batch.
Trying in a browser?
Use the Playground in the console — it runs real requests on your key without the key ever reaching the address bar.
Key format
What a key is made of
mn_4pQ8Zr1nKuT0aWxYb7cLdE3fGh9JmNpQsRtUvWxYz01
| Part | Value | Notes |
|---|---|---|
| Prefix | mn_ | Three fixed characters. Use them to spot a Monocrawl key in a config file or a secret scanner rule. |
| Body | 43 characters | 32 random bytes, base64url-encoded — the part that actually authenticates you. |
| Total length | 46 characters | New keys use this format. If yours differs, check that you copied the complete value. |
| Stored form | SHA-256 hash | The raw key is never written down. We keep the hash, the 7-character prefix and the last 4 for display. |
Because only the hash is stored, a key is displayed in full exactly once — at the moment it is created. Afterwards the console can only show you mn_4pQ8...Yz01. If you lose a key, create a new one and revoke the old one; there is no recovery path, by design.
Lifecycle
Creating and revoking keys
Your first key is provisioned for you: the first time you open the console overview with no key on the account, one is created server-side and revealed on that render. After that, keys are managed at /dashboard/api/keys — create, rename, revoke, reactivate, delete. An account can hold up to five active keys, which is enough to separate production, staging and a laptop.
| Status | Behaviour |
|---|---|
| active | Authenticates. The only status that reaches the pipeline. |
| revoked | No longer authenticates; normally answers 401 UNAUTHORIZED. Reversible from the console. |
| deleted | Stops working and disappears from the console. The row is kept rather than erased, so your historic usage and invoices still reference a real key. There is no undo. |
Revocation takes effect on the next request — there is no cache to wait out. Deleting is a soft delete so that your usage history and invoices keep pointing at a real key row.
Per-key limits
Cap what one key can spend
Every key carries an optional credit_limit. When it is set, the pipeline reserves the call's cost against that key before spending your wallet, and refuses the request the moment credits_used + cost would exceed the cap:
402 KEY_LIMIT_EXCEEDED
The response carries credit_limit and credits_used in details, and charges 0 credits. Raise the cap or move the workload to another key.
Reserved atomically
The cap is enforced by a conditional update, so concurrent calls on one key cannot race past it — a burst of 50 requests cannot overshoot the limit.
Null means uncapped
Keys are created without a limit. An uncapped key can spend the whole account balance, which is what you want for a single production key.
Independent of the wallet
The limit is a per-key budget, not a separate balance. Credits still come out of one account-wide wallet.
Rate limits are per key too — 600 requests a minute and 25 in flight at once. Details in endpoint pricing, and the failure mode in errors.
Good practice
Keeping a key safe
01Server-side only
A key in browser JavaScript is a key you have published. Call Monocrawl from your backend and forward only what your users need.
02One key per environment
Separate keys make usage attributable — the console reports requests per key — and let you revoke staging without touching production.
03Environment, not source control
Read the key from an environment variable. The mn_ prefix makes it easy to write a pre-commit rule that catches the mistake.
04Rotate by overlap
Create the replacement, deploy it, confirm traffic has moved on the per-key request counts, then revoke the old key.