API Reference

Complete reference for the Krawl REST API, SSE research, async tasks, OpenAI-compatible shim, and more

Base URL: https://api.krawl.sh

Version: 0.2.0

Authentication

Most endpoints accept an optional API key via the X-API-Key header. Behavior depends on how the server is configured:

  • krawl_* keys are validated against the keys table (multi-user, plan-scoped, revocable, expirable).
  • A legacy single KRAWL_DEV_KEY is honored for backward compatibility.
  • If neither is configured, auth is disabled (development only).
curl -H "X-API-Key: your-key" https://api.krawl.sh/health

When auth is enabled and the key is wrong, missing, revoked, or expired:

{"detail": "Invalid, revoked, or expired API key"}

The research endpoints (/research, /research/upload, /templates/{id}/research, /v1/chat/completions) also support keyless pay-per-request via x402 when enabled. Admin-only endpoints require a separate KRAWL_ADMIN_KEY. See Authentication for the full model.

Rate limits, retries & status

Idempotency-Key replays eligible successful JSON responses for up to 24 hours. Authentication and key-management routes under /auth, token issuance at /agent/auth, requests carrying the session cookie or an Authorization header, legacy or unknown X-API-Key credentials, ZDR requests, ephemeral evidence exports, SSE, no-store and Set-Cookie responses bypass this response cache. Authentication always runs again for these requests; a retry of key issuance can create a new key. Do not rely on this header to recover a lost credential. Previously cached responses are invalidated when this security update is deployed.

Rate limit: 5 requests/minute per IP by default (some endpoints set their own limits, noted below). Every response carries X-RateLimit-Limit / X-RateLimit-Remaining / RateLimit-Policy headers so you can self-throttle.

When you exceed a limit the API returns 429 Too Many Requests with a Retry-After header (delta-seconds) and a structured envelope ({ "error_code": "RATE_LIMITED", "retry_after": 60, ... }). Agents must honor Retry-After and apply exponential backoff + jitter on 429/5xx. See the full Rate Limits & Retries playbook for thresholds, the 429 envelope, and a concrete backoff loop.


Health

GET /health

Liveness and readiness check, including database connectivity.

Auth: None.

curl https://api.krawl.sh/health

Response:

{
  "status": "ok",
  "version": "0.2.0",
  "db": "ok",
  "uptime_seconds": 1234.5
}

status is ok when the database is reachable, otherwise degraded. db is ok or error.


Research

POST /research

Execute deep research and stream events via SSE. This is the primary endpoint.

Auth: API key (or keyless x402 when enabled).

Request body:

FieldTypeDefaultDescription
querystringrequiredResearch query (1 to 2000 chars)
modestring"deep"Research mode. One of: deep, quick, crypto, token-analysis, protocol-research, whale-tracking, narrative, risk-assessment, yield-strategy
depthinteger75Research depth intensity (1 to 100)
breadthinteger4Parallel queries per depth level (1 to 10)
depth_levelsinteger3Recursion depth levels (1 to 5)
sourcesstring[]["exa","x","web"]Allowed source types: exa, x, github, crypto, web, academic, media, community
modelstring | nullnullOverride the LLM model for all phases. If null, uses server defaults
session_idstring | nullnullSession ID for conversational follow-up research. Loads prior context when provided
prior_contextstring | nullnullPrior research markdown. Injected by the server (not set directly by callers)
prior_learningsobject[] | nullnullPrior learnings. Injected by the server
output_schemaobject | nullnullJSON Schema (Draft 7) for structured output extraction
source_policyobject | nullnullSource filtering rules (see below)
preferred_search_providerstring | nullnullWeb search provider: exa, firecrawl, tavily, serper, brave, duckduckgo, tinyfish. Defaults to the server's WEB_SEARCH_PROVIDER (exa unless configured)
document_contextstring | nullnullExtracted document text. Injected by the server from /research/upload
perspectivesboolean | nullnullSTORM perspective-guided research. null uses the server default; true/false forces it per request

Source policy:

{
  "source_policy": {
    "include_domains": ["arxiv.org", "nature.com"],
    "exclude_domains": ["medium.com"],
    "freshness": "30d"
  }
}

