Automation

Lead Generation API for AI Agents: How an Agent Builds a Contact List

How to give an AI agent a lead generation API: price a job for free, start it, poll status, read structured records, and hand them to a cold email tool.

E
Evascrape Team Lead generation
Updated October 12, 2026 • 9 min read
Lead Generation API for AI Agents: How an Agent Builds a Contact List

An AI agent builds a lead list by calling the Evascrape API in a fixed order. It first calls POST /quote, which prices the job for free, then POST /jobs to start it, then GET /jobs/{id} to poll until the status reaches succeeded, failed or no_results, and finally GET /jobs/{id}/results to read the records as JSON. The free quote step matters most, because the agent is spending a balance that belongs to someone else, and it should only spend after the price has been checked against a limit.

What an AI Agent Needs From a Lead Generation API

An AI agent needs a lead generation API that behaves like a function call: it accepts a structured request, returns a predictable answer, and reports the cost before any credit is spent. Evascrape exposes this under the base URL https://app.evascrape.com/api/v1. The agent authenticates with a bearer key that a person creates in the account under API keys, and sends it on every request. Store that key in a secret manager, not in the prompt or in logs.

  • A bearer key from the API keys page, so the agent can authenticate without anyone logging in.
  • POST /quote, which prices a job for free before any balance is touched.
  • POST /jobs, which starts a job with a source, filters and a record count.
  • GET /jobs/{id}, which returns the status: queued, running, succeeded, failed or no_results.
  • GET /jobs/{id}/results, which returns the records as JSON or CSV.
  • GET /balance, which shows the remaining credit so the agent knows its spending room.

The request fields matter as much as the endpoints. A LinkedIn or Sales Navigator job takes filters, not a URL: job titles to include and exclude, locations such as "california, us" or "germany", seniority level, function, company domains, industry, headcount and revenue range. An Apollo.io job takes one people search URL that was built on Apollo.io. A Google Maps job takes a search term and a place, with optional language, minimum rating, categories and a switch that skips permanently closed places. The agent should choose the source first and then validate only the fields that belong to that source.

Set the LinkedIn option to include people whose email is not yet verified when the agent needs a full list. Without it, the result is often much shorter than the number requested. Apollo.io jobs return only leads with a verified email, so their counts can be lower than what Apollo shows on screen.

Agent Workflow: Price, Start, Poll, Read

An agent builds a contact list in four calls, always in the same order: price the job, start it, poll its status, then read the records. Every call goes to the base URL https://app.evascrape.com/api/v1 and carries a bearer key that you create under API keys in your Evascrape account. The first call, POST /quote, prices the job for free, so the agent can check the cost before any balance is reserved. Only POST /jobs starts a job and spends credit.

  • Price: send the job body to POST /quote and read the cost back. Compare it with the balance from GET /balance before going further.
  • Start: send the same body to POST /jobs. The balance is reserved at the requested record count when the job starts, and the unused part is refunded when it finishes.
  • Poll: call GET /jobs/{id} with the id returned by POST /jobs and read the status field. Keep polling while it is queued or running.
  • Read: when the status is succeeded, call GET /jobs/{id}/results and request JSON for the agent's own parsing, or CSV if the next tool expects a file.

Take a concrete case. An agent is asked for up to 1,000 Apollo.io leads, and the Apollo rate is 9 US dollars per 1,000 records. It calls POST /quote, shows the quoted cost to its owner or checks it against a spending rule, then calls POST /jobs only if the rule allows it. When the job finishes, the charged and refunded amounts together equal the amount reserved. The agent should log credits_charged and credits_refunded for the audit trail, and check that they add up to the reserved amount.

Poll at an interval your agent controls. The API allows 120 requests per minute per key, so a polling loop that checks every few seconds stays well inside that limit for a single job.

Why a Free Quote Endpoint Matters Before Spending Credits

A free quote is the cheapest check an agent can make before it spends money. POST /quote prices a job without reserving anything from the account balance, so the agent learns the cost before the account is touched. This matters more for an agent than for a person. A person notices a surprising charge on the billing page, but a script in a loop keeps submitting jobs until something stops it.

  • Call POST /quote with the same filters or search term you plan to send to POST /jobs.
  • Read the quoted cost for the requested record count. Add the 5 US dollar per 1,000 verification add-on if the job will use it.
  • Call GET /balance and confirm the balance covers that amount.
  • Only then call POST /jobs. If any check fails, stop and log the reason instead of retrying the job.

