ObertObert
Integrations

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

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

FieldTypeRequiredDescription
emailstringyesThe email address to enrich.
namestringnoHint with the person's name if you already know it. Speeds up the lookup.
companystringnoHint with the company name if you already know it.
skip_personal_emailsbooleannoDefault 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

StatusMeaning
runningThe job is in progress. Poll again.
completedThe job finished. Read candidates, skipped, or notes.
failedThe job hit an unrecoverable error. Read error_message.

Reading the result

CombinationMeaningWhat to do
candidates has 1+ entriesA match was found.Use the first entry — it's the highest-quality match.
candidates is empty, skipped: trueThe 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 populatedNo match found.The note explains what was tried.
status: "failed", error_message is setThe 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: false to 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

On this page