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

How-to guide

How to run a free reverse email search (with one API call)

Send an email address, get the person who holds it. Real requests, the response shape and what each call costs.

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

The free reverse email search tool most developers want is a single request: email in, person out. LeadOcean's Free plan gives you 1,000 records, one-off, no card, so you can run this for real. This guide shows the query, the output and the cost of each call.

TL;DR

  • One GET /v2/people/reverse call returns the name, title, employer and LinkedIn URL behind an email address.
  • It takes about 10 minutes for one address. A loop over a list takes about 20.
  • Each reverse call costs 1 record, including one that finds nobody. The 1,000 Free records cover 1,000 lookups.

Prerequisites

  • A LeadOcean account on the Free plan: start free. Work email required, no card.
  • An API key from the app, exported as $LEADOCEAN_API_KEY. Free keys carry the enrich scope this endpoint needs.
  • curl for steps 1 to 3. Python 3 and the requests package for step 4.
  • An email address to test. Use one you have the right to look up.
bash
export LEADOCEAN_API_KEY="paste-your-key-here"

Step 1: Run the reverse lookup

Send one email to /v2/people/reverse with your key in the x-api-key header. Send exactly one of email or phone, never both.

bash
curl -s -G "https://api.leadocean.io/v2/people/reverse" \
  --data-urlencode "email=jane.doe@acme.com" \
  -H "x-api-key: $LEADOCEAN_API_KEY"

--data-urlencode matters. A plus tag such as jane+sales@acme.com breaks without it, because + reads as a space in a query string.

POST works too, with a JSON body of {"email": "jane.doe@acme.com"}. Use POST if your address list comes from a system that already speaks JSON.

Step 2: Read the response

The response is a thin identity block, not the full profile. This is the shape from the API reference, with placeholder values.

json
{
  "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", "schema": "leadocean.contact.reverse.v2",
            "stability": "stable", "credits": 1 }
}

Three fields decide whether you trust the answer:

  • matched.status is the deliverability of the address you sent. Read it before you send anything.
  • matched.type says whether it is a work or a personal address.
  • matched_in_record is true when the record lists your exact address. false means we matched the person on it upstream but the record does not carry it. It does not mean nobody was found.

Every identity field can be null, so a real match can arrive without a LinkedIn URL or a title.

Step 3: Pull the full profile only when it is worth it

The reverse call names the person. To get everything else, call enrich with the same email.

bash
curl -s -G "https://api.leadocean.io/v1/people/enrich" \
  --data-urlencode "email=jane.doe@acme.com" \
  -d "reveal_email=true" \
  -H "x-api-key: $LEADOCEAN_API_KEY"

Enrich costs one record when it finds the person and nothing when it does not. reveal_email=true returns every email and phone we hold for the person, at no extra charge. Without it, you get the profile and flags (has_email, email_status, email_type) but not the addresses.

Run reverse first when you only need to know who someone is. Skip it and go straight to enrich when you want the profile anyway. Doing both on one address spends 2 records.

Step 4: Run it over a list

The docs describe no batch version of the reverse endpoint, so a list means one call per address. This script reads emails.txt (one address per line) and writes matches.csv.

python
import csv, os, sys, time, requests

KEY = os.environ["LEADOCEAN_API_KEY"]
URL = "https://api.leadocean.io/v2/people/reverse"
COLS = ["email", "full_name", "job_title", "company_domain",
        "status", "type", "matched_in_record", "person_group_id"]

def lookup(email):
    for _ in range(5):
        r = requests.get(URL, params={"email": email},
                         headers={"x-api-key": KEY}, timeout=30)
        if r.status_code == 429:                      # over your plan's rate (5 a second on Free)
            time.sleep(int(r.headers.get("Retry-After", "1")))
        elif r.status_code == 503:                    # store unreachable, never billed
            time.sleep(5)
        else:
            return r
    r.raise_for_status()

with open("emails.txt") as src, open("matches.csv", "w", newline="") as out:
    w = csv.writer(out)
    w.writerow(COLS)
    for line in src:
        email = line.strip()
        if not email:
            continue
        r = lookup(email)
        if r.status_code == 404:                      # nobody holds it, still 1 record
            w.writerow([email] + [""] * (len(COLS) - 1))
            continue
        if r.status_code == 402:
            sys.exit("Out of records. Free records do not reset.")
        r.raise_for_status()
        d = r.json()["data"]
        m = d.get("matched") or {}
        w.writerow([email, d.get("full_name"), d.get("job_title"),
                    d.get("company_domain"), m.get("status"), m.get("type"),
                    d.get("matched_in_record"), d.get("person_group_id")])

