Enrichment
The enrichment endpoints cover three capabilities:
- Lookup — Find a single person or company from the open data index when you already know their email, LinkedIn URL, or domain. Returns full profile data instantly.
- Enrich — Fill missing fields (emails, phone numbers) on contacts you already have in your workspace by running them through multiple data providers in sequence (waterfall enrichment).
- Verify — Check if an email address is deliverable before sending.
Two billing buckets
Enrichment endpoints split into two buckets that bill differently. The split matters because graph8 owns the cost on one side and pays a third-party provider on the other.
Index Lookups - free on both plans
These endpoints hit graph8’s owned indexes (OpenSearch + ClickHouse). Single-record, instant, no waterfall.
| Endpoint | PAYG | Platform |
|---|---|---|
POST /enrichment/lookup/person | Free (within 5 rps) | Free (within 5 rps) |
POST /enrichment/lookup/company | Free (within 5 rps) | Free (within 5 rps) |
POST /enrichment/verify-email (internal validator) | Free (within 5 rps) | Free (within 5 rps) |
Waterfall Enrichment - always credits
These endpoints chain through external providers (Prospeo, Dropcontact, Cognism, and others). graph8 pays the provider per record - so credits apply on both PAYG and Platform.
| Endpoint | PAYG | Platform |
|---|---|---|
POST /enrichment/enrich (waterfall) | Variable per record | Variable per record |
| External email verifiers (Kickbox, ZeroBounce) | Per-verification | Per-verification |
See Pricing for the full plan comparison.
Person Lookup
POST /enrichment/lookup/person
Instant person lookup. Provide at least one of: email, linkedin_url, or first_name + last_name + company_domain.
Pricing: Free on both PAYG and Platform within the 5 rps cap (graph8-owned index data).
Request Body
| Field | Type | Required | Description |
|---|---|---|---|
email | string | No* | Work email address |
linkedin_url | string | No* | LinkedIn profile URL |
first_name | string | No* | First name (requires last_name + company_domain) |
last_name | string | No* | Last name (requires first_name + company_domain) |
company_domain | string | No* | Company domain (requires first_name + last_name) |
*At least one lookup strategy is required.
Example
curl -X POST "https://be.graph8.com/api/v1/enrichment/lookup/person" \ -H "Authorization: Bearer $API_KEY" \ -H "Content-Type: application/json" \ -d '{"email": "jane@acme.com"}'response = requests.post( f"{BASE_URL}/enrichment/lookup/person", headers=HEADERS, json={"email": "jane@acme.com"})Response
{ "data": { "found": true, "confidence": 0.95, "data": { "first_name": "Jane", "last_name": "Doe", "job_title": "VP of Sales", "company": "Acme Inc", "linkedin_url": "https://linkedin.com/in/janedoe", "work_email": "jane@acme.com" } }}Company Lookup
POST /enrichment/lookup/company
Instant company lookup by domain or name.
Pricing: Free on both PAYG and Platform within the 5 rps cap (graph8-owned index data).
Request Body
| Field | Type | Required | Description |
|---|---|---|---|
domain | string | No* | Company domain |
name | string | No* | Company name |
*At least one of domain or name is required.
Example
curl -X POST "https://be.graph8.com/api/v1/enrichment/lookup/company" \ -H "Authorization: Bearer $API_KEY" \ -H "Content-Type: application/json" \ -d '{"domain": "acme.com"}'response = requests.post( f"{BASE_URL}/enrichment/lookup/company", headers=HEADERS, json={"domain": "acme.com"})Response
{ "data": { "found": true, "confidence": 0.98, "data": { "name": "Acme Inc", "domain": "acme.com", "industry": "Technology", "employee_count": 500, "annual_revenue": "$50M-$100M" } }}Start Enrichment Job
POST /enrichment/enrich
Start an async waterfall enrichment job. The job runs across multiple data providers and returns a job_id you can poll for results.
Returns 202 Accepted.
Pricing: Credits apply on both PAYG and Platform - waterfall enrichment hits third-party providers (Prospeo, Dropcontact, Cognism, etc.) with per-record cost. Cost varies by which providers are configured in fields_config.
Request Body
| Field | Type | Required | Description |
|---|---|---|---|
contact_ids | integer[] | Yes | Contact IDs to enrich |
list_id | integer | Yes | List ID the contacts belong to |
fields_config | object | No | Fields to enrich and provider order (see below) |
fields_config example:
{ "work_email": ["prospeo", "dropcontact"], "direct_phone": ["cognism", "lusha"]}If omitted, defaults to {"work_email": ["prospeo", "dropcontact"]}.
Example
curl -X POST "https://be.graph8.com/api/v1/enrichment/enrich" \ -H "Authorization: Bearer $API_KEY" \ -H "Content-Type: application/json" \ -d '{ "contact_ids": [101, 102, 103], "list_id": 5, "fields_config": {"work_email": ["prospeo", "dropcontact"]} }'response = requests.post( f"{BASE_URL}/enrichment/enrich", headers=HEADERS, json={ "contact_ids": [101, 102, 103], "list_id": 5, "fields_config": {"work_email": ["prospeo", "dropcontact"]} })job_id = response.json()["data"]["job_id"]Response
{ "data": { "job_id": "abc-123-def", "status": "queued" }}Poll Enrichment Job
GET /enrichment/jobs/{job_id}
Check the status of an enrichment job.
Status Values
| Status | Description |
|---|---|
queued | Job is waiting to start |
running | Enrichment in progress |
completed | All contacts processed |
failed | Job failed |
Example
curl "https://be.graph8.com/api/v1/enrichment/jobs/abc-123-def" \ -H "Authorization: Bearer $API_KEY"response = requests.get( f"{BASE_URL}/enrichment/jobs/{job_id}", headers=HEADERS)status = response.json()["data"]["status"]Response
{ "data": { "job_id": "abc-123-def", "status": "completed" }}Verify Email
POST /enrichment/verify-email
Verify a single email address and check its deliverability status.
Pricing: Free on both PAYG and Platform within the 5 rps cap when graph8’s internal validator can answer. Calls that fall through to third-party verifiers (Kickbox, ZeroBounce) consume 1 credit per verify on both plans.
Request Body
| Field | Type | Required | Description |
|---|---|---|---|
email | string | Yes | Email address to verify |
Example
curl -X POST "https://be.graph8.com/api/v1/enrichment/verify-email" \ -H "Authorization: Bearer $API_KEY" \ -H "Content-Type: application/json" \ -d '{"email": "jane@acme.com"}'response = requests.post( f"{BASE_URL}/enrichment/verify-email", headers=HEADERS, json={"email": "jane@acme.com"})Response
{ "data": { "email": "jane@acme.com", "status": "valid", "sub_status": null, "is_valid": true }}| Status | Description |
|---|---|
valid | Deliverable email address |
invalid | Undeliverable |
catch-all | Domain accepts all addresses |
unknown | Could not determine |
Waterfall Credit Matrix
Waterfall enrichment (POST /enrichment/enrich) runs each contact / company through a sequence of 14 third-party providers. Only successful provider hits charge credits — if no provider matches, the entire run is free. Cached results (same record + field, within the 7-day cache TTL) also return for free.
Per-provider hit cost
| Provider | Returns | Phone | Other | |
|---|---|---|---|---|
| Apollo | Email, phones, title, seniority, company, LinkedIn | 1 | 2 (direct), 2 (mobile) | LinkedIn 1, title 1 |
| Hunter | Email, domain | 1 | — | Domain 1, verify 2 |
| Prospeo | Email, direct phone | 1 | 2 | — |
| DropContact | Email, phone, company | 1 | 2 (direct), 2 (mobile) | — |
| Lusha | Email, direct + mobile phone | 1 | 2 (direct), 2 (mobile) | — |
| Icypeas | Email, phone, LinkedIn, title, company | 1 | 1 | Domain 0.5, LinkedIn 1, company 1 |
| RocketReach | Email, phone, company, LinkedIn | 2 | 2 | 2 |
| LeadMagic | Email, mobile, LinkedIn, company, industry, funding | 0.5 | 3 (mobile) | LinkedIn 3, company 2, funding 2.5 |
| Clearbit | Company domain, industry, headcount, title | — | — | 2 |
| BuiltWith | Company tech stack, domain relationships | — | — | 2 per action |
| Semrush | Traffic, keywords, competitors, social | — | — | 1 per result returned |
| graph8 internal | Mashup contacts + companies | — | — | 2 per lookup (free if using org-owned API key) |
Email verifier add-on cost
Run after a provider returns an email; if invalid the waterfall continues to the next provider.
| Provider | Verify cost | Notes |
|---|---|---|
| LeadMagic | 0.5 | Cheapest option |
| EmailListVerify | 1 | Dedicated verifier |
| ZeroBounce | 1 (verify) / 20 (find_email) | Find_email is the expensive outlier |
| Icypeas | 1 | Also a data-finding provider |
| Hunter | 2 | Slightly higher than competitors |
Default waterfall order
Email (work_email / contact_email): Apollo → Hunter → Prospeo → DropContact → Lusha → Icypeas → RocketReach → LeadMagic → (optional verifier: EmailListVerify / ZeroBounce).
Direct phone: Apollo → Prospeo → DropContact → Lusha.
Mobile phone: Apollo → DropContact → Lusha → LeadMagic.
Company domain: Clearbit → Apollo → Hunter.
Industry / size: Clearbit only.
Order is configurable per org via the in-app waterfall editor — enrichment_configs row drives the live sequence.
When credits are (and aren’t) charged
| Scenario | Charged? |
|---|---|
| Provider returns a match | Yes — cost of that field for that provider |
| Provider returns no match | No — waterfall moves on |
| All providers exhausted, no match | No charge at all |
| Cached result returned (same field, same record, within 7d TTL) | Free |
| Provider call uses your own API key (configured in the org) | Free |
| Record skipped because run conditions weren’t met | Free |
| Email verifier validates an email as valid | Verifier cost absorbed; no extra charge |
| Email verifier rejects an email | Verifier cost charged; waterfall continues for free |
Apollo phone-reveal flags
Apollo offers two add-on flags that cost extra credits beyond the base hit:
| Flag | Extra credits |
|---|---|
reveal_phone_numbers=true | +3 to +15 (depends on number type) |
reveal_personal_emails=true | +1 to +7 |
Both flags default to off to avoid surprise spend. Enable per call when you specifically need direct phone or personal email.
Credit hold + batch charge
For large enrichment jobs the system pre-holds a credit budget then charges in batches:
- Job start: hold
total_records × max_per_record × 1.25(25% safety buffer). - Per record completion: pending credits accumulate.
- Every 100 records: pending → charged from the hold.
- Job complete: remaining pending charged, unused hold released back to balance.
Use GET /enrichment/jobs/{job_id} to monitor pending vs charged credits mid-run.
Pricing examples
| Scenario | Cost |
|---|---|
| Enrich a contact, Apollo finds work email on first hit | 1 credit |
| Enrich a contact, no provider matches | 0 credits |
| Enrich a contact, need both work email + direct phone, Apollo matches both | 1 + 2 = 3 credits |
| Re-enrich the same contact within 7 days | 0 credits (cache hit) |
| Bulk enrich 1,000 contacts, ~70% find rate, avg 1.2 credits per hit | ~840 credits |