freshness accepts windows like 7d, 30d, 90d, 1y.

Response: SSE stream. See SSE Events for all event types.

curl -N -X POST https://api.krawl.sh/research \
  -H "Content-Type: application/json" \
  -H "X-API-Key: your-key" \
  -d '{"query": "AI agents 2025", "mode": "deep", "breadth": 4, "depth_levels": 3}'

If output_schema is invalid, the request fails with 422 before the stream starts.

POST /research/upload

Research with uploaded document context. Multipart form data. Returns the same SSE stream as /research.

Auth: API key (or keyless x402 when enabled).

Form fields:

FieldTypeDefaultDescription
querystringrequiredResearch query (1 to 2000 chars)
modestring"deep"Research mode (see modes above)
filesFile[][]Up to 5 files, 10MB each. Formats: .pdf, .txt, .md, .csv, .json
curl -N -X POST https://api.krawl.sh/research/upload \
  -H "X-API-Key: your-key" \
  -F "query=Analyze the findings in these documents" \
  -F "mode=deep" \
  -F "files=@report.pdf" \
  -F "files=@data.csv"

Empty file slots are ignored. An invalid mode or too many files returns 422.

POST /playground

Run a real research stream with no API key. This is the homepage playground, a marketing and activation surface, not a free API tier.

Auth: None. Heavily constrained: forced quick mode, short query cap, tiny cost ceiling, per-IP rate limit (15/hour, raised from 3/hour in 2026-06 to fit small teams behind corporate NAT — see /rate-limits), and results are NOT persisted. Returns 404 if the playground is disabled.

Request body:

FieldTypeDefaultDescription
querystringrequiredQuery, capped at the server's playground max length
curl -N -X POST https://api.krawl.sh/playground \
  -H "Content-Type: application/json" \
  -d '{"query": "What is Uniswap v4?"}'

Response: SSE stream (same event shape as /research).

POST /research/compare

Compare research results to detect changes over time. Returns a structured diff with new findings, removed claims, changed metrics, and a natural language summary.

Auth: API key.

Request body:

FieldTypeDefaultDescription
querystringrequiredQuery used to find similar results when result_ids is empty (1 to 2000 chars)
result_idsstring[][]Up to 10 result IDs. With 2+, compares those directly. With 1, compares against the most similar prior result. Empty finds the 2 most recent similar results
curl -X POST https://api.krawl.sh/research/compare \
  -H "Content-Type: application/json" \
  -H "X-API-Key: your-key" \
  -d '{"query": "ETH staking yields"}'

Response:

{
  "old_result_id": "uuid",
  "new_result_id": "uuid",
  "old_query": "ETH staking yields",
  "new_query": "ETH staking yields",
  "old_created_at": "2026-05-01T00:00:00Z",
  "new_created_at": "2026-05-29T00:00:00Z",
  "changes": [],
  "new_findings": [],
  "removed_claims": [],
  "summary": "...",
  "has_changes": true
}

POST /research/{research_id}/steer

Inject a steering instruction into an active research session. The research_id is the session ID emitted at the start of the SSE stream.

The latest queued instruction applies before the next agent turn in quick mode, or query-generation call in recursive modes, including child research nodes. It carries into synthesis/refinement, report scope and requested structured output without rerunning completed searches. The audit precedes the result so disconnecting after the report preserves its applied controls. The steer SSE event identifies the instruction consumed at that boundary; instructions superseded before a boundary do not produce an applied event. An in-flight provider call continues under its existing request. Durable pause/resume and branch cancellation are not supported.

Auth: API key.

Request body:

FieldTypeDefaultDescription
instructionstringrequiredSteering instruction (1 to 1000 chars)
curl -X POST https://api.krawl.sh/research/550e8400-e29b-41d4-a716-446655440000/steer \
  -H "Content-Type: application/json" \
  -H "X-API-Key: your-key" \
  -d '{"instruction": "Focus on regulatory risk in the EU"}'

Response:

{
  "status": "instruction_queued",
  "research_id": "550e8400-e29b-41d4-a716-446655440000",
  "instruction": "Focus on regulatory risk in the EU"
}

