Lead generation API for AI agents

An assistant asked to "find 200 dentists in Austin with their emails" needs four things: a way to check the price, a way to start the work, a way to know when it finished, and structured records at the end. That is the whole surface here.

Get an API key

The loop

Four calls, no SDK required. This is the shape an agent should implement:

1. POST /api/v1/quote     ask what it costs        (free, changes nothing)
2. POST /api/v1/jobs      start it                 (charges the balance, returns 202)
3. GET  /api/v1/jobs/{id} poll every 20-30 seconds (until status is terminal)
4. GET  /api/v1/jobs/{id}/results   read records   (JSON, paged)

Step 1, ask the price first

This matters more for an agent than for a human, because the agent is spending a balance it does not own. The quote endpoint is free and has no side effects. A real response:

POST /api/v1/quote  {"source": "google_maps", "total_records": 200}

{
  "source": "google_maps",
  "total_records": 200,
  "email_verification": false,
  "cost": { "base": "2.40", "verification": "0.00", "total": "2.40" },
  "currency": "USD",
  "note": "You are charged up front and refunded for any records we cannot deliver."
}

Present that number to the user before spending it. If the balance is short, the start call returns 402 insufficient_credits rather than half-doing the work.

Step 2, start the job

POST /api/v1/jobs
{
  "source": "google_maps",
  "list_name": "austin-dentists",
  "total_records": 200,
  "search_terms": "dentist",
  "location": "Austin, Texas"
}

Three sources exist. Google Maps takes a search term and a place. Apollo.io takes a people search URL the user already built there. LinkedIn takes filters, not a URL: job titles, locations, company domains, keywords, revenue range and more.

Step 3, poll until terminal

Five states, three of them terminal. Real response from a finished job:

GET /api/v1/jobs/90

{
  "job": {
    "id": 90,
    "status": "succeeded",
    "source": "google_maps",
    "list_name": "austin-dentists",
    "requested_records": 200,
    "delivered_records": 173,
    "records_with_email": 118,
    "credits_charged": "2.40",
    "credits_refunded": "0.32",
    "error": null,
    "results": { "available": true, "variants": ["all"], "url": "/api/v1/jobs/90/results" }
  }
}
statusWhat the agent should do
queued, runningWait 20 to 30 seconds and poll again
succeededRead the results
no_resultsNothing matched. The full amount was refunded. Suggest a broader search rather than retrying the same one
failedRead error, report it, do not silently retry: the money was already refunded

If you would rather not poll, pass callback_url when starting the job. The finished job object is posted there, signed with HMAC-SHA256.

Step 4, read the records

GET /api/v1/jobs/90/results?limit=1000&offset=0

{ "total": 173, "offset": 0, "limit": 1000, "has_more": false,
  "fields": ["Place_Name", "Email", "Phone", "..."],
  "records": [ { "Place_Name": "...", "Email": "...", "Phone": "..." } ] }

Guard rails worth implementing

What it costs

SourcePer 1,000 records
Google Maps$12
Apollo.io$9
LinkedIn and Sales Navigator$9
Real-time email verification, optional+$5

Records delivered without an email address are billed at a reduced rate. Prepaid balance in USD, no subscription. A confirmed account starts with $2.00 of credit, which is enough to run a first job end to end before deciding anything.

Errors an agent will actually hit

401  {"error":{"code":"invalid_api_key","message":"This API key is not valid, or it was revoked."}}
400  {"error":{"code":"invalid_request","message":"source must be one of: google_maps, apollo, linkedin."}}
400  {"error":{"code":"invalid_request","message":"Search terms and location are required!"}}
402  {"error":{"code":"insufficient_credits","message":"Your credit is insufficient!"}}
429  {"error":{"code":"rate_limited","message":"Too many requests. Try again in a minute."}}

Every error is JSON with a stable code and a message written to be shown to a person. Branch on the code, show the message.

An MCP server, so an assistant can call Evascrape as a tool rather than through raw HTTP, is on the way. Until it ships, the four calls above are the whole integration.

Related