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

How-to guide

How to Automate Prospecting (With a Daily Script and an API)

Prospecting automation is a saved filter, a daily pull, a spend cap and a hand-off. Here are the queries and the script.

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

Prospecting automation means a saved filter that runs on a schedule, drops new people into a file and stops at a spend cap. You do not need a platform for that. You need one API key and a cron job.

TL;DR

  • Automated prospecting is four parts: a filter, a daily pull, a cap, a hand-off. Setup takes about 30 minutes.
  • Sizing the list is free. The script spends at most 25 records a day, plus one record per person you choose to enrich.
  • The free plan (1,000 records for life, no card) covers about a month of daily pulls at 25 a day.

Prerequisites

  • A LeadOcean API key in $LEADOCEAN_API_KEY. Free works: both search and enrich scopes are on.
  • curl, jq and a machine with cron (or any scheduler that runs a shell script).
  • A place to hand leads off to: a CSV, a sequencer import or your CRM. LeadOcean is the data source. It has no sequencer, dialer or CRM.
  • 5 requests a second is the Free limit. The script below sends far fewer.

Step 1: Write the filter once

Pick the filter by what makes someone worth a message today. Keep it narrow. A wide filter burns your daily cap on people you would skip.

Enum filters are exact strings. Look the value up first, because Vice President returns nothing and VP works.

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

Save the filter as a file you can version:

json
{
  "jobLevel": ["VP"],
  "jobFunction": ["Sales & Business Development"],
  "country": ["US"],
  "employeeRange": ["51-200"],
  "emailStatus": ["verified", "catch_all_valid"]
}

Name it filter.json. Search takes a flat JSON body and every filter is an array.

Step 2: Size it before you automate it

A filter that matches 40 people runs dry in two days. A filter that matches 400,000 never reaches the good ones. Count first. A count costs 0 records.

bash
curl -s -X POST "https://api.leadocean.io/v1/people/search?count=true&limit=1" \
  -H "x-api-key: $LEADOCEAN_API_KEY" \
  -H "content-type: application/json" \
  -d @filter.json | jq '.meta'

count=true goes in the query string, not the body. meta.total is capped at 100,000. In an AI agent you can ask the same question with the MCP tool leadocean_count_leads, which also returns distinctCompanies.

Step 3: Pull a page a day by cursor

The script reads the last cursor from a file, fetches 25 people and saves the next cursor. Each person returned is one record, so the daily cost is fixed at 25.

bash
#!/usr/bin/env bash
set -euo pipefail
cd "$(dirname "$0")"
touch cursor.txt
CURSOR="$(cat cursor.txt)"

BODY="$(jq --arg c "$CURSOR" '. + {limit: 25} + (if $c != "" then {cursor: $c} else {} end)' filter.json)"

RESP="$(curl -s -X POST "https://api.leadocean.io/v1/people/search" \
  -H "x-api-key: $LEADOCEAN_API_KEY" \
  -H "content-type: application/json" \
  -d "$BODY")"

echo "$RESP" | jq -r '.meta.nextCursor // ""' > cursor.txt
echo "$RESP" | jq -c '.data[]' >> leads.jsonl
echo "$(date -u +%F) pulled $(echo "$RESP" | jq '.data | length')" >> run.log

One search pages to 10,000 rows. When meta.depthCapped is true there is no nextCursor, so the script restarts from the top on the next run. Narrow the filter before that happens, for example by adding a country or a headcount band.

Step 4: Enrich only the people you will contact

Search rows carry no contact points. Enrich the ones that pass your own check, such as a title match or a company you already track. Enrich takes exactly one of linkedin_url, email, phone or person_id.

bash
jq -r '.meta.source_ids.person_id' leads.jsonl | head -5 | while read -r id; 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 "{\"person_id\": \"$id\", \"reveal_email\": true}"
  sleep 1
done >> enriched.jsonl

reveal_email costs no extra record. A person we hold nothing for is a 404 and costs nothing. Since 2026-10-17 enrich also returns career history in resume_data, which is useful for a first line. Read email_status before you send, because bounced emails are not refunded.

Step 5: Schedule it and cap the spend

Put export LEADOCEAN_API_KEY=... in a private env.sh file, then run the job at 07:00 on weekdays:

