Quickstart
- Create an API key in Settings → API keys.
- Send a sentence to
POST /v1/lists. You get a plan and a maximum price back. - 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...Create a list
POST https://crwl.ai/v1/lists
| Field | Type | Description |
|---|---|---|
prompt | string | Who you want to reach, in plain English. Required unless search_url is set. |
source | string | database (default), sales_navigator, google_maps or csv. sales_navigator and google_maps scrape live; csv checks and enriches a file you upload. |
search_url | string | A Sales Navigator search URL to run as-is. |
file_id | string | An uploaded CSV, for source: csv. |
limit | integer | Maximum leads to deliver. |
emails | string | verified_only (default), include_risky or none. |
webhook_url | string | Optional. 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.
| Status | Meaning |
|---|---|
needs_approval | Plan is ready; waiting for you. |
running | Scraping, finding and verifying emails. |
ready | Finished. Results can be downloaded. |
empty | No matches. Not billed. |
failed | Something 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" }
}
}
}| Tool | What it does |
|---|---|
create_list | Turns a sentence into a plan with a maximum price. |
approve_list | Runs an approved plan. |
get_list | Status and counts for a list. |
get_leads | Delivered leads, paginated. |
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_creditsin 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
| Code | Meaning |
|---|---|
400 | Missing or invalid field. The body says which. |
401 | Missing, invalid or revoked API key. |
402 | Not enough credits to approve this plan. |
404 | No list with that ID in your workspace. |
409 | The list isn't in a state that allows this action. |
429 | Rate 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.