Returns 404 if there is no active session with that ID.


Async Tasks

Submit research to run server-side without holding an SSE connection, then poll for status.

POST /research/async

Submit a research job to run in the background. Returns immediately with 202.

Auth: API key. Quota is enforced up front, and a per-key cap bounds concurrent fan-out (returns 429 when exceeded).

Request body:

FieldTypeDefaultDescription
querystringrequiredResearch query (1 to 2000 chars)
modestring"deep"Research mode (see modes above)
perspectivesboolean | nullnullSTORM perspective-guided research (null uses server default)
webhook_urlstring | nullnullOptional URL to POST a completion notification to (max 2000 chars)
curl -X POST https://api.krawl.sh/research/async \
  -H "Content-Type: application/json" \
  -H "X-API-Key: your-key" \
  -d '{"query": "State of L2 rollups", "mode": "deep"}'

Response (202):

{
  "id": "uuid",
  "query": "State of L2 rollups",
  "mode": "deep",
  "status": "queued",
  "result_id": null,
  "error": null,
  "created_at": "2026-05-29T00:00:00Z",
  "started_at": null,
  "finished_at": null
}

status is one of queued, running, done, error.

GET /tasks

List the caller's async tasks, newest first.

Auth: API key.

Query params: limit (default 50, clamped to 1 to 100), offset (default 0).

curl https://api.krawl.sh/tasks?limit=20 \
  -H "X-API-Key: your-key"

Response: an array of task objects (same shape as above).

GET /tasks/{task_id}

Poll a single async task. When status is done, fetch the report via GET /results/{result_id}.

Auth: API key.

curl https://api.krawl.sh/tasks/uuid \
  -H "X-API-Key: your-key"

Returns 404 if the task is not found. Response is a single task object.


OpenAI-compatible

Point any OpenAI SDK (or LangChain, LlamaIndex, etc.) at Krawl by setting base_url to this server.

POST /v1/chat/completions and POST /chat/completions

OpenAI-compatible Chat Completions. The model selects the research mode and the last user message is the research query. Returns a fully cited report as the assistant message. Supports streaming and non-streaming.

Auth: API key (or keyless x402 when enabled).

Request body:

FieldTypeDefaultDescription
modelstring"krawl-deep"Maps to a research mode (e.g. krawl-deep, krawl-quick, krawl-crypto)
messagesobject[]requiredChat messages. The last user message becomes the query. Each message has role, content (string or content-part array), optional name
streambooleanfalseWhen true, returns SSE chat.completion.chunk events ending with [DONE]

Unknown fields (temperature, top_p, tools, etc.) are accepted and ignored so existing OpenAI callers work unchanged.

curl -X POST https://api.krawl.sh/v1/chat/completions \
  -H "Content-Type: application/json" \
  -H "X-API-Key: your-key" \
  -d '{
    "model": "krawl-deep",
    "messages": [{"role": "user", "content": "Compare Solana and Aptos"}]
  }'

Response (non-streaming): a standard OpenAI chat.completion object with the report in choices[0].message.content. Returns 422 if there is no user message content to research.

GET /v1/models and GET /models

List the available krawl-* research models in OpenAI's format.

Auth: None enforced beyond the default rate limit.

curl https://api.krawl.sh/v1/models

Response:

{
  "object": "list",
  "data": [
    {"id": "krawl-deep", "object": "model", "owned_by": "krawl"}
  ]
}

Templates

Reusable research prompts with fillable variables.

GET /templates

List all available research templates.

Auth: API key.

curl https://api.krawl.sh/templates \
  -H "X-API-Key: your-key"

Response: {"templates": [...]}

GET /templates/{template_id}

Get a specific template by ID. Returns 404 if not found.

Auth: API key.

curl https://api.krawl.sh/templates/token-deep-dive \
  -H "X-API-Key: your-key"

POST /templates/{template_id}/research

Execute a template with filled-in variables. Returns an SSE stream (same as /research). The template's own mode is used.

Auth: API key (or keyless x402 when enabled).

Request body:

