TL;DR
- A Product Hunt launch gives you a product, a website and a maker name. Take the maker name and the product's company to LeadOcean and ask for the checked work email. Two calls.
- It takes about 15 minutes the first time, mostly getting a key. Each maker costs about 2 records of your 1,000 free ones: 1 for the search row, 1 for the email.
- For a list, loop the same two calls over a file of makers. The free plan allows 5 requests a second. Agency allows 200.
Prerequisites
- A LeadOcean account and key from app.leadocean.io. The free plan has no card and 1,000 records, one-off. Free keys get both the search and enrich scopes.
- The key in your shell as
$LEADOCEAN_API_KEY. curlandjq. For the bulk step, a file with onename,companypair per line.
export LEADOCEAN_API_KEY="paste-your-key-here"One rule first. Product Hunt has its own terms and a developer API with its own rules (Product Hunt terms, September 2026; API docs, October 2026). Read them before you scrape anything at volume, and write only to people you have a real reason to write to. The Product Hunt API 2.0 is a GraphQL interface with public, private and write scopes, so a list of launches is a query, not a scrape.
Step 1: Read the launch page
Open the launch and note three things: the product name, the website link and the makers listed. A maker's profile shows the name and what they chose to publish. Most do not publish an email, so treat a blank as a no.
Write down the company name as the product's own site states it. That is what you search on, not the Product Hunt tagline.
Product: Acme Notes
Website: acme.com
Maker: Jane Doe
Role: FounderA launch is also a timing signal. Write while the launch is recent and your note can mention it.
Step 2: Find the maker in LeadOcean
Search on the name and the company. The body is flat JSON and every filter is an array. Each person returned costs 1 record, so keep limit small.
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 Notes"],
"limit": 3
}' | jq -c '.data[] | {name: .profile_data.profile_full_name, person_id: .meta.source_ids.person_id}'Read the rows before you pick one. Check the title and company against the launch page. The name sits at profile_data.profile_full_name in each row. If the name is common, add a title filter such as ["Founder"] or a country.
To size a role before you spend anything, send count=true as a query parameter with limit 1. On 21 October 2026, the free leadocean_count_leads MCP tool counted 1,018,109 mailable people at C-Team level with the title "Founder", out of 4,101,901 matching with or without an email. Mailable here means a verified, catch-all valid or catch-all address.
Step 3: Get the work email
Send the person_id to the work email endpoint. It returns one address at the person's current employer, or data: null.
curl -s -G "https://api.leadocean.io/v2/people/email/work" \
-H "x-api-key: $LEADOCEAN_API_KEY" \
--data-urlencode "person_id=PERSON_ID_FROM_STEP_2" | jqOnly checked addresses come back: verified, catch_all_valid or catch_all. So null means "none that qualify", not "this person has no email". The call costs 1 record, found or not.
Do not guess jane@acme.com from a pattern. Guessed addresses bounce. LeadOcean gives no refund or credit-back for bounced emails, so read the status before you send.
Step 4: Run it over a list
Put one name,company pair per line in makers.csv. Loop both calls and write one JSON line per maker. The loop takes the first search row, so read a few results before you trust it.
while IFS=, read -r name company; do
id=$(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\": \"$name\", \"companyName\": [\"$company\"], \"limit\": 1}" \
| jq -r '.data[0].meta.source_ids.person_id // empty')
[ -z "$id" ] && continue
curl -s -G "https://api.leadocean.io/v2/people/email/work" \
-H "x-api-key: $LEADOCEAN_API_KEY" \
--data-urlencode "person_id=$id" \
| jq -c --arg name "$name" '{name: $name, email: .data.email, status: .data.status}'
sleep 0.5
done < makers.csv > results.jsonlEach pass makes two calls, so the sleep keeps you under 5 requests a second on the free plan. Records add up fast: 500 makers spend up to 1,000. Run 20 first and read the hit rate before you spend on the rest.
When you want every founder in a segment and not a list of launches, skip the loop. POST /v1/exports takes search filters and writes a CSV, up to 50,000 rows a request and 500,000 a day. Bulk CSV export is not on the free plan. Solo at $97 a month includes it (pricing, October 2026). The app at app.leadocean.io has an Exports page with a filter builder and a price preview if you prefer clicks.
Then filter on status before you send. Keep verified and catch_all_valid. Treat catch_all as a risk you size, not a green light.
jq -c 'select(.email != null and (.status == "verified" or .status == "catch_all_valid"))' \
results.jsonl > sendable.jsonl
wc -l sendable.jsonlWhat you get
Both responses below are the response shape from the API reference. Every value is a placeholder, not a real record.
A search row carries the id you need at meta.source_ids.person_id. The profile fields sit under profile_data and are left out here.
{
"success": true,
"data": [
{
"profile_data": {},
"meta": { "source_ids": { "person_id": "1234567890" } }
}
],
"meta": { "count": 1, "nextCursor": null }
}The work email call returns success and data, with no meta object.
{
"success": true,
"data": {
"email": "jane.doe@acme.com",
"status": "verified",
"verified_batch_date": "2026-09-20"
}
}Troubleshooting
| Error | Cause | Fix |
|---|---|---|
| 401 | Missing, wrong or revoked key | Re-copy the key from app.leadocean.io and re-export $LEADOCEAN_API_KEY. The header is x-api-key. |
| 429 | Above the plan's rate limit: 5 a second on Free, 200 on Agency | Wait for Retry-After, add a sleep, or move to a plan with a higher limit. |
| 402 | The free 1,000 records are spent | Check GET /v1/account, then see the pricing page. |
| Empty search results | A filter value that does not match, or a product name that differs from the company name | Drop companyName and search on q alone, then check enum values with leadocean_list_enum_values. |
data: null on the work email call | No checked address at the current employer | Accept the miss, or enrich by linkedin_url if you have it. |
| Export refused | More than 50,000 rows a request or 500,000 a day, or a Free plan | Split the export, or move to a plan with bulk CSV. |
FAQ
Can I get an email from a Product Hunt profile alone?
Only if the maker published one. Most do not. Take the name and company to a data source instead.
What if the product name is not the company name?
Check the product's website footer or about page for the legal or trading name. Search on that. Early products often launch under a brand with no company page.
How many records does one lookup cost?
About 2: one search row and one email call. A 404 on enrich costs nothing. The work email call costs 1 record even when it finds nothing.
Can an AI agent do the lookup?
Yes. Connect the hosted MCP server with OAuth, paste the maker name and company, and ask for the work email. It uses the same search and lookup, so it spends the same records.
Is it allowed to email makers I find on Product Hunt?
Product Hunt has its own terms, and email rules vary by country. Send relevant, individual messages and follow the laws that apply to you.
Where do I find people by role instead?
Use how to find a product manager's email, how to find a head of product's email or find email from Twitter. All sit in the blog. See pricing for plan limits.
Turn a Product Hunt launch into a checked work email
Free to start. No credit card. 1,000 records to spend whenever you like.
Get your free API key →