The reservation rule is the reason the quote matters. Evascrape charges the balance when a job starts, at the requested record count, and refunds the undelivered part when the job finishes. A request for 2,000 LinkedIn leads reserves 18 US dollars at 9 US dollars per 1,000 records, and if only 1,200 records match, the unused part comes back automatically. The requested count is a ceiling, so the quote shows the most the job can take, not what it will finally cost.

The quote is free, so there is no reason to skip it on a small job. The minimum job size is 100 records. A Google Maps search term collects up to 500 places, so a larger target needs more search terms, and the form states how many before any money is spent.

Team member reviewing printed invoices before approving a spending decision

Designing the Agent Loop With Pseudo-Code and Status Handling

An agent loop has one job: turn a search into a file it can pass on without spending more than the person approved. The loop calls POST /quote first, which prices the job for free, and calls POST /jobs only when the quoted cost fits the approved budget. It then polls GET /jobs/{id} until the status leaves queued and running, and reads the records from GET /jobs/{id}/results as JSON or CSV. Every step should branch on the status value, because each terminal outcome needs a different action.

quote = POST /quote { source, filters, record_count } if quote.cost > approved_budget: stop and ask the user balance = GET /balance if balance < quote.cost: stop and ask the user to top up job = POST /jobs { same body as quote, optional callback_url } loop: status = GET /jobs/{job.id}.status if status in [queued, running]: wait, then poll again if status == succeeded: records = GET /jobs/{job.id}/results (json); break if status == no_results: change filters or search term, do not resubmit unchanged if status == failed: log job.id, read GET /balance, do not retry without review

  • queued or running: keep polling with a pause between calls, staying within the limit of 120 requests per minute per key.
  • succeeded: read the results, then check that credits_charged plus credits_refunded equals the amount reserved at start.
  • no_results: the filters matched nothing. Widen the location or job titles, or change the Google Maps search term, before starting a new job.
  • failed: record the job ID, check the balance, and stop for human review before any retry.

Two design choices keep the loop safe. First, the agent should never start a job from a status it has not read in the same run, so a stale quote never leads to a charge. Second, if you prefer not to poll, set callback_url when starting the job and verify the HMAC-SHA256 signature on the request before trusting its contents. Polling and callbacks both end at the same place: a finished job with records you can read, or a clear reason why there are none.

Guard Rails: Record Ceilings, Rate Limits and Cost Confirmation

An agent needs three guard rails before it spends anyone's balance: a hard ceiling on records, a cost check against a quote, and a request rate that stays under the API limits. Evascrape accepts a requested count from 100 to 50,000, in multiples of 100, so the agent should reject any value outside that range before it calls POST /jobs. The requested count is a ceiling, not a promise, so the agent should set it to the most it is willing to pay for, not the number it hopes to receive.

  • Ceiling check: refuse counts below 100 or above 50,000, and round the value down to a multiple of 100 before sending it.
  • Balance check: call GET /balance and confirm the balance covers the quoted amount before starting the job.
  • Quote check: call POST /quote, which is free, and compare its total with the spend limit the agent was given for this job.
  • Google Maps check: a search term collects up to 500 places, so a larger target needs more search terms, and the form states how many before any money is spent.
  • Start rate: send no more than 20 job starts per minute per key.
  • Request rate: keep all calls under 120 requests per minute per key, which includes each GET /jobs/{id} poll.

Cost is reserved when the job starts. Evascrape charges the requested record count at the job's rate, then refunds the undelivered part automatically when the job finishes, so credits_charged plus credits_refunded always equals the amount reserved. For example, a LinkedIn job requested at 5,000 records reserves 45 US dollars at 9 US dollars per 1,000. If it delivers 3,200 records, the 1,800 undelivered records, worth 16.20 US dollars, come back to the balance. If the agent also enables real-time email verification, budget an extra 5 US dollars per 1,000 records on top, because that add-on is never included in the base rate.

Limits set by Apollo.io, LinkedIn, Sales Navigator or Google are not stated here. Check the limits of your own plan on each platform. Evascrape collects only publicly available business data. For removal requests, see https://evascrape.com/compliance

Confirming Cost Before a Job Starts

Before an agent starts a job, it should call POST /quote and read the price back, because that call is free and creates no job. The agent then calls GET /balance and confirms the balance covers the quoted amount. Only when both checks pass should it call POST /jobs with the same parameters it used for the quote. Starting a job reserves the balance at the requested record count, so the quote and the reservation use the same arithmetic. The agent applies this rate card: LinkedIn and Sales Navigator leads cost 9 US dollars per 1,000 records, Apollo.io leads cost 9 US dollars per 1,000 records, and Google Maps business records cost 12 US dollars per 1,000 records.

