Pre-Black Friday: 20% off, for as long as you stayEARLYBFon Solo and AgencyEnds Nov 28Start free

How-to guide

How to Enrich Candidate Records (With a People API)

Size the list for free, enrich by email or LinkedIn URL, read career history and email status, and keep the record cost in view.

Get your free API key →Free to start. No credit card. 1,000 records to spend whenever you like.

Candidate enrichment means re-reading each ATS record against a live data source and writing back what changed: current employer, title, a deliverable email and career history. This guide does it with LeadOcean's enrich endpoint, using real requests you can run today. It is shorter than the explainers on this topic because the work is four calls.

One framing note. Crustdata's guide splits enrichment into five data layers: contact data, employment history, skills, intent signals and employer firmographics (Crustdata blog, October 2026). That is a fair map. For the layer list in depth, read what data a candidate enrichment API should return. This page is the hands-on part.

TL;DR

  • Size your pool for free, then enrich each record with exactly one identifier: a LinkedIn URL, an email, a phone or a person_id.
  • Time: about 30 minutes to wire up, under a second per candidate after that.
  • Records: 1 per candidate found. A 404, a count and an empty page cost 0, so you pay only for matches.

Prerequisites

  • A LeadOcean API key in $LEADOCEAN_API_KEY. The Free plan gives 1,000 records for life, no card (sign up). Free keys get both the search and enrich scopes.
  • curl and jq.
  • A CSV export of your ATS with at least one identifier per candidate. The examples use placeholders: jane.doe@acme.com and https://www.linkedin.com/in/janedoe-example.

Step 1: Size the list before you spend anything

Count first. A count is free, so test your assumptions on it instead of on your record balance. Send count=true as a query parameter, not in the JSON body, with limit 1. meta.total is capped at 100,000.

bash
curl -s -X POST "https://api.leadocean.io/v1/people/search?count=true" \
  -H "x-api-key: $LEADOCEAN_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "keywords": ["Kubernetes"],
    "jobFunction": ["Engineering"],
    "country": ["GB"],
    "limit": 1
  }' | jq '.meta'

Filters are arrays of strings, and enum values are exact strings. Check a value with GET /v1/enums or the MCP tool leadocean_list_enum_values before you guess.

Step 2: Enrich by the best identifier you hold

Pick one identifier per call. Sending two is a 400. Prefer a LinkedIn URL, then a work email, then a phone. Set reveal_email to true to get addresses and phones back. It costs no extra record.

bash
curl -s -X POST https://api.leadocean.io/v1/people/enrich \
  -H "x-api-key: $LEADOCEAN_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"linkedin_url": "https://www.linkedin.com/in/janedoe-example", "reveal_email": true}'

Swap the body for {"email": "jane.doe@acme.com", "reveal_email": true} when you only hold an address. A person we hold nothing for is a 404 and costs nothing. Records with no identifier at all go through the search route in Step 4.

Step 3: Read the fields that matter

Three parts of the response decide what you write back to the ATS. Use jq to pull them.

bash
curl -s -X POST https://api.leadocean.io/v1/people/enrich \
  -H "x-api-key: $LEADOCEAN_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"email": "jane.doe@acme.com", "reveal_email": true}' \
  | jq '{
      employer_now: .data.contact_data.contact_current_experiences,
      email_type: .data.contact_data.email_type,
      email_status: .data.contact_data.email_status,
      past_roles: .data.resume_data.experiences,
      education: .data.resume_data.educations
    }'

Since October 2026 the response carries career history: past roles, education, certifications, awards and more, each when held. Past employers have no company_id yet, so match them by name. The current role is in contact_current_experiences and is not repeated in resume_data.experiences.

Check email_type before you send. work is the current employer. work_other is a former employer and the likeliest to bounce. We give no refund for bounced emails.

Step 4: Records with no identifier

About a fifth of people have no LinkedIn URL, and many ATS rows hold only a name and a company. Search narrows to a short list. Search rows are a thin projection, so enrich the right row afterwards by person_id.

bash
curl -s -X POST https://api.leadocean.io/v1/people/search \
  -H "x-api-key: $LEADOCEAN_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"q": "Jane Doe", "companyName": ["Acme"], "country": ["GB"], "limit": 3}'

Each row returned is a record, so keep limit at 3. Take meta.source_ids.person_id from the row that matches on company and city, then enrich with {"person_id": "<id>", "reveal_email": true}. There is no name plus domain lookup. If two rows fit, pick neither and show a human the short list.

Step 5: Batch a refresh without tripping the rate limit

Rate limits are per plan: 5 requests a second on Free, 200 on Agency. A short loop with a sleep stays under the Free limit. Log every 404 so you can retry it by another identifier.

