Email Enrichment
Find a LinkedIn profile from a single email address via the Obert API.
Give Obert an email address, get back a LinkedIn profile. Works for both business and personal emails where Obert can resolve the person.
Prerequisites
- An API key for your workspace.
Create a lookup
POST to /api/enrichment/reverse-email-lookups with the email you want to enrich.
curl -X POST https://api.obert.io/api/enrichment/reverse-email-lookups \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"email": "satya.nadella@microsoft.com"
}'The response returns immediately with a job_id:
{
"job_id": "f4e2a1c8-9d3b-4e5f-8a7c-1b2d3e4f5a6b",
"status": "running",
"email": "satya.nadella@microsoft.com",
"candidates": [],
"skipped": false,
"created_at": "2026-05-14T12:30:00Z",
"completed_at": null
}If the result is already cached or the email is skipped, status is completed on this first response — no polling required.
Request fields
| Field | Type | Required | Description |
|---|---|---|---|
email | string | yes | The email address to enrich. |
name | string | no | Hint with the person's name if you already know it. Speeds up the lookup. |
company | string | no | Hint with the company name if you already know it. |
skip_personal_emails | boolean | no | Default true. When true, free-provider addresses (gmail, yahoo, outlook, etc.) are skipped and return status: "completed" with skipped: true. Set to false to attempt the lookup anyway. |
Poll the job
GET /api/enrichment/reverse-email-lookups/{job_id} until status is completed or failed.
curl https://api.obert.io/api/enrichment/reverse-email-lookups/f4e2a1c8-9d3b-4e5f-8a7c-1b2d3e4f5a6b \
-H "Authorization: Bearer YOUR_API_KEY"A completed response:
{
"job_id": "f4e2a1c8-9d3b-4e5f-8a7c-1b2d3e4f5a6b",
"status": "completed",
"email": "satya.nadella@microsoft.com",
"candidates": [
{
"linkedin_url": "https://www.linkedin.com/in/satyanadella",
"name": "Satya Nadella",
"headline": "Chairman and CEO at Microsoft"
}
],
"skipped": false,
"notes": null,
"created_at": "2026-05-14T12:30:00Z",
"completed_at": "2026-05-14T12:30:09Z"
}Polling cadence
Poll every 2–3 seconds. Most lookups finish in under 20 seconds.
Status values
| Status | Meaning |
|---|---|
running | The job is in progress. Poll again. |
completed | The job finished. Read candidates, skipped, or notes. |
failed | The job hit an unrecoverable error. Read error_message. |
Reading the result
| Combination | Meaning | What to do |
|---|---|---|
candidates has 1+ entries | A match was found. | Use the first entry — it's the highest-quality match. |
candidates is empty, skipped: true | The email is a personal provider and skip_personal_emails was true. | Set skip_personal_emails: false to attempt anyway, or skip. |
candidates is empty, notes is populated | No match found. | The note explains what was tried. |
status: "failed", error_message is set | The lookup errored out. | Inspect the error, retry after a delay. |
Caching
Lookups are cached for 30 days by email address. A second call with the same email returns instantly from cache and costs nothing. Misses are cached for 7 days, so Obert won't repeatedly attempt the same unknown email.
Limits
- Concurrency — one job per email at a time. Re-submitting the same email while a job is running returns the in-flight job.
- Personal emails — by default these are skipped to save cost. Set
skip_personal_emails: falseto override. - Job retention — completed jobs are retrievable for 24 hours by
job_id. Cached results live 30 days, but the job record itself expires sooner.
Example: batch enrichment
To enrich a list of emails, fire all POSTs first, then poll each job:
import httpx, asyncio
API_KEY = "YOUR_API_KEY"
BASE = "https://api.obert.io/api/enrichment/reverse-email-lookups"
async def enrich(client, email):
# 1. Submit
r = await client.post(BASE, json={"email": email},
headers={"Authorization": f"Bearer {API_KEY}"})
job = r.json()
if job["status"] != "running":
return job # cache hit or personal-email skip
# 2. Poll
while True:
await asyncio.sleep(2)
r = await client.get(f"{BASE}/{job['job_id']}",
headers={"Authorization": f"Bearer {API_KEY}"})
job = r.json()
if job["status"] != "running":
return job
async def main(emails):
async with httpx.AsyncClient(timeout=120) as client:
return await asyncio.gather(*(enrich(client, e) for e in emails))Submit all jobs first, then poll. Lookups run concurrently across jobs, so a batch of 50 emails finishes in roughly the same wall-clock time as a single email.
What's Next
- API Keys — generate the credentials you'll use.
- API Reference — full schema for every endpoint.