Take a request for 2,000 LinkedIn records. At 9 US dollars per 1,000 records, the base reservation is 18 US dollars. Adding the optional real-time email verification at 5 US dollars per 1,000 records raises the reservation to 28 US dollars. Records that arrive without an email address are billed at 6 US dollars per 1,000 instead of the full rate, and the undelivered part of the reservation is refunded when the job finishes. The agent should therefore treat the reserved amount as a ceiling, not as the final bill.

  • Call POST /quote with the same source and filters you will send to POST /jobs.
  • Read the price returned for the requested record count.
  • Call GET /balance and confirm the balance is at least the quoted amount.
  • Stop and report to the operator if the quoted amount is above the budget set for this run.
  • Call POST /jobs only after all three checks pass.

A quote is a price, not a hold. The balance is charged when POST /jobs starts a job, and credits_charged plus credits_refunded always equals the amount reserved, so the agent can log both values to reconcile each run.

Handing Finished Records to a Cold Email Tool

Once a job's status reads succeeded, the agent fetches the records with GET /jobs/{id}/results and requests either CSV or JSON. Use CSV when the next step is a cold email tool that accepts file imports, and JSON when the agent will map each record into another API call itself. Before doing anything else, the agent should store the raw response body under the job ID, so that a failure in the email step does not force you to start the same job again.

  • Call GET /jobs/{id}/results with the format you need, and save the response unchanged in your own storage.
  • If the verification add-on was enabled on the job, the export arrives split into good, risky and bad email files. Load only the good file into a sending campaign, and review the risky file by hand before using it.
  • If the verification add-on was not enabled, verify addresses with whatever check your sending tool offers before the first message goes out.
  • Map fields by header name, not by position. Point the sending tool's email field at the email column in the file, and match each remaining field to its header.
  • Store the job ID on every campaign record, so that a removal request received under https://evascrape.com/compliance can be traced back to the job that produced the contact.

Consider an agent that pulls dental practices in one city from Google Maps. It saves the CSV, loads only the good email file into the outreach tool, and maps the file's columns into the tool's fields by header name. The job ID is written into a campaign note, so the agent can later answer the question of where each contact came from. Evascrape has no native CRM integration and no browser extension, so the handoff is a file import or a short script that calls the API.

Only publicly available business data is collected. Any contact you move into a sending tool remains your responsibility, so check the rules that apply to your sending region before the first message goes out.

Two colleagues planning an outreach campaign at a whiteboard in an office

Handling no_results, failed Jobs and Partial Deliveries

A lead generation job ends in one of three states: succeeded, failed or no_results. Your agent should read the status from GET /jobs/{id} once the job leaves queued and running, and treat each terminal state as its own branch rather than a generic error. A no_results outcome means the filters matched nothing, so the agent should change a filter before it retries, because sending the identical request again repeats the same filters. A succeeded job can also return fewer rows than requested, because the requested count is a ceiling and not a promise.

  • no_results: do not retry the same request. Change one filter, for example remove a seniority level or add a location, then call POST /quote again before starting a new job.
  • LinkedIn jobs: check whether the option to include people whose email is not yet verified was left off. Leaving it off is the most common reason a LinkedIn list comes back much shorter than requested.
  • Apollo.io jobs: expect fewer rows than Apollo.io shows on screen, because Evascrape delivers only leads with a verified email address.
  • Google Maps jobs: if your target is above 500 places, split it across several search terms. The form states how many terms you need before any money is spent.
  • failed: record the job ID, call GET /balance, and stop for human review before any retry.

When a job finishes short, the agent should call GET /jobs/{id}/results, count the rows and compare that count with the requested number before it reports success. For example, if a job requested 5,000 records and returns 3,120 rows, the agent records the shortfall, passes the 3,120 rows to the next step and checks GET /balance to confirm the unused part was refunded. The agent should not silently start a second job to fill the gap. It should re-quote with POST /quote, adjust the filter that caused the shortage, and only then spend credits again.

Log every no_results and failed job with the exact filters used and the status returned. A person reviewing the log can then see which setting, such as a missing unverified-email option or an overly narrow location, caused the empty or short result.

Scale Your Lead Generation

Start extracting thousands of leads with Evascrape today. See the pay-per-lead pricing or compare credit pack pricing.

Get Started

Frequently Asked Questions

Data Compliance Disclaimer: Evascrape only extracts publicly available data in compliance with web standards. We prioritize ethical scraping practices and user privacy.
E

About Evascrape Team

Experts in B2B data extraction and sales automation. We help companies turn web-scale data into actionable lead lists through high-performance scraping technology.