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

How-to guide

How to Find an Email From Webinar Registrants (2026)

Registrants often sign up with a personal address or none you can use. Go from that row to a checked work email, one person by hand and a whole list by export.

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

TL;DR

  • A registrant list gives you the address they typed in. That is often a Gmail or a student account, so you need the work email at their current employer.
  • It takes about 15 minutes for 10 people. The API route spends about 10 LeadOcean records: one per enrichment that finds the person. A miss costs nothing.
  • For a long list, skip the loop and run one export. Read email_status in the CSV before you send.

Prerequisites

  • A LeadOcean account from app.leadocean.io. The free plan has no card and gives 1,000 records one-off.
  • curl, jq and python3.
  • An API key in your shell as $LEADOCEAN_API_KEY. Free keys get both scopes, search and enrich.
  • Your registrant export from your webinar tool (name, email, company if the form asked). Use only people who registered for your own event.
bash
export LEADOCEAN_API_KEY="paste-your-key-here"

Step 1: Split the list into usable and unusable rows

Sort the CSV first. A row on a company domain may already be the work address, and enrichment tells you its status. A row with a free-mail address needs enrichment to find the employer, because the domain tells you nothing.

The script below splits a registrant file into two. The rows are placeholders.

bash
cat > registrants.csv <<'EOF2'
first,last,email,company
Jane,Doe,jane.doe@gmail.com,Acme
John,Roe,john@example.org,Example
EOF2

python3 - <<'EOF2'
import csv
free = {"gmail.com","yahoo.com","outlook.com","hotmail.com","icloud.com","proton.me"}
work, personal = [], []
for r in csv.DictReader(open("registrants.csv")):
    domain = r["email"].split("@")[-1].lower()
    (personal if domain in free else work).append(r)
print(len(work), "on a company domain,", len(personal), "on a free-mail address")
EOF2

Step 2: Guess the pattern by hand (one or two people)

For a registrant on a company domain you can guess the address pattern. This prints the usual candidates for a placeholder name.

bash
first="jane"; last="doe"; domain="acme.com"
for p in "$first.$last" "$first" "${first:0:1}$last" "$first$last"; do
  echo "$p@$domain"
done

Every line is a guess. A domain that accepts all mail answers the same for a real and a fake address, so a guess can look fine and still bounce. Use this for one or two people, then move to the API.

Step 3: Enrich the address they gave you

POST /v1/people/enrich takes exactly one of linkedin_url, email, phone or person_id. Send the email the registrant typed, even if it is personal. The call returns the profile we hold for that address, with the current employer. With reveal_email true it also returns every address we hold.

bash
curl -s https://api.leadocean.io/v1/people/enrich \
  -H "x-api-key: $LEADOCEAN_API_KEY" -H "content-type: application/json" \
  -d '{"email": "jane.doe@gmail.com", "reveal_email": true}' \
  > jane.json

jq '.data | {name: .profile_data.profile_full_name,
  linkedin: .profile_data.profile_url,
  current: .contact_data.contact_current_experiences[0].company_domain}' jane.json

A person we hold nothing for returns a 404 and costs nothing. A hit costs one record, the same with or without the reveal. Save the response, because the next step reads it.

Step 4: Pick the work email

The saved response already holds the addresses. Keep the ones of type work with a status of verified, catch_all_valid or catch_all. This step reads a file, so it costs nothing.

bash
jq '[.data.contact_data.contact_emails[]?
     | select(.type == "work")
     | select(.status == "verified" or .status == "catch_all_valid" or .status == "catch_all")
     | {email, status, verified_batch_date}]' jane.json

An empty array means no work address qualifies, not that the person has no email. For only the one best checked address, GET /v2/people/email/work takes the linkedin_url from Step 3, but it costs one more record, found or not. Loop Step 3 over your rows with a short sleep so you stay under your plan's rate limit.

Step 5: Size the lookalike audience for free

Webinars bring in people who did not register. Ask how many people we hold with your registrants' profile before you spend a record. Send count=true as a query parameter with limit at 1.

bash
curl -s "https://api.leadocean.io/v1/people/search?count=true" \
  -H "x-api-key: $LEADOCEAN_API_KEY" -H "content-type: application/json" \
  -d '{
    "jobFunction": ["Advertising & Marketing"],
    "jobLevel": ["Director"],
    "country": ["US"],
    "limit": 1
  }' | jq '.meta | {total, totalIsExact, credits}'

