TL;DR
- Codex CLI reaches LeadOcean through one
codex mcp addcommand and a browser sign-in. You size the audience for free, spot-check five people, then export a CSV. - It takes about 20 minutes. The example audience (VPs of sales in Canada) spends about 105 records: 5 on the spot-check and 100 on the export. The export step needs Solo or above.
- Counts, filter lookups and enum lookups cost nothing. Only people you receive count as records.
Prerequisites
- A LeadOcean account from app.leadocean.io. The free plan has no card and 1,000 records, one-off.
- Codex CLI installed and signed in. The Codex MCP docs, October 2026, say the CLI, the IDE extension and the desktop app share one MCP configuration.
- For the REST export step: a key in your shell as
$LEADOCEAN_API_KEY, pluscurlandjq.
export LEADOCEAN_API_KEY="paste-your-key-here"The MCP steps need no key. Setup details for every client, Codex included, are on MCP for agents.
Step 1: Add the LeadOcean MCP server
Register the server with the CLI, then sign in.
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 are available. Codex supports OAuth with Dynamic Client Registration, and the LeadOcean server supports it too, so you do not create a client ID by hand.
If you prefer a file, the same entry goes in ~/.codex/config.toml. A project-scoped .codex/config.toml also works for trusted projects.
[mcp_servers.leadocean]
url = "https://api.leadocean.io/mcp"Do not add a bearer token or header block. Run codex mcp list to confirm the entry exists, then start codex.
Step 2: Check the tools and your balance
The server exposes 13 leadocean_ tools. Seven never spend records: lead count, company count, filter list, enum list, account, get export and list exports. In the Codex terminal UI, type /mcp to see the server and its tools.
Ask for your balance first.
Use leadocean_get_account. Tell me my plan and how many records I have left.
Do not search anything.The REST route for the same check is free too.
curl -s https://api.leadocean.io/v1/account \
-H "x-api-key: $LEADOCEAN_API_KEY" | jqRead each tool call before you approve it. The Codex docs describe per-server approval modes in config.toml, such as prompt, which asks every time. That is a sensible default while you are watching what spends records.
Step 3: Size the audience for free
Write the audience as filters. This example is VPs in sales at companies with a Canadian base. Filters take exact strings, so resolve the function name first.
Use leadocean_list_enum_values for job_function with q "sales". Then use
leadocean_count_leads with jobLevel ["VP"], jobFunction ["Sales & Business
Development"], country ["CA"], facets false. Report the total and say which
email statuses it counted. Do not call any tool that spends records.The REST version is a people search with count=true as a query parameter and limit at 1. It returns meta.total and no rows.
curl -s "https://api.leadocean.io/v1/people/search?count=true" \
-H "x-api-key: $LEADOCEAN_API_KEY" -H "content-type: application/json" \
-d '{
"jobLevel": ["VP"],
"jobFunction": ["Sales & Business Development"],
"country": ["CA"],
"emailStatus": ["verified", "catch_all_valid", "catch_all"],
"limit": 1
}' | jq '.meta'Change one filter at a time so you know which one moved the total. Add employeeRange first, then industry, then title keywords. Stop when the count is a list you could work in a month. The meta.total figure in a search is capped at 100,000, so a very wide audience shows a floor, not a true total.
Step 4: Spot-check five people
Look at real rows before you pay for 100 of them. Search returns thin records and no addresses.
Use leadocean_search_leads with the same filters and limit 5. Show a table of
name, job title, company, company domain and email_status. Flag any title that
is not a sales leader so I can add it to excludeTitle.Each person returned costs one record, so this step spends 5. The REST version is the same body with limit set to 5 and no count. Add excludeTitle (for example ["assistant","intern"]) if the sample shows noise. Search pages take limit 1 to 100 and continue with cursor.
Step 5: Export the list to CSV
Use an export for the bulk pull. A terminal chat is a poor place for hundreds of rows, and an export writes one file. An export takes up to 50,000 rows a request and 500,000 a day. Bulk CSV export is not on Free: it is included on Solo at $97 a month and on Agency at $497 a month.
curl -s https://api.leadocean.io/v1/exports \
-H "x-api-key: $LEADOCEAN_API_KEY" -H "content-type: application/json" \
-d '{
"name": "VPs of sales in Canada",
"filters": {
"jobLevel": ["VP"],
"jobFunction": ["Sales & Business Development"],
"country": ["CA"],
"emailStatus": ["verified", "catch_all_valid", "catch_all"]
},
"limit": 100,
"preset": "full"
}' | tee export.json | jq '.data | {id, status, requested, perRow, remaining, pollAfter}'Read perRow and remaining in the reply. They are the price of the job before it runs. The API reference says perRow is 1, or 2 when a column needs the full record, so check it before you let the job run. The job then runs in the background. Poll every pollAfter seconds and download when the status is done.
ID=$(jq -r '.data.id' export.json)
curl -s https://api.leadocean.io/v1/exports/$ID -H "x-api-key: $LEADOCEAN_API_KEY" | jq '.data.status'
curl -s https://api.leadocean.io/v1/exports/$ID/download -H "x-api-key: $LEADOCEAN_API_KEY" -o leads.csvTo stay in Codex, ask for it in one prompt. The tools leadocean_export_leads and leadocean_get_export start the job and read its status.
Start a leadocean export with the filters above, limit 100, preset full. Poll it
with leadocean_get_export until it is done, then give me the download link.Read the email_status column of leads.csv before you load it anywhere. Verified and catch_all_valid addresses are the safest to send to. Bounced emails are not refunded, so drop anything you do not trust.
The LeadOcean app has the same export on its Exports page. It has a filter builder, a column picker, the record price before you start and a Download button.
What you get
The Step 3 run is real. On 21 October 2026 we ran leadocean_list_enum_values and leadocean_count_leads with the filters above. Both are free. The enum lookup returned this.
{
"name": "job_function",
"total": 22,
"matched": 1,
"values": ["Sales & Business Development"],
"truncated": false
}The count returned this.
{
"total": 5574,
"totalIsExact": true,
"totalAll": 18800,
"distinctCompanies": 3873,
"credits": 0,
"defaults": "Counted only addresses worth sending to (emailStatus: verified or catch_all_valid or catch_all), which is this tool's default.",
"sample": 1,
"hasMore": true
}The total describes the mailable audience, meaning verified, catch_all_valid or catch_all emails, at 3,873 companies. Everyone matching the other filters is totalAll, 18,800 people. Your number will differ as the data refreshes monthly.
We did not run the spending steps for this page. The export request returns HTTP 202 and a job. This is the response shape from the API reference, with placeholder values.
{
"success": true,
"data": {
"id": "<export id>",
"name": "VPs of sales in Canada",
"status": "queued",
"requested": 100,
"perRow": "<records per row>",
"remaining": "<records left after this reservation>",
"pollAfter": "<seconds>",
"file": null
}
}The finished download is a plain .csv, one row per person. Records are reserved when the export is admitted, and rows never written are released. The run should cost about 105 records if perRow is 1: 5 for the spot-check and 100 for the export. At 2 a row it is about 205.
Troubleshooting
| Error | Cause | Fix |
|---|---|---|
| 401 | Missing, wrong or revoked key on a REST call, or the MCP sign-in was not approved | Re-copy the key from app.leadocean.io and re-export $LEADOCEAN_API_KEY. For MCP, run codex mcp login leadocean again. |
| 429 | Above the plan's rate limit (5 a second on Free, 50 on Solo, 200 on Agency), a fair-use window, or the daily export ceiling | Wait for the Retry-After header, then retry. Send full pages of 100 rather than more calls. |
| 402 | The 1,000 free records are spent, or an export limit is above what is left | Lower limit to what GET /v1/account shows, or move to Solo at $97 a month. Free records do not reset. |
| 400, or a count of 0 | A filter value outside the catalogue, such as jobFunction: "Sales" | Resolve it with leadocean_list_enum_values and pass the exact string. |
| 400 on an export | limit is above 50,000 rows for one export | Split the audience by country or headcount band and run several exports. |
FAQ
How many records does a lead list cost?
One record per person returned, and one or two per export row, as perRow shows. Sizing is free, so the plan is: count, spot-check five, then export. The example should spend about 105 records.
Do I need an API key in Codex CLI?
Not for the MCP steps. The server uses OAuth sign-in through codex mcp login, and you paste nothing into the chat. The REST steps read $LEADOCEAN_API_KEY from your shell.
Why export instead of searching in the terminal?
A search returns at most 100 rows a page and thin records without addresses. An export writes one CSV with the contact columns, and you see its price in perRow before it runs.
Can I use the free plan for this?
Partly. Free is 1,000 records, one-off, with search and enrich both available, so the count and the spot-check run on Free. Bulk CSV export is not on Free. For Step 5, move to Solo at $97 a month, or page through search results on Free instead. See pricing.
More recipes live in the blog hub. The same steps work in Gemini CLI, Windsurf and Cursor.
Build your first Codex CLI lead list free
Free to start. No credit card. 1,000 records to spend whenever you like.
Get your free API key →