Pre-Black Friday: 20% off, for as long as you stayEARLYBFon Solo and AgencyEnds Nov 28Start free

How-to guide

How to run GDPR-compliant B2B prospecting (with the LeadOcean API)

Five steps from a country filter to a logged, suppressible list. Real requests, real output, no legal theatre.

Get your free API key →Free to start. No credit card. 1,000 records to spend whenever you like.

GDPR-compliant B2B prospecting comes down to five habits. Collect only what you need. Record your legal basis. Tell people where you got their data. Honour opt-outs fast. Keep a suppression list. This guide turns each habit into a request you can run. It is engineering guidance, not legal advice. Ask counsel before you send.

TL;DR

  • Takes about 30 minutes. Sizing is free. The test pull spends about 25 records, plus 1 per person you enrich.
  • Narrow by country, role and work email so you hold less personal data, not more.
  • Log your legal basis and source per batch, and check every list against a suppression file before you send.

Prerequisites

  • A LeadOcean API key from the free plan (1,000 records for life, no card), exported as $LEADOCEAN_API_KEY.
  • curl and jq.
  • A file called suppression.txt with one email per line. It can start empty.
  • A short legitimate interest assessment written down. Recital 47 says direct marketing may be a legitimate interest, but it needs careful assessment (GDPR Recital 47, September 2026).

Step 1: Decide the scope before you pull anything

Data minimisation starts with the filter, not the export. Write down the market, the role and the reason. Then express exactly that and nothing wider.

Example scope: VPs of sales in Germany, France and the Netherlands, work email only, for a sales tooling offer. Your assessment should say why that role would reasonably expect the message.

Check the exact enum strings first. They are case sensitive.

bash
curl -s "https://api.leadocean.io/v1/enums" \
  -H "x-api-key: $LEADOCEAN_API_KEY" | jq 'keys'

Use the MCP tool leadocean_list_enum_values if you work from Claude. Country takes ISO codes (DE, FR, NL). Job level takes VP. Job function takes Sales & Business Development.

Step 2: Size the list for free

Count before you search. A count spends no records and tells you whether the scope is worth a send.

bash
curl -s "https://api.leadocean.io/v1/people/search?count=true&limit=1" \
  -H "x-api-key: $LEADOCEAN_API_KEY" \
  -H "content-type: application/json" \
  -d '{
    "country": ["DE","FR","NL"],
    "jobLevel": ["VP"],
    "jobFunction": ["Sales & Business Development"]
  }' | jq '.meta'

If you work through Claude with the hosted MCP server, leadocean_count_leads does the same for free. count=true goes in the query string, not the body. The response has meta.countOnly: true and an empty data array. meta.total is capped at 100,000.

Step 3: Pull only work emails you can mail

Personal addresses carry more risk and rarely help B2B outreach. Leave them out. Ask for work addresses with a mailable status.

bash
curl -s "https://api.leadocean.io/v1/people/search" \
  -H "x-api-key: $LEADOCEAN_API_KEY" \
  -H "content-type: application/json" \
  -d '{
    "country": ["DE","FR","NL"],
    "jobLevel": ["VP"],
    "jobFunction": ["Sales & Business Development"],
    "emailType": ["work"],
    "emailStatus": ["verified","catch_all_valid"],
    "limit": 25
  }' > batch_001.json

Each person returned costs one record. Page with cursor from the response. One search pages to 10,000 rows; narrow the filters instead of walking the index. For bigger volumes use POST /v1/exports (up to 50,000 rows a request, 500,000 a day, not on the Free plan).

Leave out what the campaign does not use. Skip phone numbers for an email-only campaign, skip personal addresses, and do not store career history (resume_data) for a cold email. "We pulled work email and job title because the message is about their job" is easy to defend.

LeadOcean refreshes the dataset monthly, and each record carries its own fetched_at date. Store it. If a record is old, re-enrich before you send.

Article 14 covers data you did not collect from the person. You must tell them within one month at the latest, including the legitimate interest you rely on (GDPR Article 14, September 2026). That means a record of where each batch came from.