FieldTypeDefaultDescription
variablesobject{}Map of variable names to string values used to fill the template
curl -N -X POST https://api.krawl.sh/templates/token-deep-dive/research \
  -H "Content-Type: application/json" \
  -H "X-API-Key: your-key" \
  -d '{"variables": {"token": "ETH"}}'

Returns 404 for an unknown template and 422 if required variables are missing.


Results & Sessions

GET /results

List persisted research results.

Auth: API key (results are scoped to the key).

Query params: offset (default 0), limit (default 50, 1 to 100).

curl https://api.krawl.sh/results?limit=10 \
  -H "X-API-Key: your-key"

Response: {"results": [...]}

GET /results/{result_id}

Retrieve a specific result. The result_id must be a UUID. Returns 404 if not found.

Auth: API key.

curl https://api.krawl.sh/results/550e8400-e29b-41d4-a716-446655440000 \
  -H "X-API-Key: your-key"

GET /results/{result_id}/export

Export a result as HTML, PDF, Markdown, DOCX, or a slide deck.

Auth: API key.

Query params:

ParamTypeDefaultDescription
formatstringhtmlOne of pdf, html, markdown, docx, slides, run-record
themestringlightOne of light, dark (HTML output)
curl -L "https://api.krawl.sh/results/550e8400-e29b-41d4-a716-446655440000/export?format=pdf" \
  -H "X-API-Key: your-key" \
  -o report.pdf

Returns the rendered file with an appropriate Content-Type and Content-Disposition. Returns 404 if the result is not found.

GET /sessions/{session_id}

Return all research results in a session, ordered by creation time. The session_id must be a UUID.

Auth: API key.

curl https://api.krawl.sh/sessions/550e8400-e29b-41d4-a716-446655440000 \
  -H "X-API-Key: your-key"

Response:

{
  "session_id": "550e8400-e29b-41d4-a716-446655440000",
  "results": [...]
}

Returns 404 if the session does not exist or has no results.

GET /audit/{result_id}

Get the audit trail for a research result: per-step search, extract, learn, gap-detect, synthesize, and verify actions with timing and token usage.

Auth: API key.

curl https://api.krawl.sh/audit/550e8400-e29b-41d4-a716-446655440000 \
  -H "X-API-Key: your-key"

Response:

{
  "result_id": "550e8400-e29b-41d4-a716-446655440000",
  "entries": [
    {
      "timestamp": "2026-05-29T00:00:00Z",
      "action": "search",
      "tool": "webSearch",
      "query": "...",
      "sources_found": 8,
      "learnings_extracted": 3,
      "model": "...",
      "tokens_used": 1200,
      "duration_ms": 850.0
    }
  ]
}

Returns 404 if there is no audit trail for the result.


POST /search

Execute a single-source search and return JSON results. This is not the full research pipeline, it queries one source directly.

Auth: API key.

Request body:

FieldTypeDefaultDescription
querystringrequiredSearch query (1 to 2000 chars)
sourcestring"exa"Source to query: exa, x, github, crypto, web, academic, media
optionsobject{}Source-specific options (see below)

Common options keys: num_results (clamped to 1 to 30, default 8), category, start_date, include_domains (Exa); from_date, to_date, include_handles, exclude_handles (X); token_id (crypto); url (web and media, fetches or transcribes a specific URL).

curl -X POST https://api.krawl.sh/search \
  -H "Content-Type: application/json" \
  -H "X-API-Key: your-key" \
  -d '{"query": "restaking protocols", "source": "exa", "options": {"num_results": 10}}'

Response: shape varies by source. Most sources return {"sources": [...]}. X also returns text, citations, and tweets. GitHub adds repos. Crypto returns token, tvl, and/or search_results.

{
  "sources": [
    {
      "url": "https://example.com",
      "title": "...",
      "content": "...",
      "published_date": null,
      "source_type": "exa",
      "author": null,
      "favicon": null,
      "credibility_tier": null
    }
  ]
}

Lookouts

Scheduled recurring research tasks.

POST /lookouts

Create a new lookout. Enforces a per-key limit (returns 429 when reached).

Auth: API key.

Request body:

