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

How-to guide

How to find who owns an email address (with the reverse lookup API)

Check the address, name the person, confirm the employer. Three calls, the real queries and the response shape.

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

To find who owns an email address, send it to a reverse lookup endpoint and read the person it returns. This guide uses one LeadOcean call for the owner, one for the employer, and a status check so you know what the address is before you trust it. It skips the browser tricks and shows the queries and the output.

TL;DR

  • GET /v2/people/reverse returns the name, title, employer domain and LinkedIn URL behind an address, plus the address's own status.
  • It takes about 10 minutes for one address. Wrapping it in a script takes about 20.
  • Each reverse call costs 1 record, including one that finds nobody. The walk-through spends 2 records.

Prerequisites

  • A LeadOcean account on the Free plan: start free. 1,000 records for life, no card.
  • An API key from the app, exported as $LEADOCEAN_API_KEY. Free keys carry the search and enrich scopes.
  • curl and dig. Python 3 with requests for step 5.
  • An address you have the right to look up. The examples use jane.doe@acme.com, a placeholder.
bash
export LEADOCEAN_API_KEY="paste-your-key-here"

Step 1: Classify the address before you look it up

Ownership means different things for different addresses. A work address belongs to a person at a company. A role address such as info@ belongs to a team. A disposable address belongs to nobody for long.

Split the address and check the domain first. This costs no records.

bash
EMAIL="jane.doe@acme.com"
DOMAIN="${EMAIL#*@}"
LOCAL="${EMAIL%@*}"
echo "local=$LOCAL domain=$DOMAIN"
dig +short MX "$DOMAIN"

No MX record means the domain cannot receive mail, so there is no owner to find. A MX pointing at Google or Microsoft only tells you the company hosts its mail there. If the local part is info, sales or support, expect a role address, not a person. For syntax rules, see email address syntax. For throwaway inboxes, see temporary email address and disposable email address.

Step 2: Run the reverse lookup

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

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

Keep --data-urlencode. A tagged address such as jane+sales@acme.com breaks without it, because + reads as a space in a query string. POST with a JSON body of {"email": "jane.doe@acme.com"} also works.

Step 3: Read what came back

The response names the person and describes the address. This is the response 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" }
}

Three fields decide how far you trust the owner:

  • matched.type says work or personal.
  • matched.status is the deliverability of the address you sent.
  • matched_in_record is true when the record lists your exact address. false means the person was matched on it upstream and the record does not carry it. It does not mean nobody was found. Nobody found is a 404.

Every identity field can be null. A real match can arrive without a LinkedIn URL.

Step 4: Confirm the employer

A match on a work address should agree with the domain you started from. Compare company_domain to $DOMAIN. If they differ, the person has probably moved on and the address is stale.

Then pull the company record for that domain.

bash
curl -s -G "https://api.leadocean.io/v1/companies/enrich" \
  --data-urlencode "domain=acme.com" \
  -H "x-api-key: $LEADOCEAN_API_KEY"

This costs 1 record when it finds the company. For the full person profile instead, call /v1/people/enrich with the same email. It costs 1 record and reads the same cached record the reverse call used.

Step 5: Run it over a list

The docs describe no batch version, so a list is one call per address. This script reads emails.txt and writes owners.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"]

with open("emails.txt") as src, open("owners.csv", "w", newline="") as out:
    w = csv.writer(out)
    w.writerow(COLS)
    for line in src:
        email = line.strip()
        if not email:
            continue
        while True:
            r = requests.get(URL, params={"email": email},
                             headers={"x-api-key": KEY}, timeout=30)
            if r.status_code != 429:
                break
            time.sleep(int(r.headers.get("Retry-After", "1")))
        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.")
        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 come back under different person_id values through different contact points.

Free keys run at 5 requests a second. A sequential loop stays under that.

Prefer an agent? Connect the hosted MCP server at https://api.leadocean.io/mcp (OAuth sign-in, no key pasted) and ask "Who owns jane.doe@acme.com?". The agent calls leadocean_get_lead with the email, which costs 1 record when it finds someone.

Step 6: Decide what the answer means

Turn the response into a verdict with a few rules. They take a minute to write into the script.

You seeIt meansDo this
type work, status verified, company_domain equals your domainA current employee owns itUse the identity.
type work, company_domain differsThe person likely moved onTreat the address as stale. Enrich the person to see their current role.
type personalA private individual owns itRead the status with care. Check your own rules before using it.
status roleA shared mailbox, not a personFind a named contact at the company instead.
matched_in_record falseMatched upstream, not listed in the recordKeep the identity, but confirm the address elsewhere.
404Nobody in the dataset holds itStop. The call still cost 1 record.

A found owner is a starting point, not proof of consent. Check how you may use personal data under the rules that apply to you.

What you get

The status field is the part to learn 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 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.

The person record in step 3 is the response shape from the API reference. No real person is shown.

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.
404No person holds this addressNot a bug in your code. Reverse calls cost 1 record even on a miss, so check the spelling and MX before you send.
429Above the plan's rate limit (5 a second on Free, 200 on Agency)Wait for Retry-After, then retry.
402Free plan: the 1,000 records are spentThey do not reset. Move to Solo at $97 a month, or Agency at $497 for teams.

FAQ

Can I find the owner of any email address?

No. LeadOcean holds 693M people and 366M of them have an email (measured 2026-09-23). An address outside that set returns a 404. Personal addresses are the weakest case, because consumer domains are not verified.

Does a 404 mean the address is fake?

No. It means no person in the dataset holds it. Check the syntax and the MX record from step 1 to tell a typo from a real address we do not hold.

What does it cost compared with other tools?

LeadOcean charges 1 record per reverse call, found or not (contact docs, October 2026). Icypeas lists reverse email lookup at 10 credits per found profile (credit costs and pricing, September 2026). A credit and a record are different units, so price your own volume on both. See LeadOcean pricing.

Can I look up the owner of a phone number?

Yes. The same endpoint takes phone instead of email. Send one or the other, never both.

Every input and output field is on the reverse email lookup page.

Find out who owns your next unknown address

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

Get your free API key →