# Authentication

Authentication

# One header, one key, every endpoint

Monocrawl authenticates with a single API key sent on each request. There are no OAuth flows, no per-platform credentials and no tokens to refresh.

## 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`.

**terminal**

```
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`.

**401 · application/json**

```
{
  "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"
}
```

## 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.

**terminal**

```
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.

## What a key is made of

**api key**

```
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 | `Hash + encrypted copy` | A SHA-256 hash validates requests. New keys also have an encrypted copy for owner-only recovery; the console normally shows the prefix and last 4. |

Keys are masked by default, for example `mn_4pQ8...Yz01`. For new active keys, sign in and use Reveal or Copy on the API keys page. Older keys only have a hash and cannot be recovered. If you no longer have one, create a new key and revoke the old one.

## 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, delete (a revoked key stays revoked: create a new one). 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 or can be revealed. Revocation is permanent; create a new key instead. |
| 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.

## 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.

Request limits follow your plan and apply both per key and across the account. Starter allows 600 requests a minute and 50 in flight per key; higher tiers increase both. Details in [endpoint pricing](https://www.monocrawl.com/docs/endpoint-pricing#rate-limits), and the failure mode in [errors](https://www.monocrawl.com/docs/errors#rate-limited).

## Keeping a key safe

`01`

Server-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.

`02`

One key per environment

Separate keys make usage attributable — the console reports requests per key — and let you revoke staging without touching production.

`03`

Environment, 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.

`04`

Rotate by overlap

Create the replacement, deploy it, confirm traffic has moved on the per-key request counts, then revoke the old key.