FieldTypeDefaultDescription
querystringrequiredResearch query (1 to 2000 chars)
modestring"deep"Research mode (see modes above)
schedulestring"daily"One of hourly, daily, weekly
webhook_urlstring | nullnullOptional notification URL (max 500 chars, validated against SSRF)
curl -X POST https://api.krawl.sh/lookouts \
  -H "Content-Type: application/json" \
  -H "X-API-Key: your-key" \
  -d '{"query": "Base ecosystem news", "mode": "quick", "schedule": "daily"}'

Response: the created lookout object.

{
  "id": "uuid",
  "query": "Base ecosystem news",
  "mode": "quick",
  "schedule": "daily",
  "active": true,
  "last_run": null,
  "last_result_id": null,
  "created_at": "2026-05-29T00:00:00Z",
  "webhook_url": null
}

GET /lookouts

List lookouts.

Auth: API key.

Query params: offset (default 0), limit (default 50, 1 to 100).

curl https://api.krawl.sh/lookouts \
  -H "X-API-Key: your-key"

Response: {"lookouts": [...]}

GET /lookouts/{lookout_id}

Get a specific lookout. UUID required. Returns 404 if not found.

Auth: API key.

DELETE /lookouts/{lookout_id}

Delete a lookout. Returns 404 if not found.

Auth: API key.

curl -X DELETE https://api.krawl.sh/lookouts/uuid \
  -H "X-API-Key: your-key"

Response: {"deleted": true}

POST /lookouts/{lookout_id}/run

Manually trigger a lookout run. Rate-limited to 2/minute and subject to a per-lookout cooldown (returns 429 if triggered too soon after the last run, or 503 if the server is busy).

Auth: API key.

curl -X POST https://api.krawl.sh/lookouts/uuid/run \
  -H "X-API-Key: your-key"

Response: the run result.


Memory

Cross-session knowledge base of prior research findings.

GET /memory

Search the knowledge base.

Auth: API key.

Query params: q (required, max 500 chars), limit (default 20, 1 to 100).

curl "https://api.krawl.sh/memory?q=staking&limit=10" \
  -H "X-API-Key: your-key"

Response:

{
  "query": "staking",
  "count": 3,
  "memories": [
    {
      "id": "uuid",
      "content": "...",
      "source_query": "...",
      "source_urls": ["..."],
      "tags": ["..."],
      "created_at": "2026-05-29T00:00:00Z",
      "result_id": "uuid"
    }
  ]
}

Returns 400 if q is empty.

GET /memory/stats

Statistics about the knowledge base.

Auth: API key.

curl https://api.krawl.sh/memory/stats \
  -H "X-API-Key: your-key"

DELETE /memory/{memory_id}

Delete a specific memory entry. UUID required. Returns 404 if not found.

Auth: API key.

curl -X DELETE https://api.krawl.sh/memory/uuid \
  -H "X-API-Key: your-key"

Response: {"deleted": true, "id": "uuid"}


Ask

POST /ask

NLWeb-compatible natural language query endpoint. Accepts a question and returns a structured answer pointing the caller to the right research endpoint. Optionally streams NLWeb-style SSE events (start, result, complete).

Auth: API key.

Request body:

FieldTypeDefaultDescription
querystringrequiredNatural language question (1 to 2000 chars)
streamingbooleanfalseWhen true, returns SSE NLWeb events instead of JSON
curl -X POST https://api.krawl.sh/ask \
  -H "Content-Type: application/json" \
  -H "X-API-Key: your-key" \
  -d '{"query": "How do I research a token?"}'

Response:

{
  "_meta": {"response_type": "answer", "version": "0.1", "provider": "krawl.sh"},
  "query": "How do I research a token?",
  "answer": "Use the /research endpoint for deep research on: How do I research a token?",
  "sources": [],
  "suggested_endpoint": "/research",
  "auth": {"method": "api_key", "header": "X-API-Key", "signup": "https://krawl.sh"}
}

MCP

POST /mcp and GET /mcp

Model Context Protocol server over Streamable HTTP (JSON-RPC 2.0). Agent platforms call POST /mcp to initialize, tools/list, and tools/call. Discovery methods are never gated; only tools/call execution is authenticated (X-API-Key, or keyless x402 when enabled). GET /mcp returns server metadata and the tool list.

See Agents & MCP for the full integration guide.