bash
jq -n --arg file "batch_001.json" '{
  batch: $file,
  pulled_at: (now | todate),
  source: "LeadOcean people search",
  basis: "legitimate interest (Art. 6(1)(f))",
  assessment: "lia-2026-10-sales-tooling.pdf",
  notice_due: ((now + 2592000) | todate)
}' >> prospecting_log.jsonl

The notice is usually your first email, with a privacy link and a plain opt-out. National e-marketing laws sit on top of GDPR and differ by country. Check each market you send into with counsel.

Step 5: Re-check and reveal just before you send

Data ages and people change jobs. Writing to someone about a role they no longer hold hurts deliverability and is a privacy problem.

Search rows carry no addresses. Enrich each person you will mail by the id on their search row (meta.source_ids.person_id), with reveal_email set to true. A hit counts one record. A person we hold nothing for is a 404 and costs nothing.

bash
curl -s -X POST "https://api.leadocean.io/v1/people/enrich" \
  -H "x-api-key: $LEADOCEAN_API_KEY" \
  -H "content-type: application/json" \
  -d '{"person_id": "1234567890", "reveal_email": true}' \
  | jq -r '.data.contact_data.contact_emails[0].email' >> emails.txt

The id above is a placeholder. Before you append, read data.contact_data.email_status and the current role in data.contact_data.contact_current_experiences, and drop anyone who no longer fits your scope.

Step 6: Apply the suppression list and honour objections

People can object to direct marketing at any time, and then you must stop using their data for it (GDPR Article 21, September 2026). A suppression list is how you make that real.

bash
grep -vFxf suppression.txt emails.txt > sendable.txt
wc -l sendable.txt

Append every unsubscribe and objection to suppression.txt the same day, and check it before every send. Never delete an address from that file, or you will mail them again. Any sequencer, Clay table or n8n flow that receives the list must honour the same file. LeadOcean has no sequencer of its own, so the send step lives in a tool you choose.

Set a retention period too. If a contact has not replied within it, delete their row from your own tools and keep only the address in the suppression file.

What you get

A real free count, run on 2026-10-21 with the MCP tool leadocean_count_leads. Filters: country DE, FR, NL, jobLevel VP, jobFunction Sales & Business Development, emailType work. The tool counts verified, catch_all_valid and catch_all work addresses by default.

json
{
  "total": 2685,
  "totalIsExact": true,
  "totalAll": 5787,
  "distinctCompanies": 1565,
  "credits": 0
}

So 2,685 people at 1,565 companies match with a mailable work email, out of 5,787 matching people with no email condition. The count spent 0 records. Your batch_001.json from Step 3 has the people themselves.

Troubleshooting

ErrorCauseFix
401Missing or wrong x-api-key headerRe-export $LEADOCEAN_API_KEY and send it as x-api-key
429Above the plan's rate limit (5 a second on Free, 200 on Agency)Wait for Retry-After, then slow the loop
402Out of records (Free is 1,000 for life)Check GET /v1/account, then pick a plan
Empty dataWrong enum string, such as vp for jobLevelLook up the exact value with GET /v1/enums
data empty on a count callYou sent count=true, which returns totals onlyRemove count=true to get rows
Export refusedOver 50,000 rows a request or 500,000 a day, or on the Free planSplit the export or move to a paid plan

FAQ

Is B2B cold email legal under GDPR?

Often yes, on a legitimate interest basis, if you document it and respect objections. Recital 47 allows direct marketing as a legitimate interest after careful assessment. National e-marketing laws can add rules on top, so check each country with counsel.

Should I use personal emails?

Not for B2B outreach. Filter emailType to work and mail people about their job.

How do I prove where a contact came from?

Keep the batch file and the pull date in your log, as in Step 4, plus the record's fetched_at date.

How many records does the whole guide cost?

Sizing is free. The test pull costs one record per person returned, so 25 records at limit 25, plus one record for each person you enrich in Step 5.

Hub: sales prospecting guides. Related: best AI tools for sales prospecting, B2B outbound sales, GDPR-compliant B2B data providers, and pricing.

Size your EU prospect list for free, then pull only what you need

Free to start. No credit card. 1,000 records to spend whenever you like.

Get your free API key →