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

How-to guide

How to Look Up Companies by Domain (cURL)

One POST per domain, a retry flag for 429, a loop for a list and a cursor walk for the people at a company.

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

TL;DR

  • Send POST /v1/companies/enrich with {"domain": "acme.com"} and an x-api-key header. A match costs 1 record. A 404 costs nothing.
  • Add --retry 5 and curl waits out a 429 for you. A 429 is free.
  • Setup takes about 10 minutes. Twenty domains spend at most 20 records, which fits the Free plan's 1,000.

Prerequisites

  • A LeadOcean API key in $LEADOCEAN_API_KEY. Get one free at app.leadocean.io. No card.
  • curl 7.66 or newer (for Retry-After handling) and jq.
  • A file domains.txt with one domain per line, such as acme.com.

Step 1: Look up one domain

One call returns one company. The body is flat JSON. You can send linkedin_url instead of domain.

bash
curl -s -X POST https://api.leadocean.io/v1/companies/enrich \
  -H "x-api-key: $LEADOCEAN_API_KEY" \
  -H "content-type: application/json" \
  -d '{"domain": "acme.com"}' | jq .

GET /v1/companies/enrich?domain=acme.com also works. See the company lookup docs (October 2026).

Step 2: Retry on 429

Above your plan's rate limit the API answers 429 with a Retry-After header. The limit is 5 requests a second on Free, 50 on Solo and 200 on Agency. A 429 costs no records.

With --retry, curl treats a 429 as a transient error, retries it and honors Retry-After (curl 7.66 or newer). See the curl manual (September 2026).

bash
curl -s --retry 5 -X POST https://api.leadocean.io/v1/companies/enrich \
  -H "x-api-key: $LEADOCEAN_API_KEY" \
  -H "content-type: application/json" \
  -d '{"domain": "acme.com"}'

Step 3: Loop over a domain list

This script reads domains.txt, writes one JSON object per line and logs misses to stderr. It stops on any status it does not expect, so a bad key or a spent plan does not burn the whole file.

bash
set -u
tmp=$(mktemp)
while read -r d; do
  [ -z "$d" ] && continue
  code=$(curl -s -o "$tmp" -w '%{http_code}' --retry 5 \
    -X POST https://api.leadocean.io/v1/companies/enrich \
    -H "x-api-key: $LEADOCEAN_API_KEY" \
    -H "content-type: application/json" \
    -d "$(jq -nc --arg d "$d" '{domain:$d}')")
  case $code in
    200) jq -c --arg d "$d" '{domain:$d} + .data' "$tmp" ;;
    404) echo "miss: $d" >&2 ;;
    *)   echo "stop: HTTP $code at $d" >&2; exit 1 ;;
  esac
  sleep 0.25
done < "$1"

The sleep 0.25 keeps you near 4 requests a second, under the Free limit. Drop it on Solo or Agency. Save it as lookup.sh and run bash lookup.sh domains.txt > companies.ndjson. Misses print to your terminal and never reach the file.

Two details matter. The jq -nc --arg call builds the JSON body, so a domain with odd characters cannot break the request. And -o "$tmp" -w '%{http_code}' splits the body from the status, which lets the case block treat a 404 as a miss instead of an error.

Step 4: Page through the people at a company

Company enrich returns the company only. To list its people, call GET /v1/companies/{domain}/people and follow meta.nextCursor. Each person returned costs 1 record, so keep limit small while you test.

bash
cursor=""
while :; do
  page=$(curl -s -G "https://api.leadocean.io/v1/companies/acme.com/people" \
    -H "x-api-key: $LEADOCEAN_API_KEY" \
    --data-urlencode "limit=25" --data-urlencode "cursor=$cursor")
  echo "$page" | jq -c '.data[]' >> people.ndjson
  cursor=$(echo "$page" | jq -r '.meta.nextCursor // empty')
  [ -z "$cursor" ] && break
done

Pass the cursor back unchanged. A hand-built or edited cursor gets a 400. A roster stops at 10,000 rows, and the last page then carries meta.depthCapped: true.

Step 5: Resume and dedupe a long run

A 404 costs nothing but a matched domain costs 1 record. Do not pay twice for a domain after a crash. Dedupe the input and skip anything already in the output file.

bash
sort -u domains.txt > todo.txt
jq -r '.domain' companies.ndjson 2>/dev/null | sort -u > done.txt
comm -23 todo.txt done.txt > remaining.txt
bash lookup.sh remaining.txt >> companies.ndjson

Misses are not in done.txt, so a rerun asks about them again. They are free, so that is fine.

Step 6: Check what you spent

Sizing and account calls are free. Read your balance before and after a run with GET /v1/account. The difference is the records you spent.

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

On Free the 1,000 records are one-off. On paid plans there are no credits. Each plan has a fair-use pace, and reaching it slows your key until the window resets. Nothing stops and nothing needs a top-up. See pricing for Solo at $97 a month and Agency at $497 a month.

What you get

Response shape from the company lookup docs (October 2026), placeholder values. Fields we hold nothing for come back as null. company_data also carries funding, revenue, NAICS and SIC codes, and company_number_of_profiles when we hold them.

json
{
  "success": true,
  "data": {
    "company_data": {
      "company_name": "Acme",
      "company_domain": "acme.com",
      "company_type": "Privately Held",
      "company_industries": [{ "name2": "Software Development", "priority": 1 }],
      "company_employees": { "number_of_employees": 120, "number_of_employees_code": "51-200" },
      "company_primary_address": { "city": "San Francisco", "country_code": "US" },
      "company_number_of_detected_technologies": 2
    },
    "company_detected_technologies": [
      { "technology_product": { "name": "Example CDN" }, "technology_category": { "name2": "Content Delivery Network" } }
    ],
    "company_metrics": { "completion_score": 90, "marketability_score": 80 },
    "meta": { "sources": ["own"], "schema": "leadocean.company.v1" }
  },
  "meta": { "source": "leadocean" }
}

Step 3 prepends "domain": "acme.com" to data so each line of companies.ndjson stands alone. Read it back with jq -r '.domain' companies.ndjson.

Troubleshooting

ErrorCauseFix
401Missing or wrong x-api-keyCheck echo ${#LEADOCEAN_API_KEY} is not 0. Copy the key again from the app.
429Above the plan's rate limit (5 a second on Free, 200 on Agency)Keep --retry 5 and the sleep. The response has a Retry-After header. A 429 is free.
402The plan's records are spent (Free is 1,000 for life)Check GET /v1/account, then move to Solo or Agency. The script stops here.
404No company held for that domainCheck the domain: acme.com, not https://www.acme.com. A 404 costs nothing.
400 "Invalid cursor"The cursor was edited, or minted before 2026-09-19Restart the roster walk with no cursor.
meta.depthCapped is trueA roster reached 10,000 rowsNarrow the search instead of paging harder.

FAQ

How many records does a company lookup cost?

One per company returned. A miss, a 404 and a 429 cost nothing. Twenty domains cost at most 20 records.

Can I look up by LinkedIn URL instead?

Yes. Send {"linkedin_url": "..."} in the same body. Use exactly the field you have.

How do I send many domains faster?

Raise the rate with a plan that allows it: 50 requests a second on Solo, 200 on Agency. Run several copies of the script on separate files. Stay below your limit or the retries slow you down.

Where do I find the same lookup in another language?

See the Go version, the C# version and the TypeScript version. Plans are on pricing.

More recipes live in the blog hub.

Look up your first 1,000 company domains free

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

Get your free API key →