curl -X POST https://api.krawl.sh/mcp \
  -H "Content-Type: application/json" \
  -d '{"jsonrpc": "2.0", "id": 1, "method": "tools/list"}'

Auth & Keys

See Authentication for the complete model. Key issuance and listing are admin-only and require KRAWL_ADMIN_KEY.

POST /auth/demo-key

Generate a rate-limited demo API key for testing. No auth required. The key is hashed at rest and expires in 24 hours. Rate-limited to 3/hour.

curl -X POST https://api.krawl.sh/auth/demo-key

Response:

{
  "api_key": "krawl_demo_...",
  "type": "demo",
  "plan": "demo",
  "rate_limit": "...",
  "monthly_search_credits": 0,
  "monthly_deep_krawls": 0,
  "expires_in": "24 hours",
  "note": "Demo key for testing. Sign up at krawl.sh for full access."
}

POST /auth/keys

Issue a new API key. The raw key is returned exactly once.

Auth: Admin (KRAWL_ADMIN_KEY).

Request body:

FieldTypeDefaultDescription
planstring"free"Plan name: free, payg, pro, test. demo is not issuable here
namestring""Human-readable label (max 100 chars)
owner_emailstring | nullnullOwner email (validated format, max 255 chars)
curl -X POST https://api.krawl.sh/auth/keys \
  -H "Content-Type: application/json" \
  -H "X-API-Key: $KRAWL_ADMIN_KEY" \
  -d '{"plan": "pro", "name": "acme prod", "owner_email": "ops@acme.com"}'

GET /auth/keys

List API key metadata. Never returns raw keys.

Auth: Admin (KRAWL_ADMIN_KEY).

Query params: owner_email (optional, max 255 chars).

curl "https://api.krawl.sh/auth/keys?owner_email=ops@acme.com" \
  -H "X-API-Key: $KRAWL_ADMIN_KEY"

Response: {"keys": [{"prefix": "...", "plan": "...", "name": "...", "owner_email": "...", "active": true, "created_at": "...", "last_used_at": "...", "expires_at": "...", "revoked_at": "..."}]}

DELETE /auth/keys

Revoke the krawl_ key presented in the X-API-Key header (self-service). Rate-limited to 20/minute.

Auth: the key being revoked.

curl -X DELETE https://api.krawl.sh/auth/keys \
  -H "X-API-Key: krawl_live_..."

Response: {"revoked": true}. Returns 404 if the key is not found or already revoked.


Usage & Billing

GET /usage

Return the caller's usage for the current (or given) month plus plan quotas. Requires a krawl_ API key. Rate-limited to 60/minute.

Auth: krawl_ API key.

Query params: period (optional, format YYYY-MM).

curl https://api.krawl.sh/usage \
  -H "X-API-Key: krawl_live_..."

Response:

{
  "plan": "pro",
  "period": "2026-05",
  "usage": {"deep_krawls": 12, "search_credits": 340, "total_tokens": 1200000, "cost_usd": 4.231},
  "limits": {"monthly_deep_krawls": 100, "monthly_search_credits": 5000, "rate_limit": "..."},
  "remaining": {"deep_krawls": 88, "search_credits": 4660}
}

remaining values are null for unlimited (negative-limit) plans.

GET /usage/events

Return recent per-request usage events for the caller's key. Rate-limited to 60/minute.

Auth: krawl_ API key.

Query params: limit (default 50, 1 to 200).

curl https://api.krawl.sh/usage/events \
  -H "X-API-Key: krawl_live_..."

Response: {"events": [...]}

POST /billing/checkout

Start a Stripe Checkout session to upgrade the caller's key to Pro. Only krawl_live_ keys can be upgraded. Rate-limited to 10/minute. Returns 503 if billing is not enabled.

Auth: krawl_live_ API key.

Request body:

FieldTypeDefaultDescription
emailstring | nullnullCustomer email (max 255 chars). Falls back to the key's owner email
curl -X POST https://api.krawl.sh/billing/checkout \
  -H "Content-Type: application/json" \
  -H "X-API-Key: krawl_live_..." \
  -d '{"email": "ops@acme.com"}'