bash
while IFS= read -r email; do
  code=$(curl -s -o "out/${email}.json" -w "%{http_code}" \
    -X POST https://api.leadocean.io/v1/people/enrich \
    -H "x-api-key: $LEADOCEAN_API_KEY" \
    -H "Content-Type: application/json" \
    -d "{\"email\": \"${email}\", \"reveal_email\": true}")
  echo "${email},${code}" >> results.csv
  sleep 0.25
done < emails.txt

For thousands of rows, the Exports page in the app (app.leadocean.io) or POST /v1/exports is the better tool. Exports take up to 50,000 rows a request and 500,000 a day. Bulk CSV export is not on Free. Solo is $97 a month and Agency is $497 a month (pricing).

Step 6: Write back, and keep the old value

Never overwrite an ATS field blindly. Map each enriched field to a destination, and keep the previous value in an audit column so a recruiter can undo a bad match.

json
{
  "current_employer": "data.contact_data.contact_current_experiences[0]",
  "work_email": "data.contact_data.contact_emails[type=work].email",
  "email_status": "data.contact_data.email_status",
  "career_history": "data.resume_data.experiences",
  "enriched_at": "data.meta.fetched_at"
}

Three rules keep the data honest. Write an email only when email_status is verified or catch_all_valid. Flag a candidate as "moved" when the current employer differs from your ATS value, and route those to a recruiter. Treat career history as context for a conversation, not as a screening score.

Also check consent rules for your region before you enrich candidates who never applied to you. Crustdata's guide raises GDPR and CCPA obligations as a gap most teams miss (Crustdata blog, October 2026). We make no compliance claims here. Ask your counsel.

What you get

Sizing first. The MCP tool leadocean_count_leads ran on 2026-10-21 with these filters and spent 0 records:

text
keywords: ["Kubernetes"], jobFunction: ["Engineering"], country: ["GB"]
-> total 11,688 (mailable), totalAll 31,184, distinctCompanies 3,896, credits 0

The 11,688 counts only verified, catch_all_valid and catch_all addresses, which is the tool's default. The 31,184 is everyone matching the same filters with no email condition.

The enrich response, as documented in the API reference (response shape from the API reference, placeholder values only):

json
{
  "success": true,
  "data": {
    "profile_data": { "profile_full_name": "Jane Doe", "profile_headline": "Platform Engineer at Acme" },
    "contact_data": {
      "has_email": true,
      "email_status": "verified",
      "email_type": "work",
      "contact_emails": [
        { "email": "jane.doe@acme.com", "type": "work", "status": "verified" }
      ],
      "contact_phones": [{ "phone": "+14155550133", "type": "mobile" }],
      "contact_current_experiences": [
        { "company_name": "Acme", "company_domain": "acme.com", "job_title": "Platform Engineer", "start_date": "2024-03-01" }
      ]
    },
    "resume_data": {
      "experiences": [
        { "company_name": "Globex", "job_title": "DevOps Engineer", "start_date": "2020-01-01", "end_date": "2024-02-01", "job_seniority": { "job_level": "Staff" } }
      ]
    },
    "meta": { "schema": "leadocean.person.v1", "fetched_at": "2026-10-01" }
  },
  "meta": { "credits": 1 }
}

Write fetched_at back to the ATS with the rest. The dataset refreshes monthly, and a date lets you skip records you enriched recently.

Troubleshooting

ErrorCauseFix
401Missing, wrong or revoked keyCheck the x-api-key header and that $LEADOCEAN_API_KEY is set in this shell.
400 on enrichMore than one identifier sentSend exactly one of linkedin_url, email, phone or person_id.
404 on enrichNobody matched, or nothing to enrich byFree. Try another identifier or use the search route.
429Above the plan's rate limit (5 a second on Free, 200 on Agency)Wait for Retry-After, then retry. Add a sleep to the loop.
402The 1,000 Free records are spentThey do not reset. Move to a paid plan (pricing).
Empty search dataA filter value is not an exact enum stringCheck values with GET /v1/enums or leadocean_list_enum_values.

FAQ

What is candidate enrichment?

It is adding current, verifiable data to a candidate record: employer, title, contact points and career history. It differs from resume parsing, which only reads what the candidate submitted.

How many records does enriching one candidate cost?

One, when we find the person. A 404, a count and an empty page cost nothing. Search rows cost one record each, so keep limit small.

How often should I re-enrich?

The dataset is refreshed monthly, and each record carries its own fetched_at date. Re-enrich the pipeline you are actively working, and skip records stamped within the last month.

Can I run this from an AI agent?

Yes. The hosted MCP server at https://api.leadocean.io/mcp signs in with OAuth and has 13 tools. Counting tools are free. Search and get tools spend records exactly as the REST calls do.

Where can I compare providers?

Start with the best candidate enrichment APIs for recruiting platform builders. The term itself is defined in the enrichment glossary entry. More guides are on the blog.

Enrich your first 1,000 candidate records free

Free to start. No credit card. 1,000 records to spend whenever you like.

Get your free API key →