You enrich a CSV in Claude by connecting the LeadOcean MCP server, attaching the file, and asking Claude to look up each row. Claude does the lookups. LeadOcean returns the email, status and phone it holds.
This guide tests on five rows, then moves to a script for the full file. Total time is about 20 minutes.
TL;DR
- Takes about 20 minutes: 5 minutes to connect, 5 to test on five rows, 10 to run a script on the full file.
- Spends 1 record per row we match, so a 25-row file costs up to 25 of your 1,000 free records. A row we hold nothing for costs 0.
- Claude handles small files in chat. Past about 25 rows, switch to the API script in Step 4. It runs faster and Claude does not need to stay open.
Prerequisites
- A LeadOcean account. Start free: no card, 1,000 records, one-off, work email required.
- A Claude account that can add a custom connector. The Claude connector docs, October 2026, list Free, Pro, Max, Team and Enterprise. Free allows one custom connector.
- A CSV with a
linkedin_urlcolumn. Email or phone also work as the lookup key. - For Step 4 only: an API key from the app, saved as
$LEADOCEAN_API_KEY, pluscurlandjq.
A sample contacts.csv, with placeholder rows:
name,company,linkedin_url
Jane Doe,Acme,https://www.linkedin.com/in/janedoe-example
John Roe,Acme,https://www.linkedin.com/in/johnroe-exampleStep 1: Connect LeadOcean to Claude
Add https://api.leadocean.io/mcp as a custom connector, then sign in with OAuth. No key is pasted. The full click path is on the Claude setup page.
The short version, on a Pro or Max Claude plan, is below.
Customize > Connectors > + Add > Add custom connector
Name: LeadOcean
URL: https://api.leadocean.io/mcpClick Continue, approve the LeadOcean sign-in in your browser, then switch the connector on in your chat under "+" and "Connectors". The server exposes 13 tools. Seven are free to call.
Step 2: Check the filters for free
Before you spend records, confirm Claude can reach the server. Ask for a free lookup:
Using LeadOcean, list the valid values for job level. Do not search for anyone.Claude calls leadocean_list_enum_values. That costs 0 records. If it answers with six values, the connection works.
Step 3: Enrich five rows in chat
Attach contacts.csv to the chat and cap the run at five rows. The cap keeps the spend visible.
I attached contacts.csv. Take the first 5 rows. For each linkedin_url, call
leadocean_get_lead with reveal_email true, so it returns the emails and phones it holds.
Stop after 5 rows. Return a table with name, company, email, email_status, phone.
If a row is not found, write "not found" and move on. Do not guess an address.Claude calls leadocean_get_lead once per row. reveal_email covers both emails and phone numbers and adds no record. A found row costs 1 record. A row that finds nobody costs nothing, the same as a REST 404.
Read email_status before you send. LeadOcean gives no refund or credit for bounced emails.
Step 4: Run the full file with the API
A script is the right tool above about 25 rows. It calls POST /v1/people/enrich once per row. Set reveal_email to true, which returns every email and phone we hold at no extra record.
export LEADOCEAN_API_KEY=your_key_here
echo "linkedin_url,email,email_status,phone" > enriched.csv
tr -d '\r' < contacts.csv | tail -n +2 | while IFS=, read -r name company linkedin; do
curl -s -X POST https://api.leadocean.io/v1/people/enrich \
-H "x-api-key: $LEADOCEAN_API_KEY" \
-H "Content-Type: application/json" \
-d "{\"linkedin_url\":\"$linkedin\",\"reveal_email\":true}" |
jq -r --arg u "$linkedin" '[$u,
(.data.contact_data.contact_emails[0].email // ""),
(.data.contact_data.email_status // ""),
(.data.contact_data.contact_phones[0].phone // "")] | @csv' >> enriched.csv
doneThe script runs one request at a time, inside the Free plan's 5 requests a second. Open enriched.csv in any spreadsheet.
Each request takes exactly one of linkedin_url, email, phone or person_id. There is no name plus domain lookup. To find people at a company first, search by domain, then enrich by person_id.
Step 5: Check the result before you send
Enrichment gives you addresses. It does not tell you which ones are safe to mail. Filter on email_status first.
awk -F, 'NR==1 || $3 ~ /verified|catch_all/' enriched.csv > mailable.csvThat keeps rows whose status contains verified or catch_all. Drop risky, unknown, role, disposable and spam_trap. The full list of 13 statuses is in GET /v1/enums/email_status. Count the rows left in mailable.csv before you load it into your sending tool.
In Claude, the equivalent prompt is short:
From the table above, keep only rows where email_status is verified or catch_all.
Say how many rows you dropped and why.Expect some rows to come back empty. A CSV of LinkedIn URLs usually includes profiles we do not hold, and each of those is a free 404. Check the enriched.csv row count against contacts.csv so you know the match rate for your own list.
What you get
Step 2 returns the exact strings the jobLevel filter accepts. We ran it on 2026-10-01:
{"name":"job_level","description":"Seniority band of the person's current role.","total":6,"matched":6,"values":["C-Team","Director","Manager","Other","Staff","VP"],"truncated":false}Steps 3 and 4 return one record per matched row. This is the response shape from the API reference, September 2026, with placeholder values:
{
"success": true,
"data": {
"profile_data": { "...": "name, title, company" },
"contact_data": {
"has_email": true,
"has_phone": true,
"email_status": "verified",
"email_type": "work",
"contact_emails": [{ "email": "jane.doe@acme.com", "type": "work", "status": "verified" }],
"contact_phones": [{ "phone": "+14155550133", "type": "mobile" }]
}
}
}Without reveal_email, contact_emails and contact_phones are absent. has_email and has_phone still tell you whether a value exists.
Troubleshooting
| Error | Cause | Fix |
|---|---|---|
| 401 | The x-api-key header is missing or the key is wrong. | Copy a fresh key from the app and re-export $LEADOCEAN_API_KEY. |
| 402 | Free plan: your records are spent. The free plan is 1,000 records, one-off. | Run GET /v1/account to check the balance, or move to Solo at $97 a month. |
| 404 | We hold nothing for that identifier. | Nothing to fix. A 404 costs 0 records. Try the email or person_id for that row instead. |
| 429 | Above your plan's rate: 5 requests a second on Free, 200 on Agency. | Pause a second and retry. A one-at-a-time loop does not hit this. |
| Empty search results | A filter value is not an exact enum string, such as Vice President instead of VP. | Call leadocean_list_enum_values and use the string it returns. |
| Export refused | One export is capped at 50,000 rows, and an account at 500,000 rows a day. | Split the job across requests and days, or narrow the filters. |
FAQ
Can Claude read a CSV and enrich every row?
Yes for small files. Claude makes one leadocean_get_lead call per row, so a long file means a long chat. Cap each prompt at 25 rows, or use the Step 4 script for the rest.
How many records does a 25-row file use?
Up to 25. Each matched row costs 1 record. A row we hold nothing for costs 0. Counts, filter lookups and account checks are free.
Can I upload a CSV to the Exports page instead?
The Exports page in the app builds a file from filters: a filter builder, a column picker and a price preview. It does not take an uploaded list. To enrich your own rows, use Claude or the API.
What if my CSV has no LinkedIn URLs?
Use email or phone as the key instead. Each request takes exactly one identifier. For a list of company domains, search first, then enrich by person_id.
Does this work on the free plan?
Yes. Free keys get both search and enrich scopes inside the 1,000 one-off records. The MCP server is the same one paid plans use.
Enrich your own CSV in Claude, free
Free to start. No credit card. 1,000 records to spend whenever you like.
Get your free API key →