Response: a Stripe Checkout session (includes the hosted checkout URL).

POST /billing/portal

Open the Stripe billing portal for the caller's linked customer. Rate-limited to 10/minute. Returns 400 if no billing account is linked, 503 if billing is disabled.

Auth: krawl_ API key.

curl -X POST https://api.krawl.sh/billing/portal \
  -H "X-API-Key: krawl_live_..."

Response: a Stripe billing portal session (includes the portal URL).

POST /billing/webhook

Consume signature-verified Stripe webhook events and apply plan changes. Called by Stripe, not by API clients. Requires STRIPE_WEBHOOK_SECRET and a valid stripe-signature header.

Auth: Stripe signature.

Response: {"received": true, ...}. Returns 400 on invalid payload or signature, 503 for retryable reconciliation errors.


Discovery

OAuth and x402 metadata that let agents discover how to authenticate and pay.

GET /.well-known/oauth-protected-resource

OAuth 2.0 Protected Resource metadata (RFC 9728).

Auth: None.

curl https://api.krawl.sh/.well-known/oauth-protected-resource

Response:

{
  "resource": "https://api.krawl.sh",
  "authorization_servers": ["https://api.krawl.sh"],
  "scopes_supported": ["research:read", "research:write", "results:read"],
  "bearer_methods_supported": ["header"],
  "resource_documentation": "https://docs.krawl.sh"
}

GET /.well-known/oauth-authorization-server

OAuth 2.0 Authorization Server metadata (RFC 8414) plus a WorkOS agent_auth discovery block.

Auth: None.

curl https://api.krawl.sh/.well-known/oauth-authorization-server

GET /discovery/resources

Agent-payable resource discovery for x402 / MPP. Lists the API resources an agent can pay for per-request, with price, network, and asset, so an autonomous agent can discover costs without first triggering a 402.

Auth: None.

curl https://api.krawl.sh/discovery/resources

Response:

{
  "x402Version": 1,
  "enabled": true,
  "facilitator": "...",
  "resources": [
    {
      "resource": "https://api.krawl.sh/research",
      "method": "POST",
      "description": "Deep multi-source research report (cited markdown).",
      "x402": {
        "scheme": "exact",
        "network": "...",
        "asset": "...",
        "maxAmountRequired": "...",
        "priceUsd": 0.0,
        "payTo": null
      }
    }
  ],
  "payment_methods": ["x402", "api_key"],
  "auth_docs": "https://krawl.sh/auth.md"
}

Internal / experimental

Agentic commerce endpoints (/checkout-sessions, /checkout_sessions, /checkout_sessions/{session_id}, /agentic_commerce/delegate_payment) implement UCP/ACP checkout flows. They are internal and excluded from the public OpenAPI schema, and are subject to change.

Evidence time and access

Research sources carry optional retrieved_at (UTC), published_date, updated_date, date provenance, access_status, source_record_id and temporal_status. Missing fields on older reports mean unknown; never assign export time as retrieval time. Access states are inspected_text, partial_excerpt, provider_summary, metadata_only, fetch_failed, or unknown. These describe available evidence, not claim support or truth. A recent fetch does not make an old article current. Summary/metadata-only results do not establish that the original text was read.

Optional source_policy.as_of sets a UTC evidence cutoff; dates after it are quarantined in quarantined_sources, outside synthesis. Without it the cutoff is research start. This is not a historical web archive; undated content remains uncertain. See evidence methodology.

Evidence coverage

The optional evidence_coverage result field gives descriptive counts of unique exact URLs discovered, explicitly fetched, retained, referenced in outgoing model input, and cited. Stages overlap: a retained search snippet may never have had its original page fetched. It also reports explicit failed/unavailable fetches, unknown fetch/access states, retained-host concentration, missing publication dates and their known range. A model-input reference may contain only metadata or a short excerpt; a citation is not a support verdict.

These counts do not establish representative sampling or correct selection bias. Hostnames do not prove independent origins; mirrors may share a publisher. Old reports without this field have unknown coverage. No extra LLM call is made; the measured instrumentation overhead is reported as measurement_overhead_ms, not a guarantee of zero added latency. See evidence methodology.

On this page