TL;DR
- You add LeadOcean to Codex CLI once with one command. Then you ask for verified work emails in plain English.
- It takes about 15 minutes the first time and about 5 after that.
- A full run for 5 people spends 8 LeadOcean records: 5 for the search, 3 for revealing the emails you pick. Sizing the list is free.
Prerequisites
- A LeadOcean account. Start free: no card, 1,000 records one-off.
- Codex CLI installed and signed in. The Codex MCP docs, September 2026, say the CLI, the IDE extension and the desktop app share one MCP configuration.
- Optional, for Step 5:
$LEADOCEAN_API_KEYin your shell, pluscurl.
export LEADOCEAN_API_KEY="paste-your-key-here"You paste no API key into Codex. The MCP server uses OAuth sign-in.
Step 1: Add LeadOcean to Codex CLI
Register the server with one command, then sign in. Server details are on the LeadOcean MCP page.
codex mcp add leadocean --url https://api.leadocean.io/mcp
codex mcp login leadoceanThe login opens your browser on LeadOcean. Approve, return to the terminal, and the tools load. Codex supports OAuth with Dynamic Client Registration, and our server supports it too, so you create no client ID.
If you prefer a file, the same entry goes in ~/.codex/config.toml. A project-scoped .codex/config.toml also works for trusted projects. The optional default_tools_approval_mode line makes Codex ask before every LeadOcean tool call, which is how you keep control of spend.
[mcp_servers.leadocean]
url = "https://api.leadocean.io/mcp"
default_tools_approval_mode = "prompt"Run codex mcp list to confirm. Start codex and type /mcp to see the server. It exposes 13 tools, and seven of them are free: the two count tools, leadocean_list_filters, leadocean_list_enum_values, leadocean_get_account, leadocean_get_export and leadocean_list_exports.
Step 2: Size the list of work emails first
Ask for a count before you pull anyone. Counting costs 0 records, so you can tune the filters as often as you like. In the Codex prompt, send:
Use the leadocean MCP server only. How many Directors in Advertising &
Marketing in Germany have a verified work email? Count only. Do not
pull anyone.Codex calls leadocean_count_leads with these arguments:
{
"jobLevel": ["Director"],
"jobFunction": ["Advertising & Marketing"],
"country": ["DE"],
"emailType": ["work"],
"emailStatus": ["verified"],
"facets": false
}emailType set to work keeps only addresses at the person's current employer. emailStatus set to verified keeps addresses our verifier tested. The values are exact strings. If Codex guesses one, tell it to call leadocean_list_enum_values first.
Step 3: Pull five people
Now ask for a small batch. Keep the limit low while you check the output.
Call leadocean_search_leads with the same filters and limit 5.
Show title, company and whether an email is on file. Do not reveal
any emails yet. Tell me how many records this spent.The tool call looks like this:
{
"jobLevel": ["Director"],
"jobFunction": ["Advertising & Marketing"],
"country": ["DE"],
"emailType": ["work"],
"emailStatus": ["verified"],
"limit": 5
}This spends 5 records, one per person returned. Search shows flags such as has_email, not the address itself. Each row carries a person_id, which you use in the next step.
Step 4: Reveal the emails for the people you pick
Enrich by person_id with reveal_email set to true. Ask Codex to reveal only the people you want, not all five.
Reveal the work email for the first three people from that list with
leadocean_get_lead, using their person_id and reveal_email true. For
each, show the address, its type and its status. Say how many records
this call spent.Each reveal costs 1 record, so three people cost 3. There is no extra charge for the email itself. A person we hold nothing for is a miss, and an MCP miss costs nothing.
Read status before you send anything. A search row can say verified while the enriched answer says untested, because enrichment applies our verification rule per address. Trust the enriched answer. We do not refund bounced emails.
The same reveal returns more than the address. Since October 2026, person enrich also returns career history in resume_data: past roles with titles, companies and dates, plus education and certifications when we hold them. Use it to check that the person still fits your list before you write. Past employers carry no company_id yet, so you cannot chain them into a company lookup.
Step 5: Run the same lookup from a script
Codex calls the same API you can call yourself. Use this when you want results in a file, or when you have a LinkedIn URL and no search row. The search is a flat JSON body, and filters are arrays.
curl -s -X POST https://api.leadocean.io/v1/people/search \
-H "x-api-key: $LEADOCEAN_API_KEY" \
-H "Content-Type: application/json" \
-d '{"jobLevel": ["Director"], "jobFunction": ["Advertising & Marketing"], "country": ["DE"], "emailType": ["work"], "emailStatus": ["verified"], "limit": 5}'The dedicated work email endpoint returns one current, checked address or null. Send a LinkedIn URL or a person_id:
curl -s "https://api.leadocean.io/v2/people/email/work?linkedin_url=https://www.linkedin.com/in/jane-doe" \
-H "x-api-key: $LEADOCEAN_API_KEY"That call costs 1 record, including when it returns null. You cannot look a person up by name plus company. Search by domain first, then send the person_id. Field detail is on Find Work Email. The Find Work Email skill wraps the same steps for Claude, and the Gemini CLI version of this guide covers the other terminal agent.
What you get
The filter values in Step 2 are exact strings. We looked up the email statuses through the free leadocean_list_enum_values tool on 2026-10-01. It returned 13 values:
{
"name": "email_status",
"total": 13,
"matched": 13,
"values": ["verified", "catch_all_valid", "catch_all", "risky", "unknown", "untested", "invalid", "role", "disposable", "spam_trap", "abuse", "derived", "none"]
}We then ran the Step 2 filters through the free leadocean_count_leads tool on 2026-10-01. It returned an exact total of 2,893, at a cost of 0 records. That counts Directors in Advertising & Marketing in DE whose best email is a work address marked verified. It is not everyone we hold for those filters. Search counts of verified are an upper bound, so confirm each address when you reveal it.
Each revealed person comes back in the response shape from the API reference. These are placeholder values, not a real person:
{
"success": true,
"data": {
"profile_data": { /* name, title, company for Jane Doe at acme.com */ },
"contact_data": {
"has_email": true,
"email_status": "verified",
"email_type": "work",
"contact_emails": [
{
"email": "jane.doe@acme.com",
"type": "work",
"status": "verified",
"verified_batch_date": "2026-09-20"
}
]
}
}
}Cost of the whole run: 0 records for the connect and the count, 5 for the search, 3 for the reveals. That is 8 records. On the free plan you have 1,000, so this run uses under 1% of them. We ran the free steps ourselves. The 8 is the documented cost, not a measured one.
Troubleshooting
| Error | Cause | Fix |
|---|---|---|
| 401 | Missing or wrong x-api-key header, or the MCP sign-in was not approved. | Check $LEADOCEAN_API_KEY, or run codex mcp login leadocean again. |
| 402 | The 1,000 free records are spent. | Ask Codex to run leadocean_get_account, or move to Solo at $97 a month. |
| 429 | Above the plan rate limit (5 requests a second on Free, 200 on Agency), or a fair-use window was reached. | Honour Retry-After and retry. Add a short delay between calls. |
| Empty results | An enum value is not an exact string, such as Vice President for jobLevel. | Call leadocean_list_enum_values and copy the value. Use title for free-text job titles. |
data: null on the work email endpoint | No address at the current employer is verified, catch_all_valid or catch_all. | Do not retry, the answer will not change. The call still cost 1 record. Try another person. |
| Export refused | A bulk pull is past 50,000 rows a request or 500,000 a day. | Split the pull into smaller exports across days. |
FAQ
Can Codex find a work email from a name and company?
Not directly. LeadOcean has no name plus domain lookup. Search by title and company domain, then reveal the email by person_id. A LinkedIn URL also works as the key.
Which email status should I send to?
Start with verified. It means our verifier sent to the address and it accepted. catch_all_valid is a catch-all domain where we confirmed the mailbox. catch_all accepts everything, so no test can confirm it.
Do I need to write code?
No. Steps 1 to 4 run entirely in the Codex prompt. The script in Step 5 is optional.
Can Codex spend my records without asking?
Only if you let it. The default_tools_approval_mode setting in config.toml controls whether Codex prompts before a tool runs, per the Codex MCP docs, September 2026. Read the arguments, especially limit, before you approve. A wrong limit is the usual way to spend more records than you planned.
Find your first verified work email in Codex, free
Free to start. No credit card. 1,000 records to spend whenever you like.
Get your free API key →