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:

  1. API key: a plan-scoped krawl_* key passed in the X-API-Key header. 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.
  2. 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.
  3. Keyless x402: an autonomous agent pays per request in USDC on Base, with no account at all. Enabled when the server has X402_PAY_TO set. 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:

SegmentUsed by
livefree, payg, pro, pro_zdr plans
demono-signup demo keys
ententerprise plan
testinternal 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:

FieldTypeDefaultNotes
planstring"free"One of free, payg, pro, pro_zdr, enterprise, test. The demo plan is not issuable here (use /auth/demo-key).
namestring""Human-readable label, up to 100 chars.
owner_emailstringnullOptional 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).

PlanSearch credits / moDeep krawls / moRate limitMax $ / reqZDR
demo10035/hour$0.50No
free (default)1,0002510/minute$1.50No
paygUnlimited (metered)Unlimited (metered)60/minute$5.00No
proUnlimited (metered)Unlimited (metered)120/minute$8.00No
pro_zdrUnlimited (metered)Unlimited (metered)120/minute$8.00Yes
enterpriseUnlimited (metered)Unlimited (metered)600/minute$20.00Yes
test2001020/minute$1.00No

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 returns 429.
  • Monthly quota (per-key, soft cap). Before each research request, Krawl checks the key's monthly deep_krawls or search_credits usage against the plan limit. Finite plans that are exhausted return 402. 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_request caps 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:

  1. A krawl_* key is validated against the multi-user key store (plan-scoped, revocable, expirable).
  2. If the key is not a krawl_* key, it is compared against KRAWL_DEV_KEY (constant-time).
  3. 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.json

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

On this page