This stack gives each tool one job. LeadOcean builds the list, n8n filters the risky emails and moves the rows, and Attio holds the People your reps work. Sizing the list is free. The CSV export in step 2 needs Solo.
The example audience is VPs of sales in the US. Swap the filters for yours.
TL;DR
- Time: about 90 minutes for a 200-row pilot, including the Attio API key and the n8n canvas.
- Records: 200 to 400 LeadOcean records for the pilot. Sizing is free. The free plan holds 1,000 records, one-off, but bulk CSV export is not on Free, so the pilot export needs Solo.
- Cost per 10,000 contacts: $97 for LeadOcean Solo, $0 for self-hosted n8n, and $44 for one Attio Plus seat. That is $141 in a month at monthly list prices, or $132 with Attio billed yearly.
Prerequisites
- A LeadOcean key in
$LEADOCEAN_API_KEY. Sign up at app.leadocean.io/register. No card needed. Free covers step 1. The step 2 export needs Solo ($97 a month) or Agency ($497 a month). - An n8n instance. The Community Edition is free to self-host, and Cloud plans bill by workflow execution (n8n pricing, September 2026).
- An Attio workspace and an API key for it. Attio supports a single-workspace API key or OAuth 2.0 (Attio docs, September 2026). Test in a throwaway workspace first.
curlfor the first two steps.
Sending is outside this guide. Point your sender, or Attio's own email tools, at the People this stack creates.
Step 1: Size the list before you spend anything
Count first. A search with count=true and limit=1 is free (LeadOcean API reference, September 2026).
curl -X POST "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":["US"],"emailType":["work"],"emailStatus":["verified"],"limit":1}'Read meta.total. It is capped at 100,000. Filters join with AND, and several values inside one filter join with OR.
Step 2: Export the pilot list
Export 200 rows, not the whole audience. Bulk CSV export is included on Solo and Agency, not on Free. The export is asynchronous and reserves records up front (LeadOcean docs, September 2026).
curl -X POST https://api.leadocean.io/v1/exports \
-H "x-api-key: $LEADOCEAN_API_KEY" \
-H "Content-Type: application/json" \
-d '{"name":"vp-sales-us pilot","filters":{"jobLevel":["VP"],"jobFunction":["Sales & Business Development"],"country":["US"],"emailType":["work"],"emailStatus":["verified","catch_all_valid"]},"limit":200,"columns":["person_id","profile_first_name","profile_last_name","profile_url","current_job_title","current_company_name","current_company_domain","email_status","email_N_address","email_N_status"],"caps":{"emails":1}}'The answer is a 202 with an export id. Poll GET /v1/exports/{id} until status is done. You do not need to download it by hand: n8n does that in step 3.
One export takes up to 50,000 rows, and an account takes 500,000 rows a day. Email columns need a key with the enrich scope.
Step 3: Run the export from n8n
Build the same call as an n8n workflow so a schedule can repeat it. The pattern is the one on the n8n integration page: create, wait, check, download, parse.
- Create a Header Auth credential with Name
x-api-keyand your LeadOcean key as the Value. - HTTP Request, POST
https://api.leadocean.io/v1/exports, JSON body from step 2. - Wait node, then HTTP Request GET
https://api.leadocean.io/v1/exports/{{ $json.data.id }}. - If node:
data.statusequalsdone. The false branch loops back to Wait. - HTTP Request GET
https://api.leadocean.io/v1/exports/{{ $json.data.id }}/download, Response Format set to File. - Extract From File, operation Extract From CSV.
Header auth and bearer auth are both generic credential types in n8n (n8n docs, September 2026). You now have one n8n item per person.
Step 4: Drop risky emails and shape the Attio payload
LeadOcean tags every address with email_status. The export in step 2 already asked for verified and catch_all_valid only. This Code node is a second guard, and it builds the body Attio expects.
// n8n Code node, "Run Once for All Items"
const ok = ['verified', 'catch_all_valid'];
return $input.all()
.filter(i => ok.includes(i.json.email_1_status) && i.json.email_1_address)
.map(i => {
const r = i.json;
return { json: { body: { data: { values: {
email_addresses: [r.email_1_address],
name: [{
first_name: r.profile_first_name,
last_name: r.profile_last_name,
full_name: `${r.profile_first_name} ${r.profile_last_name}`
}],
job_title: r.current_job_title,
linkedin: r.profile_url,
company: [{
target_object: 'companies',
target_record_reference: {
match: { domains: [r.current_company_domain] },
if_unmatched: 'create',
create: { name: r.current_company_name }
}
}]
} } } } };
});Attio requires all three name properties when you write the object syntax (Attio docs, September 2026). The company value uses Attio's record-reference format, which matches on domain and creates the company if it is missing (Attio docs, September 2026).
Step 5: Upsert People into Attio
Add an HTTP Request node. Method PUT, URL https://api.attio.com/v2/objects/people/records?matching_attribute=email_addresses, a Bearer Auth credential holding your Attio key, and the JSON body set to the expression {{ $json.body }}.
The upsert updates a person when the email matches and creates one when it does not. The matching attribute is required, and Attio recommends a unique one such as an email address (Attio docs, September 2026). That makes reruns safe: the same export twice does not duplicate People.
The same call from a terminal, to test before you wire the node:
curl -X PUT "https://api.attio.com/v2/objects/people/records?matching_attribute=email_addresses" \
-H "Authorization: Bearer $ATTIO_API_KEY" \
-H "Content-Type: application/json" \
-d '{"data":{"values":{"email_addresses":["jane.doe@acme.com"],"name":[{"first_name":"Jane","last_name":"Doe","full_name":"Jane Doe"}],"job_title":"VP Sales"}}}'Now set the node's pace. Attio allows 25 write requests a second. A request that creates a referenced company counts as a second write (Attio docs, September 2026). Under Options, add Batching with 10 items per batch and a 1000 ms interval. That is at most 20 writes a second (n8n docs, September 2026). A 10,000-row run then takes about 17 minutes.
Step 6: Scale from 200 rows to 10,000
Change limit from 200 to 10000 in the step 2 body. Nothing else changes.
{"name":"vp-sales-us full","filters":{"jobLevel":["VP"],"jobFunction":["Sales & Business Development"],"country":["US"],"emailType":["work"],"emailStatus":["verified","catch_all_valid"]},"limit":10000,"columns":["person_id","profile_first_name","profile_last_name","profile_url","current_job_title","current_company_name","current_company_domain","email_status","email_N_address","email_N_status"],"caps":{"emails":1}}Check the records first: the create response reports perRow, which is 1 or 2.
Run the workflow on a schedule only after one manual pilot looks right in Attio. Narrow the filters instead of exporting everything. A single export holds 50,000 rows at most.
Two habits keep the run cheap. Re-size the list with a free count before each scheduled run, so you know the record cost before it is reserved. And keep the person_id column in the export. It is stable across exports, so you can store it in a custom text attribute on the Attio person if you want a trail back to the LeadOcean record. The payload in step 4 does not write it, so add that field yourself.
Step 7: Cost a 10,000-contact run and know where each tool stops
Price the run before you scale. This script is arithmetic from list prices, not a quote.
contacts = 10_000
leadocean_solo = 97 # flat per month, fair-use pace, no per-record price
n8n_self_hosted = 0 # Community Edition, your own hosting not included
attio_plus = (44, 35) # per seat: monthly, billed yearly
print("Monthly list:", leadocean_solo + n8n_self_hosted + attio_plus[0])
print("Attio billed yearly:", leadocean_solo + n8n_self_hosted + attio_plus[1])
print("Per contact:", round((leadocean_solo + attio_plus[0]) / contacts, 4))It prints $141 a month at list prices with one Attio seat, $132 with Attio billed yearly, and about $0.014 a contact. Self-hosted n8n is free but not free of hosting.
The inputs come from public pages:
- LeadOcean Solo is $97 a month flat, with a fair-use pace instead of credits. A 10,000-contact run uses 10,000 to 20,000 records. Agency is $497 a month (LeadOcean pricing, October 2026).
- n8n counts one run of the whole workflow as one execution, however many rows it moves. Cloud Starter is 20€ a month billed yearly for 2.5k executions, and Pro is 50€ billed yearly for 10k (n8n pricing, September 2026). This page prices self-hosted n8n, so the Cloud figures are for comparison only.
- Attio Plus is $44 per user a month, or $35 billed yearly, with 250,000 records. Pro is $99, or $79 billed yearly (Attio pricing, September 2026). Free is $0 with up to 3 seats and 50,000 records. The plan table has an "API and webhook access" row, so confirm your plan has it before you build step 5.
Ten thousand People plus their companies fit inside the Plus and Free record limits.
Where each tool stops:
- LeadOcean stops at the list. It has no sequencer, dialer, CRM or Chrome extension.
email_statusis a signal, and there is no refund for bounced emails. - n8n stops at orchestration. It moves rows and runs rules you write. It supplies no contact data and sends no email on its own.
- Attio stops at the CRM and its email tools. Sequences are listed on Pro, and Plus caps bulk sends at 50 emails at once (Attio pricing, September 2026). For high-volume cold sending, point a dedicated sender at the People instead.
What you get
Step 1 is a free count. We ran the same audience through LeadOcean's MCP leadocean_count_leads tool on 2026-10-21 with emailType work and emailStatus set to verified.
jobLevel VP, jobFunction Sales & Business Development, country US, emailType work, emailStatus verified
total 26988 totalIsExact true distinctCompanies 16114 credits 0That is a count of verified work addresses, not a total for the audience. Adding catch_all_valid, as the export in step 2 does, gave the same 26,988 that day. Your filters will give a different number.
The export create call answers with the shape below. This is the response shape from the API reference, with placeholder values.
{
"success": true,
"data": {
"id": "EXPORT_ID",
"name": "vp-sales-us pilot",
"status": "queued",
"requested": 200,
"estimatedTotal": 26988,
"perRow": 1,
"remaining": null,
"pollAfter": 5
}
}requested is the records reserved. remaining is null on a paid plan, which has a pace rather than a balance. After step 5 each person sits in Attio with a name, email, job title, LinkedIn URL and a linked company.
Troubleshooting
| Error | Cause | Fix |
|---|---|---|
| 401 "Missing or malformed x-api-key" | The LeadOcean header is missing or wrong. | Send x-api-key: $LEADOCEAN_API_KEY on every LeadOcean node. |
| 402 on export create | Fewer records are left than the export reserves. | Lower limit. On Free the 1,000 records are one-off, and bulk CSV export is not on Free. |
| 429 from LeadOcean | More requests a second than your plan allows (5 on Free, 50 on Solo, 200 on Agency), or an export ceiling. | Honour Retry-After. Exports allow 500,000 rows a day. |
| 429 from Attio | More than 25 write requests a second. | Lower the batch size in step 5. The response carries a Retry-After date (Attio docs, September 2026). |
| Zero rows back | A filter value is outside the catalogue, such as United States for US. | Read the valid values from GET /v1/enums. |
| 400 on export create | limit is over 50,000, or a filter cannot be applied. | Split into several exports with narrower filters. |
FAQ
Do I need n8n at all?
Not for a one-off load. Attio can import a CSV directly. n8n earns its place when the run repeats, or when you want rules between the list and the CRM.
Can an AI agent do the list step instead?
Yes. n8n's MCP Client Tool node can use LeadOcean's MCP server at https://api.leadocean.io/mcp. Setup is on the n8n MCP page. Each person a tool returns counts one record.
How many records does a full run cost?
One export row costs one record, or two when the create response shows perRow of 2. Solo at $97 a month and Agency at $497 a month have no per-record price, only a fair-use pace. See pricing.
Can I send from Attio?
Attio lists sequences on Pro and enhanced email sending on Plus (Attio pricing, September 2026). If you already run a sender, compare the VerifyHQ and ReachHQ stack or the Smartlead and Pipedrive stack.
What if my CRM is HubSpot?
The lemlist and HubSpot stack covers it. Back to the blog hub.
Build your first LeadOcean list for n8n and Attio. Start free.
Free to start. No credit card. 1,000 records to spend whenever you like.
Get your free API key →