Authentication
API keys, no-signup demo keys, keyless x402, plans and quotas, key management, and usage tracking
Krawl supports three ways to authenticate a request, plus a legacy single shared key honored by the server for backward compatibility. Most of the work happens through plan-scoped API keys that carry quotas, rate limits, and a per-request cost ceiling. Autonomous agents that do not want an account can pay per request with keyless x402.
Website sign-in
People can sign in on https://krawl.sh/auth with email and password, and with Google, GitHub, or X when those providers are configured. Continue with ChatGPT appears only when OPENAI_OAUTH_CLIENT_ID is set. The callback to register with OpenAI is https://api.krawl.sh/auth/oauth/openai/callback. That sign-in creates a Krawl session. It does not by itself grant ChatGPT plan usage, and it is separate from the API key an agent sends.
Auth Modes
Krawl accepts three forms of authentication on its research endpoints:
- API key: a plan-scoped
krawl_*key passed in theX-API-Keyheader. Keys carry a plan with monthly quotas, a rate limit, and a per-request cost ceiling. This is the standard way to use the API. - Demo key: a rate-limited key you can mint with no signup via
POST /auth/demo-key. It expires in 24 hours and is meant for evaluation, not production. - Keyless x402: an autonomous agent pays per request in USDC on Base, with no account at all. Enabled when the server has
X402_PAY_TOset. See Payments & x402.
A legacy single shared key (KRAWL_DEV_KEY) is also honored on the server for backward compatibility (see below).
Sending a Key
Pass your key in the X-API-Key header on every request:
curl -X POST https://api.krawl.sh/research \
-H "X-API-Key: krawl_live_xxxxxxxxxxxxxxxxxxxxxxxx" \
-H "Content-Type: application/json" \
-d '{"query": "What is EIP-4844?", "mode": "quick"}'If a key is invalid, revoked, or expired, the server responds with 401:
{
"detail": "Invalid, revoked, or expired API key"
}Key Format
Keys look like krawl_<segment>_<token>, for example krawl_live_a1b2c3d4.... The middle segment encodes the key class:
| Segment | Used by |
|---|---|
live | free, payg, pro, pro_zdr plans |
demo | no-signup demo keys |
ent | enterprise plan |
test | internal developer testing |
The raw key is shown exactly once when it is created. Krawl never stores the plaintext key, only a short display prefix (e.g. krawl_live_a1b2c3d4e5f6) and an HMAC-SHA256 hash of the full key. The hash is the irreversible handle used for data isolation, usage rollups, and billing linkage. Store the raw key securely; it cannot be recovered.
Demo Keys (No Signup)
To try the API without signing up, mint a demo key. No auth is required, and the endpoint itself is rate-limited to 3/hour per IP.
curl -X POST https://api.krawl.sh/auth/demo-key{
"api_key": "krawl_demo_xxxxxxxxxxxxxxxxxxxxxxxx",
"type": "demo",
"plan": "demo",
"rate_limit": "5/hour",
"monthly_search_credits": 100,
"monthly_deep_krawls": 3,
"expires_in": "24 hours",
"note": "Demo key for testing. Sign up at krawl.sh for full access."
}Use it like any other key:
curl -X POST https://api.krawl.sh/research \
-H "X-API-Key: krawl_demo_xxxxxxxxxxxxxxxxxxxxxxxx" \
-H "Content-Type: application/json" \
-d '{"query": "What is EIP-4844?", "mode": "quick"}'The demo key is persisted (hashed) and expires 24 hours after issuance. It runs on the demo plan: 100 search credits, 3 deep krawls, a 5/hour rate limit, and a $0.50 per-request cost ceiling.
Managing Keys
Standard key issuance and listing are admin-only operations, gated by a dedicated KRAWL_ADMIN_KEY (separate from the data-plane key). Revocation is self-service.
Create a Key
POST /auth/keys issues a new key and returns the raw value once. The request body is:
| Field | Type | Default | Notes |
|---|---|---|---|
plan | string | "free" | One of free, payg, pro, pro_zdr, enterprise, test. The demo plan is not issuable here (use /auth/demo-key). |
name | string | "" | Human-readable label, up to 100 chars. |
owner_email | string | null | Optional owner email. |
curl -X POST https://api.krawl.sh/auth/keys \
-H "X-API-Key: $KRAWL_ADMIN_KEY" \
-H "Content-Type: application/json" \
-d '{
"plan": "pro",
"name": "production key",
"owner_email": "team@example.com"
}'{
"api_key": "krawl_live_xxxxxxxxxxxxxxxxxxxxxxxx",
"prefix": "krawl_live_xxxxxxxxxxx",
"plan": "pro",
"rate_limit": "120/minute",
"monthly_search_credits": -1,
"monthly_deep_krawls": -1,
"note": "Store this key securely — it will not be shown again."
}A -1 quota means unlimited (metered and billed). An unknown or non-issuable plan returns 400.
List Keys
GET /auth/keys returns key metadata only. It never returns raw keys. Filter by owner_email with a query parameter.
curl https://api.krawl.sh/auth/keys?owner_email=team@example.com \
-H "X-API-Key: $KRAWL_ADMIN_KEY"{
"keys": [
{
"prefix": "krawl_live_xxxxxxxxxxx",
"plan": "pro",
"name": "production key",
"owner_email": "team@example.com",
"active": true,
"created_at": "2025-01-10T12:00:00+00:00",
"last_used_at": "2025-01-12T09:30:00+00:00",
"expires_at": null,
"revoked_at": null
}
]
}Revoke a Key
DELETE /auth/keys is self-service: it revokes the krawl_ key presented in the X-API-Key header. No admin key is needed; you simply prove you hold the key you want to revoke.
curl -X DELETE https://api.krawl.sh/auth/keys \
-H "X-API-Key: krawl_live_xxxxxxxxxxxxxxxxxxxxxxxx"{
"revoked": true
}Revoking a key that does not exist or is already revoked returns 404.
Plans & Quotas
Each key is bound to a plan that defines its monthly quotas, rate limit, per-request cost ceiling, and whether Zero Data Retention is enforced. A -1 value means unlimited (metered and billed per use).
| Plan | Search credits / mo | Deep krawls / mo | Rate limit | Max $ / req | ZDR |
|---|---|---|---|---|---|
demo | 100 | 3 | 5/hour | $0.50 | No |
free (default) | 1,000 | 25 | 10/minute | $1.50 | No |
payg | Unlimited (metered) | Unlimited (metered) | 60/minute | $5.00 | No |
pro | Unlimited (metered) | Unlimited (metered) | 120/minute | $8.00 | No |
pro_zdr | Unlimited (metered) | Unlimited (metered) | 120/minute | $8.00 | Yes |
enterprise | Unlimited (metered) | Unlimited (metered) | 600/minute | $20.00 | Yes |
test | 200 | 10 | 20/minute | $1.00 | No |
free is the default plan for any key without an explicit plan. test is for internal developer testing. pro_zdr is identical to pro but adds Zero Data Retention.
How Quotas Are Enforced
Krawl enforces limits at three layers:
- Rate limit (per-key). The plan's
rate_limit(slowapi format, e.g.120/minute) bounds request frequency. Exceeding it returns429. - Monthly quota (per-key, soft cap). Before each research request, Krawl checks the key's monthly
deep_krawlsorsearch_creditsusage against the plan limit. Finite plans that are exhausted return402. Unlimited plans (-1) skip this check. This is an eventually-consistent soft cap (no per-request lock), so a burst at the boundary can slightly overshoot. - Per-request cost ceiling (hard cap). The plan's
max_cost_usd_per_requestcaps LLM spend on a single request. When metered cost crosses the ceiling, the agent loop stops gathering and proceeds to synthesis with what it has, so a runaway request cannot burn unbounded money.
The metering layer (UsageMeter) tracks, per request, the LLM/prompt/completion tokens, cost in USD, LLM call count, search call count, and source count. On completion each request writes a usage event and bumps the monthly rollup. Billable units: a deep krawl counts as one deep_krawl, and search_credits are billed per source collected.
Rate Limit Exceeded (429)
{
"error_code": "RATE_LIMITED",
"message": "Rate limit exceeded",
"detail": "Rate limit exceeded",
"retry_after": 60
}Quota Exceeded (402)
{
"detail": {
"error": "quota_exceeded",
"message": "Monthly deep krawls limit reached for the free plan (25/25). Upgrade at https://krawl.sh/pricing or use pay-as-you-go.",
"plan": "free",
"limit": 25,
"used": 25,
"upgrade_url": "https://krawl.sh/pricing"
}
}Checking Usage
Monthly Rollup
GET /usage returns the caller's usage for the current month (or a given period like 2025-01) alongside the plan limits and remaining quota. Requires a krawl_ key in X-API-Key.
curl https://api.krawl.sh/usage \
-H "X-API-Key: krawl_live_xxxxxxxxxxxxxxxxxxxxxxxx"{
"plan": "free",
"period": "2025-01",
"usage": {
"deep_krawls": 7,
"search_credits": 142,
"total_tokens": 318450,
"cost_usd": 1.284512
},
"limits": {
"monthly_deep_krawls": 25,
"monthly_search_credits": 1000,
"rate_limit": "10/minute"
},
"remaining": {
"deep_krawls": 18,
"search_credits": 858
}
}For unlimited plans, the remaining fields are null (there is no finite limit to count down from).
Per-Request Events
GET /usage/events returns recent per-request usage events for the key, most recent first. Use the limit query parameter (default 50, max 200).
curl "https://api.krawl.sh/usage/events?limit=20" \
-H "X-API-Key: krawl_live_xxxxxxxxxxxxxxxxxxxxxxxx"{
"events": [
{
"id": "f3a1c2d4-...",
"result_id": "9b8a7c6d-...",
"session_id": "1a2b3c4d-...",
"mode": "deep",
"prompt_tokens": 41200,
"completion_tokens": 8800,
"total_tokens": 50000,
"llm_calls": 23,
"search_calls": 14,
"source_count": 19,
"cost_usd": 0.842301,
"created_at": "2025-01-12T09:30:00+00:00"
}
]
}Zero Data Retention
Zero Data Retention (ZDR) is available on the pro_zdr and enterprise plans. When a key's plan has ZDR enabled, the request's query, report, sources, sessions, audit trail, and memory are NOT persisted. Only anonymous usage counters (tokens and cost) are kept, for billing. This is intended for enterprise compliance use cases where prompt and result data must not be stored at rest.
ZDR is a property of the plan, so it applies automatically to every request made with a ZDR key. No per-request flag is required.
Legacy KRAWL_DEV_KEY
Krawl still honors a single shared key set on the server via the KRAWL_DEV_KEY environment variable for backward compatibility with internal tooling. (Upgrading from a pre-2026-06 setup? The old MOGRA_API_KEY name is no longer honored — rename it to KRAWL_DEV_KEY.) Key resolution order is:
- A
krawl_*key is validated against the multi-user key store (plan-scoped, revocable, expirable). - If the key is not a
krawl_*key, it is compared againstKRAWL_DEV_KEY(constant-time). - If neither is configured, auth is disabled (development only) and the server logs a warning at boot.
The legacy key has no plan, so monthly quota enforcement is a no-op for it. Admin key-management routes deliberately do not fall back to KRAWL_DEV_KEY. They require a separate KRAWL_ADMIN_KEY, so an ordinary data-plane key can never mint quota or read key metadata.
Billing
Paid plans use Stripe. POST /billing/checkout starts a Stripe Checkout session to upgrade a krawl_live_ key to Pro, and POST /billing/portal opens the Stripe billing portal for the key's linked customer. Both require billing to be enabled on the server (STRIPE_SECRET_KEY set) and a krawl_ key in X-API-Key. Demo and test keys cannot be upgraded. See the billing endpoints in the API Reference.
See Also
- Payments & x402: keyless, pay-per-request agent payments in USDC on Base.
- API Reference: full endpoint, request, and response details, including billing.
Audit export
Signed-in account owners can download GET /auth/me/audit/export using their
account session cookie. An API key does not authorize account-wide audit access.
The version 1 JSON export is paginated (default 100 events, maximum 500). It
contains only events attributable to that account's user ID and current keys.
There is no user_id override.
curl --cookie cookies.txt \
'https://api.krawl.sh/auth/me/audit/export?limit=100' -o audit.jsonPass the returned next_cursor as ?cursor=...&limit=100 for the next page.
The cursor preserves the requested time window and position; it is not an
authentication token or an immutable database snapshot. New requests always
check the account session and current ownership. Optional start and end
parameters must include UTC or another explicit timezone. The maximum window
is DATA_RETENTION_DAYS (90 days by default); a read-time cutoff excludes expired
rows even if the purge job has not run yet. An empty history does not prove that
an event never happened.
Events include recorded account/key activity, content-free research usage, accepted billing-meter submissions, and recorded account-deletion requests. The latter become inaccessible after account erasure. Billing-meter acceptance is not an invoice or proof of payment. This export does not claim to include unrecorded events or every Stripe event.
Each event contains id, at, event, category, actor and a small facts
object. Facts may include a known plan, mode or sign-in method and numeric usage
counts. Query/report text, source URLs and hashes, IP addresses, user agents,
key hashes, raw keys, email addresses, free-text metadata and billing identifiers
are excluded. The response is a private, noncached download; no new export
record is stored. Existing content-free ZDR usage accounting is unchanged.
On successful account erasure, sessions and keys are removed. Any retained
security event loses its user ID, IP, user agent, target and metadata, and a
completed deletion request is labelled account.erased. Only content-free
operational history remains for the existing retention period. A caller's
previously downloaded exports remain on the caller's device.