Bulk jobs: poll or webhook
The sync People Bulk Enrich and Company Bulk Enrich answer up to 50 items in the request. For bigger lists, send them as one job of up to 10,000 rows. It runs the same pipeline as the CSV enrichment in the app: a database match, the stored email reveal, then the live email finder for rows with a name plus a company or domain. Bulk email verification works the same way.
| Job | Create | Read | Results |
|---|---|---|---|
| People enrichment | Create People Enrichment | Get Enrichment | Get Enrichment Results |
| Company enrichment | Create Company Enrichment | Get Enrichment | Get Enrichment Results |
| Email verification | Create Email Verification | Get Email Verification | Get Email Verification Results |
1. Create the job
The rows are the items of the sync bulk enrich, each with an optional identifier of yours that comes back on its
result. The answer is 202 at once, with the job and Location: /v2/jobs/{id}. There is no cost estimate and no
start call: the job is already running.
curl --request POST \
--url https://api.getprospect.com/v2/people/enrichments \
--header 'Content-Type: application/json' \
--header 'x-api-key: YOUR_API_KEY' \
--data '
{
"name": "Webinar leads",
"data": [
{
"identifier": "crm-1",
"linkedin_url": "https://www.linkedin.com/in/jane-doe"
},
{
"identifier": "crm-2",
"first_name": "John",
"last_name": "Smith",
"company_domain": "acme.com"
},
{
"identifier": "crm-3",
"email": "ann@globex.com"
}
],
"enrich_email": true,
"enrich_phone": false
}
'Send an Idempotency-Key and retry with the same key on a network error: the retry answers the job it created
(Idempotent-Replayed: true) instead of starting a second one. A row with no usable lookup (a LinkedIn URL, an email,
or first and last name with a company domain or name) does not fail the request; it comes back as invalid.
2. Wait for it
Poll Get Job (or the job's own resource) until status is done, failed or cancelled.
While it runs, the answer carries Retry-After in seconds: wait that long between polls. Polls are free and do not
count against the per-minute rate limit.
Or subscribe once with Create Webhook and "events": ["job.finished"]. Every
enrichment and email verification of your workspace then sends one signed job.finished event when it ends, with
the job and its links in data. The event carries no person data: read the results with your key.
{
"id": "evt_6702a1b2c3d4e5f607182931",
"type": "job.finished",
"api_version": "2026-10",
"created_at": "2026-10-18T12:01:10.000Z",
"webhook_id": "6702a1b2c3d4e5f607182930",
"data": {
"job": { "id": "6702a1b2c3d4e5f607182901", "type": "csv_enrichment", "status": "done", "...": "..." },
"links": {
"self": "/v2/enrichments/6702a1b2c3d4e5f607182901",
"job": "/v2/jobs/6702a1b2c3d4e5f607182901",
"results": "/v2/enrichments/6702a1b2c3d4e5f607182901/results"
}
}
}Jobs created through the API send no notifications in the app (no bell, no "file ready" email): the job resource and
job.finished are the only signals.
3. Read the results
Page Get Enrichment Results with limit (up to 500) and send
metadata.pagination.next_cursor back as cursor until it is null. There is one item per request row, in request
order, each with your identifier and a status:
matched- found;personorcompanyis the same object the sync bulk enrich returns, andmatched_bysays how.matched_by: "email_finder"means the live finder worked out the email from the name and company; that person carries what the row said plus the email.not_matched- nothing found.invalid- the row had no usable lookup;error.messagesays what to send.failed- a lookup errored on our side; send the row again.not_processed- the job ran out of credits before reaching the row; send it again after a top-up.
Only valid emails are returned. To get the whole result in one download, the job's links.jsonl (one result item per
line) and links.csv (your rows with the enrichment columns, as the app exports it) are there once it completed.
Results are kept for 30 days (results_expires_at); after that the results route answers 410.
const BASE = "https://api.getprospect.com";
const headers = { "x-api-key": process.env.GETPROSPECT_API_KEY, "content-type": "application/json" };
const created = await fetch(`${BASE}/v2/people/enrichments`, {
method: "POST",
headers: { ...headers, "Idempotency-Key": "crm-sync-2026-10-18" },
body: JSON.stringify({ data: rows }),
}).then((r) => r.json());
const id = created.data.id;
let job;
do {
const res = await fetch(`${BASE}/v2/jobs/${id}`, { headers });
job = (await res.json()).data;
if (!["done", "failed", "cancelled"].includes(job.status)) {
await new Promise((r) => setTimeout(r, 1000 * Number(res.headers.get("retry-after") || 2)));
}
} while (!["done", "failed", "cancelled"].includes(job.status));
const results = [];
let cursor = null;
do {
const q = new URLSearchParams({ limit: "500", ...(cursor && { cursor }) });
const page = await fetch(`${BASE}/v2/enrichments/${id}/results?${q}`, { headers }).then((r) => r.json());
results.push(...page.data);
cursor = page.metadata.pagination.next_cursor;
} while (cursor);Credits and limits
- People enrichment: 1 email credit per person whose email is revealed and your workspace does not already own
(the live finder charges only when it finds a valid email), 1 phone credit per mobile number found with
enrich_phone. Re-enriching people you already revealed is free. Creating the job needs at least one email credit (and one phone credit withenrich_phone); the job charges as it runs and stops early, keeping what it found, if the balance runs out. - Company enrichment: free.
- Email verification: 1 email verification credit per unique valid-syntax address, charged when the job starts; the unverified remainder is refunded on cancel or failure. With a short balance the job waits and starts once the credits are there.
- Limits: 10,000 rows per job; at most 5 active enrichments per workspace (a sixth answers
409 limit_reached). Creating a job spends 1 unit of thejobsrate limit (2 per second, 30 per minute, 1,000 per day): on429, waitRetry-Afterand retry. See Credits and rate limits.
Recipe: bulk email finder
There is no separate bulk finder: a people enrichment with enrich_email is one. Send first name, last name and the
company domain (or name), then read person.email from the results.
{
"data": [
{ "identifier": "lead-1", "first_name": "Sundar", "last_name": "Pichai", "company_domain": "google.com" },
{ "identifier": "lead-2", "first_name": "Patrick", "last_name": "Collison", "company_name": "Stripe" }
]
}Rows we hold come back matched with their stored valid email; for the others the live finder works out the address
(matched_by: "email_finder"). You pay only for emails found.