Dedupe on person_group_id, not person_id. The same person can return under different person_id values when you reach them through different contact points. Two records with the same person_group_id are one person as far as our de-duplication knows.

Before a large run, read GET /v1/account. It is free and shows records used and remaining.

Test a sample first. The docs publish no match rate, so take 100 addresses from your list and count the rows that come back with a name. LeadOcean holds 693M people and 366M of them have an email (measured 2026-09-23), a little over half. An address outside that set finds nobody.

Each reverse call costs one record whatever it returns. A sparse match still counts, and so does a miss. If your list has many addresses we may not hold, run it through /v1/people/enrich instead: an enrich that finds nobody is a 404 and costs nothing.

Step 5: Ask an AI agent instead

If you work in Claude or another MCP client, skip the script. Connect https://api.leadocean.io/mcp (OAuth sign-in, no key pasted) and ask in plain language.

text
Who is jane.doe@acme.com? Give me her title, employer and LinkedIn URL.

The agent calls leadocean_get_lead with email. That tool returns the full person record, so it behaves like the enrich call rather than the thin reverse block. It costs 1 record when it finds someone and nothing when it finds nobody.

What you get

The status values are the part worth knowing before you act on a match. This is the live output of the free leadocean_list_enum_values MCP tool for email_status, run on 2026-10-21, with the long description cut.

json
{
  "name": "email_status",
  "total": 13,
  "values": ["verified", "catch_all_valid", "catch_all", "risky", "unknown",
             "untested", "invalid", "role", "disposable", "spam_trap",
             "abuse", "derived", "none"],
  "labels": {
    "verified": "Deliverable",
    "catch_all_valid": "Catch-all, mailbox confirmed",
    "catch_all": "Catch-all domain",
    "risky": "Risky",
    "unknown": "Unknown",
    "untested": "Not yet checked",
    "invalid": "Undeliverable",
    "role": "Role address",
    "disposable": "Disposable address",
    "spam_trap": "Spam trap",
    "abuse": "Abuse / complainer",
    "derived": "Derived, not verified",
    "none": "No email"
  }
}

Two rules follow from the tool's own description:

  • On a business address, verified means our own verifier sent to it and it accepted.
  • On a personal address such as Gmail, we do not verify the domain. A verified there means a supplier asserted it. Treat it as a lead to check, not a confirmed address.
  • catch_all_valid is a mailbox on a catch-all domain whose existence was confirmed. It ranks between verified and catch_all.

The record-level response in step 2 is the response shape from the API reference. No real person is shown here.

Troubleshooting

ErrorCauseFix
401Missing, wrong or revoked keyCheck the x-api-key header and that $LEADOCEAN_API_KEY is set in this shell.
400Validation failed, for example both email and phone sent, or neitherSend exactly one. The details field names the bad parameter.
403The key lacks the enrich scope, or the account is suspendedCreate a key with the enrich scope in the app.
404Nobody holds this addressNot an error in your code. On the reverse endpoint it still costs 1 record. Check the spelling before you send.
429Faster than your plan's rate per account: 5 a second on Free, 50 on Solo, 200 on AgencyWait for Retry-After, then retry. A sequential loop rarely hits this.
402Free plan: the 1,000 records are spentThey do not reset. Move to Solo ($97 a month) or Agency ($497 a month) to keep going.

FAQ

Is there a truly free reverse email search tool?

Free here means 1,000 records, one-off, no card. Each person found spends one. After that, Solo is $97 a month and Agency $497 a month. Both run at a fair-use pace with no per-record price. See pricing.

Does it work on Gmail and other personal addresses?

The endpoint accepts any address and matched.type labels it. Personal addresses are the weak case: we do not verify consumer domains, so read matched.status with care. Our guide to reverse Gmail search covers that case.

Can I search by name or by social profile?

Not with this endpoint. It takes an email or a phone number. To go the other way, from a profile to an address, see find an email from an X profile or find an email from an email signature.

How does it compare with other free tiers?

FullEnrich lists a reverse email lookup at 1 credit and a free trial of 50 credits, no card (credit costs and pricing, September 2026). LeadOcean's Free plan is 1,000 records at 1 record per reverse call. Compare the unit: a credit and a record are not the same thing.

Can I do this without code?

Not for a reverse lookup. The Exports page at app.leadocean.io is a filter builder with a column picker, a price preview and a CSV download. The docs describe no way to upload a list of emails there. Use the API or an agent. Every input and output field is on the reverse email lookup page, and more guides are in the blog.

Look up your first email free

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

Get your free API key →