GET /v2/people/reverse takes one email address and returns the person who holds it: name, LinkedIn URL, title and employer. It costs 1 record per call. A 404, where nobody holds the address, costs nothing.
This page is the developer reference. For what the data is and where it is thin, read Reverse email lookup. Agents call the same lookup through the leadocean_get_lead MCP tool.
Quick start (curl)
Create a free account, copy a key with the enrich scope and export it as LEADOCEAN_API_KEY. The address below is a placeholder.
curl -s -G "https://api.leadocean.io/v2/people/reverse" \
--data-urlencode "email=jane.doe@acme.com" \
-H "x-api-key: $LEADOCEAN_API_KEY"The endpoint also answers POST with a JSON body:
curl -s -X POST "https://api.leadocean.io/v2/people/reverse" \
-H "x-api-key: $LEADOCEAN_API_KEY" -H "content-type: application/json" \
-d '{ "email": "jane.doe@acme.com" }'Node.js (fetch)
const url = new URL("https://api.leadocean.io/v2/people/reverse");
url.searchParams.set("email", "jane.doe@acme.com");
const res = await fetch(url, {
headers: { "x-api-key": process.env.LEADOCEAN_API_KEY },
});
if (res.status === 404) {
console.log("Nobody holds this address. Not billed.");
} else if (!res.ok) {
throw new Error(`${res.status} ${(await res.json()).error?.message}`);
} else {
const { data } = await res.json();
console.log(data.full_name, data.job_title, data.company_domain);
console.log(data.matched_in_record ? "Exact address on record" : "Matched upstream");
}Python (requests)
import os
import requests
r = requests.get(
"https://api.leadocean.io/v2/people/reverse",
params={"email": "jane.doe@acme.com"},
headers={"x-api-key": os.environ["LEADOCEAN_API_KEY"]},
timeout=10,
)
if r.status_code == 404:
print("Nobody holds this address. Not billed.")
else:
r.raise_for_status()
data = r.json()["data"]
print(data["full_name"], data["job_title"], data["company_domain"])Request
Send exactly one of email or phone, never both. The key needs the enrich scope.
| Parameter | Type | Required | Meaning |
|---|---|---|---|
email | string (email) | One of the two | The address to look up. |
phone | string | One of the two | A number in any common format. Same endpoint, same price. See Reverse Phone Lookup API. |
There is no lookup by name and company. Search by company domain first, then enrich by person_id.
Response
Response shape from the API reference. Every value below is a placeholder.
{
"success": true,
"data": {
"person_id": "1234567",
"person_group_id": "1234567",
"linkedin_url": "https://www.linkedin.com/in/jane-doe",
"full_name": "Jane Doe",
"job_title": "Finance Manager",
"company_name": "Acme",
"company_domain": "acme.com",
"query": { "email": "jane.doe@acme.com" },
"matched": { "email": "jane.doe@acme.com", "status": "verified",
"verified_batch_date": "2026-09-14", "type": "work" },
"matched_in_record": true
},
"meta": { "source": "leadocean", "key_hash": "...", "schema": "leadocean.contact.reverse.v2",
"stability": "stable", "credits": 1 }
}| Field | Meaning |
|---|---|
data.person_id | The record that answered. Send it back as contact_id to the other v2 endpoints. |
data.person_group_id | The person across duplicate records. Compare it, never look it up. |
data.linkedin_url, full_name, job_title, company_name, company_domain | The identity. Each can be null. |
data.query | The address you sent, as we compared it. |
data.matched | The address as the record holds it, with its own type, status and check date. Null when the record does not echo it back. |
data.matched_in_record | true when the record lists the exact address. false means a match upstream, not "nobody found". |
meta.credits | Records counted for this call. |
This is the one v2 endpoint that sends meta. It returns the identity only. For the full profile, call POST /v1/people/enrich, which costs the same and reads the same cached record.
Errors
Every failure is { "success": false, "error": { "message": "...", "details": ... } }. Statuses are from the limits docs, September 2026.
| Status | Meaning | What to do |
|---|---|---|
| 400 | Validation failed. details names the fields. | Send exactly one of email or phone. |
| 401 | Missing, wrong or revoked key. | Check the x-api-key header. |
| 402 | Free plan: the 1,000 records are spent. They do not reset. | Upgrade. Not billed. |
| 403 | The key lacks the enrich scope, or the account is suspended. | Create a key with the scope. |
| 404 | No person holds this address. | Stop. It costs nothing. |
| 429 | Over 100 requests a second, or 1 a minute on a paid account past its records. | Wait for Retry-After, then retry. Not billed. |
| 503 | The store behind the lookup is unreachable (code: no_source). | Retry with backoff. Never billed. |
Rate limits and cost
A call that returns a person is 1 record, and a 200 with sparse fields still costs 1. A 404, 402, 429 or 503 costs nothing.
The limit is 100 requests per second per key, on every plan. Free is 1,000 records, one-off, no card. Pro is $499 a month, flat, with no per-record price inside fair use. See pricing.
Past a paid account's records, calls are paced to 1 a minute and X-Quota-State: over-limit appears in the headers. Alert on that header.
Bulk
POST /v1/exports is the bulk path, but it is built from filters. The docs describe no way to upload a list of emails to it. A list of addresses means one call here per address.
If you want people rather than a lookup, export by filter instead. One row is 1 record whatever the columns, up to 50,000 rows per export. Email columns are numbered: you request email_N_address and email_N_status, and the CSV comes back as email_1_address, email_2_address and so on.
curl -X POST https://api.leadocean.io/v1/exports \
-H "x-api-key: $LEADOCEAN_API_KEY" -H "content-type: application/json" \
-d '{
"name": "Finance managers with an email",
"filters": { "title": ["Finance Manager"], "country": ["US"], "hasEmail": true },
"limit": 5000,
"columns": ["person_id", "profile_url", "email_N_address", "email_N_status"],
"caps": { "emails": 2 }
}'The reply is a 202 with a job id. Poll GET /v1/exports/{id}, then fetch /v1/exports/{id}/download.
FAQ
Does a lookup that finds nobody cost a record?
No. A 404 costs nothing. A 200 that names a person costs 1 record, even when most fields are null.
Can I look up by name and company?
No. Search people by company domain and title, then call POST /v1/people/enrich with the person_id.
Can an agent call it without an API key?
Yes. The MCP server at https://api.leadocean.io/mcp signs in with OAuth 2.1, so no key is pasted. Pass email to leadocean_get_lead, 1 record, and a miss is free. To pick a tool first, read the best reverse email lookup tools.
Get your reverse email lookup API key free
Free to start. No credit card. 1,000 records to spend whenever you like.
Get your free API key →