Check enum values with GET /v1/enums before you guess. A wrong string returns nothing. The search body is flat JSON, filters are arrays, and limit runs 1 to 100.

Step 6: Export the matches in bulk

For a few hundred people, export instead of looping. Bulk CSV export is not on the Free plan: it comes with Solo ($97 a month) and Agency ($497 a month). One request takes up to 50,000 rows and a day takes up to 500,000. Each row costs one record.

bash
curl -s https://api.leadocean.io/v1/exports \
  -H "x-api-key: $LEADOCEAN_API_KEY" -H "content-type: application/json" \
  -d '{
    "name": "Webinar lookalikes",
    "filters": {
      "jobFunction": ["Advertising & Marketing"],
      "jobLevel": ["Director"],
      "country": ["US"],
      "emailStatus": ["verified", "catch_all_valid", "catch_all"]
    },
    "limit": 100,
    "preset": "full"
  }' | jq '.data | {id, status}'

Poll GET /v1/exports/{id}, then fetch GET /v1/exports/{id}/download when the status is done. The app does the same on its Exports page, with a filter builder, a column picker and the record price before you start.

Step 7: Check before you send

Columns are numbered: email_1_address, email_1_status. Keep rows where the status is verified or catch_all_valid.

bash
python3 - <<'EOF2'
import csv
rows = list(csv.DictReader(open("export.csv")))
ok = [r for r in rows if r.get("email_1_status") in ("verified", "catch_all_valid")]
print(len(rows), "rows,", len(ok), "with a sendable status")
EOF2

LeadOcean gives no refund or credit-back for bounced emails, so read the status first. catch_all means the domain accepts any address, so it is riskier than catch_all_valid.

What you get

The Step 5 sizing call is a real run. We ran it on 2026-10-21 with leadocean_count_leads, which spends 0 records, for Directors in Advertising & Marketing in the US.

json
{
  "total": 245637,
  "totalIsExact": true,
  "totalAll": 683895,
  "distinctCompanies": 106102,
  "credits": 0,
  "unsupportedFilters": []
}

That is 245,637 mailable people on 2026-10-21 (verified, catch_all_valid or catch_all), out of 683,895 with no email filter. It is a mailable count, not everyone we hold.

Step 4 prints the shape below for one registrant. This is the response shape from the API reference, with placeholder values.

json
[
  {
    "email": "jane.doe@acme.com",
    "status": "verified",
    "verified_batch_date": "2026-09-20"
  }
]

Troubleshooting

ErrorCauseFix
401Missing, wrong or revoked keyRe-copy the key from app.leadocean.io and export $LEADOCEAN_API_KEY again.
429Above your plan's rate limit (5 a second on Free, 200 on Agency)Slow the loop and honour the Retry-After header, or use an export.
402The 1,000 free records are spentCheck GET /v1/account, or move to Solo ($97 a month) or Agency ($497 a month) on pricing. Free records do not reset.
Search returns no rowsA filter value outside the catalogue, such as a job level of "Director of Marketing"Check exact strings with GET /v1/enums or leadocean_list_enum_values.
404 on enrichWe hold nothing for that emailIt costs nothing. Try the registrant's name and company through a search by domain.
400 export_limit_rowsThe export limit is above 50,000 rowsSplit the audience by country or title and run several exports.

FAQ

Can I email webinar registrants?

They gave you their address for the event. Check the registration terms and the rules where your recipient lives before you send follow-ups. Write about the webinar topic, not a cold pitch.

Why did the registrant use a personal email?

People avoid sales follow-up and sign up with a Gmail. Enrichment by that address finds the profile we hold, so you can look up the work address.

How many records does it cost?

One per enrichment hit. A 404 on enrich is free, and so are counts. Ten registrants spend about 10 at most. The optional work-email call in Step 4 adds one each.

What if the person changed jobs?

Enrichment returns the current employer we hold, and each record carries a fetched_at date. If the company does not match what the registrant told you, skip the row.

Can an AI agent run the lookups?

Yes. The hosted MCP server signs in with OAuth, so no key is pasted. Ask it to look up each registrant's address with leadocean_get_lead, which costs one record and nothing on a miss.

Where do I find more recipes?

Start from Twitter handles or an email signature. The find-work-email skill runs the lookup from Claude. Plans are on pricing.

Turn your webinar registrants into checked work emails, free

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

Get your free API key →