bash
0 7 * * 1-5 . /opt/prospecting/env.sh && /opt/prospecting/daily.sh

Check your balance in the script's first line with GET /v1/account, which is free, and exit when it is low. On Free the key answers 402 once the 1,000 records are spent. Paid plans have no credits. Solo ($97 a month) and Agency ($497 a month) run at a fair-use pace. A key that reaches it slows down instead of stopping.

For a bigger one-off audience, use POST /v1/exports (Solo and above, 50,000 rows a request, 500,000 a day) or the Exports page in the app at app.leadocean.io. It shows the record price before you start.

Step 6: Hand off and de-duplicate

Prospecting automation fails quietly when the same person shows up twice. Keep a ledger of person_id values and skip any you have already pulled or contacted.

bash
jq -r '.meta.source_ids.person_id' leads.jsonl | sort -u > seen.txt
wc -l seen.txt

Then convert the file you keep into the import format your sender expects. Build it from enriched.jsonl, where each line holds one person under data. Drop any row whose data.contact_data.email_status is not verified or catch_all_valid. Review the first week by hand. If more than a few rows look wrong, tighten the filter in Step 1 instead of adding rules downstream.

Two habits keep the job cheap. Re-count the filter once a month, because the dataset is refreshed monthly and totals move. And change one filter at a time, so you know which edit changed the list.

What you get

A free count for the Step 1 filter without its emailStatus line, run on 2026-10-21 through the leadocean_count_leads MCP tool (jobLevel VP, jobFunction Sales & Business Development, country US, employeeRange 51-200):

json
{
  "total": 18567,
  "totalIsExact": true,
  "totalAll": 60456,
  "distinctCompanies": 12192,
  "credits": 0
}

total counts people with a verified, catch_all_valid or catch_all email (the tool's default). totalAll is the same filters with no email condition. That is 18,567 mailable people at 12,192 companies. The emailStatus line in filter.json drops catch_all, so your own total will be lower. At 25 a run, the 10,000-row depth cap arrives after 400 runs, so narrow the filter before then.

Each search row has this shape (response shape from the API reference, placeholder values only):

json
{
  "success": true,
  "data": [
    {
      "profile_data": { "profile_full_name": "Jane Doe" },
      "contact_data": {
        "has_email": true,
        "email_status": "verified",
        "contact_current_experiences": [{ "company_name": "Acme" }]
      },
      "resume_data": {},
      "meta": { "source_ids": { "person_id": "1234567890" } }
    }
  ],
  "meta": { "count": 25, "credits": 25, "nextCursor": "eyJ..." }
}

Troubleshooting

ErrorCauseFix
401Missing or wrong x-api-key headerExport $LEADOCEAN_API_KEY in the cron environment. Cron does not read your shell profile.
429Above the plan's rate limit (5 a second on Free, 200 on Agency)Honor the Retry-After header and keep the sleep in the loop.
402Out of records (Free is 1,000 for life)Add a plan at leadocean.io/pricing. Counts still work.
Empty dataBad enum value, such as Vice President instead of VPLook the value up with GET /v1/enums or leadocean_list_enum_values.
Same people every runcursor.txt is empty or not writtenCheck the jq line and file permissions.
Export refusedOver 50,000 rows a request or 500,000 a daySplit the audience with narrower filters.

FAQ

What should I automate in prospecting?

Automate list building and enrichment. Those are mechanical. Keep the message and the judgment about who is worth contacting with a person.

How many records does a daily pull cost?

One per person returned, so 25 a day at limit: 25. Counts, empty pages and enrichments that find nothing cost 0.

Can an AI agent run this instead of cron?

Yes. The hosted MCP server at https://api.leadocean.io/mcp has 13 tools, including free leadocean_count_leads and leadocean_list_filters. Sign-in is OAuth, so no key is pasted.

Is this a sequencer?

No. It produces a clean list. Send from the tool you already use. See the B2B data prospecting guide for the full size, search, enrich flow.

Where do I go from here?

Read lead generation or prospecting for the distinction, best lead generation software for SDRs for tools that send, and pricing for plan limits.

Automate your prospecting on a free API key

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

Get your free API key →