Docs

Build lists from your own code.

The REST API, MCP server and webhooks do everything the app does: describe a list, approve its plan, get verified leads back.

Draft reference. Engineering should confirm endpoints and fields before launch.

Quickstart

  1. Create an API key in Settings → API keys.
  2. Send a sentence to POST /v1/lists. You get a plan and a maximum price back.
  3. Approve the plan. Crwl runs it and calls your webhook when the list is ready.
curl -X POST https://crwl.ai/v1/lists \
  -H "Authorization: Bearer $CRWL_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "prompt": "VPs of Sales at logistics companies in Chicago",
    "source": "sales_navigator",
    "limit": 500,
    "emails": "verified_only"
  }'

Authentication

Send your key as a bearer token on every request. Keys are shown once when created; store them in a secret manager and rotate them from Settings anytime.

Authorization: Bearer crwl_live_8c1f...
Never put API keys in browser code or public repos. Use them from your server.

Create a list

POST https://crwl.ai/v1/lists

FieldTypeDescription
promptstringWho you want to reach, in plain English. Required unless search_url is set.
sourcestringdatabase (default), sales_navigator, google_maps or csv. sales_navigator and google_maps scrape live; csv checks and enriches a file you upload.
search_urlstringA Sales Navigator search URL to run as-is.
file_idstringAn uploaded CSV, for source: csv.
limitintegerMaximum leads to deliver.
emailsstringverified_only (default), include_risky or none.
webhook_urlstringOptional. Where to send list.ready for this list.

The response is the plan. Nothing runs and nothing is billed yet.

{
  "id": "lst_8f2k1c",
  "status": "needs_approval",
  "plan": {
    "source": "sales_navigator",
    "filters": ["VP Sales", "Logistics", "Chicago, IL"],
    "estimated_leads": 500,
    "max_credits": 1500
  }
}

Approve the plan

POST /v1/lists/{id}/approve starts the run. To change the plan instead, create a new list with a revised prompt.

curl -X POST https://crwl.ai/v1/lists/lst_8f2k1c/approve \
  -H "Authorization: Bearer $CRWL_KEY"

Check a list

GET /v1/lists/{id} returns the status and live counts.

StatusMeaning
needs_approvalPlan is ready; waiting for you.
runningScraping, finding and verifying emails.
readyFinished. Results can be downloaded.
emptyNo matches. Not billed.
failedSomething went wrong. Not billed.

Download results

GET /v1/lists/{id}.csv or GET /v1/lists/{id}/leads (JSON, paginated with cursor). Each lead includes an email_status of valid or risky; invalid emails are dropped before delivery.

Webhooks

When a list finishes, Crwl sends a signed POST to your endpoint.

{
  "event": "list.ready",
  "list_id": "lst_8f2k1c",
  "rows": 487,
  "dropped_invalid": 13,
  "credits_billed": 1461,
  "download_url": "https://crwl.ai/v1/lists/lst_8f2k1c.csv"
}

Verify the signature

Each request has a Crwl-Signature header like t=1730822400,v1=5f2c…. Compute an HMAC-SHA256 of {t}.{raw body} with your webhook secret and compare. Reject requests older than 5 minutes.

import crypto from "node:crypto"

export function verify(rawBody: string, header: string, secret: string) {
  const parts = Object.fromEntries(header.split(",").map((p) => p.split("=")))
  const expected = crypto.createHmac("sha256", secret).update(`${parts.t}.${rawBody}`).digest("hex")
  const fresh = Math.abs(Date.now() / 1000 - Number(parts.t)) < 300
  return fresh && crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(parts.v1))
}

MCP server

Let Claude, ChatGPT or your own agent build and pull lists. Add Crwl to your client's MCP config:

{
  "mcpServers": {
    "crwl": {
      "url": "https://mcp.crwl.ai",
      "headers": { "Authorization": "Bearer $CRWL_KEY" }
    }
  }
}
ToolWhat it does
create_listTurns a sentence into a plan with a maximum price.
approve_listRuns an approved plan.
get_listStatus and counts for a list.
get_leadsDelivered leads, paginated.
Agents act with your key, so their runs are billed to your account. Use a separate key per agent and set a daily spend limit (Growth and up).

Credits and billing

  • Database or Google Maps lead: 1 credit. Sales Navigator lead: 2 credits. Email found or validated: +1.
  • On Unlimited, database lists, Google Maps scraping and email validation don't use credits (fair use); Sales Navigator scrapes are 50% off.
  • Invalid emails, failed lookups, empty runs and reruns of a paid search are free.
  • max_credits in the plan is the most a run can cost. See pricing.

Rate limits

240 read and 120 write requests per minute per workspace. Over the limit you get 429 with a Retry-After header (seconds); wait and retry.

Errors

CodeMeaning
400Missing or invalid field. The body says which.
401Missing, invalid or revoked API key.
402Not enough credits to approve this plan.
404No list with that ID in your workspace.
409The list isn't in a state that allows this action.
429Rate limited. Retry after the Retry-After seconds.
{ "error": { "code": "insufficient_credits", "message": "This plan needs up to 1,500 credits; you have 640." } }

Get an API key

Free accounts include API and MCP access with 50 leads.