POST /v1/companies/enrich takes one domain or LinkedIn company URL and returns the company record, including annual revenue in USD. It costs 1 record per company found. A 404 costs nothing.
Revenue has no endpoint of its own. It is one field in the company record, company_data.company_revenue_in_usd. This page is the developer reference. For what the field is and how thin it is, read Company revenue data. The same lookup is the leadocean_get_company tool over MCP.
Quick start (curl)
Create a free account, copy a key with the enrich scope and export it as LEADOCEAN_API_KEY.
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"}'No domain? Send the LinkedIn company page instead. The endpoint also answers GET with the same fields as query parameters.
curl -s -G "https://api.leadocean.io/v1/companies/enrich" \
--data-urlencode "linkedin_url=https://www.linkedin.com/company/acme" \
-H "x-api-key: $LEADOCEAN_API_KEY"Node.js (fetch)
const res = await fetch("https://api.leadocean.io/v1/companies/enrich", {
method: "POST",
headers: {
"x-api-key": process.env.LEADOCEAN_API_KEY,
"content-type": "application/json",
},
body: JSON.stringify({ domain: "acme.com" }),
});
if (res.status === 404) {
console.log("No record for this company. Not billed.");
} else if (!res.ok) {
throw new Error(`${res.status} ${(await res.json()).error?.message}`);
} else {
const { data } = await res.json();
const revenue = data.company_data.company_revenue_in_usd;
console.log(revenue ?? "Company found, revenue not held");
}Python (requests)
import os
import requests
r = requests.post(
"https://api.leadocean.io/v1/companies/enrich",
json={"domain": "acme.com"},
headers={"x-api-key": os.environ["LEADOCEAN_API_KEY"]},
timeout=10,
)
if r.status_code == 404:
print("No record for this company. Not billed.")
else:
r.raise_for_status()
company = r.json()["data"]["company_data"]
print(company.get("company_revenue_in_usd") or "Company found, revenue not held")Request
Send one identifier. The key needs the enrich scope.
| Parameter | Type | Required | Meaning |
|---|---|---|---|
domain | string | One of the two | Website domain, for example acme.com. Preferred key. |
linkedin_url | string (URL) | One of the two | LinkedIn company page URL. |
There is no lookup by company name. With only a name, call POST /v1/companies/search with the name filter, then send the domain you get back. The older POST /v1/companies/enrich still works as a deprecated alias.
Response
Response shape from the API reference. Every value below is a placeholder, and the real record carries many more fields.
{
"success": true,
"data": {
"company_data": {
"company_name": "Acme",
"company_domain": "acme.com",
"company_employees": {
"number_of_employees": 240,
"number_of_employees_code": "201-500"
},
"company_revenue_in_usd": 38000000,
"company_revenue_in_usd_code": "<band code>",
"company_financials": []
},
"company_metrics": { "completion_score": 94, "marketability_score": 81 },
"meta": { "sources": ["own"], "schema": "leadocean.company.v1" }
},
"meta": { "credits": 1, "source": "own" }
}| Field | Meaning |
|---|---|
company_data.company_revenue_in_usd | Annual revenue in US dollars. null when we hold none. |
company_data.company_revenue_in_usd_code | The same revenue as a banded code. Use it when a range is enough. |
company_data.company_financials[] | Yearly revenue and growth, where we have them. Empty when we hold none. |
company_data.company_employees | Headcount and a size band. A sanity check on the revenue. |
company_metrics | completion_score and marketability_score. |
meta.fetched_at | When the record was fetched. The docs do not say which year the revenue refers to, so check this and company_financials[]. |
meta.credits | Records charged for this call. |
Unknown values are null, never guessed. Arrays that hold nothing come back empty. The docs call revenue thinly held: a found company with no revenue is normal, not an error.
Errors
Every failure is { "success": false, "error": { "message": "...", "details": ... } }. Status codes are from the limits docs, September 2026.
| Status | Meaning | What to do |
|---|---|---|
| 400 | Validation failed. details names the fields. | Send domain or linkedin_url, as a string. Not billed. |
| 401 | Missing, wrong or revoked key. | Check the x-api-key header. |
| 402 | Free plan: the 1,000 records are spent. They do not reset. | Upgrade. Not billed. |
| 403 | The key lacks the enrich scope, or the account is suspended. | Create a key with the scope. |
| 404 | We hold nothing for this company. | Stop. It costs nothing. Try the other identifier. |
| 429 | Over 100 requests a second, or 1 a minute on a paid account past its records. | Wait for Retry-After, then retry. Not billed. |
| 503 | The store behind the lookup is unreachable (code: no_source). | Retry with backoff. Never billed. |
Rate limits and cost
One company found is 1 record, by domain or by LinkedIn URL. A found company with company_revenue_in_usd: null still costs 1 record, because you received the company. A 404, 400, 401, 402, 403, 429 or 503 costs nothing.
The limit is 100 requests per second per key, on every plan. Free is 1,000 records, one-off, no card. Pro is $499 a month, flat, with no per-record price inside fair use. See pricing.
Before a large run, test your hit rate. Send a few hundred domains and count how many return a non-null revenue. The docs state no share for revenue, so this is the only honest number. Read GET /v1/account first: it is free and shows records left.
Bulk
For a list of companies, loop this endpoint. At 100 requests a second, 10,000 domains is under two minutes of calls, and only the companies you receive are billed. To find companies by size instead, POST /v1/companies/search takes minRevenue and maxRevenue in USD and returns only records where we hold a figure. It costs 1 record per company returned. See find companies by revenue.
POST /v1/exports is the bulk route for people, not companies. Its columns cover each person and their current company (current_company_name, current_company_domain, current_company_industry) but not revenue. Use it for the contacts at the companies you screened, with numbered columns such as email_N_address and phone_N_number. They come back as email_1_address, phone_1_number and so on, and caps sets how many.
curl -X POST https://api.leadocean.io/v1/exports \
-H "x-api-key: $LEADOCEAN_API_KEY" -H "content-type: application/json" \
-d '{
"name": "VPs at Acme-sized companies",
"filters": { "domain": ["acme.com"], "jobLevel": ["VP"] },
"limit": 500,
"columns": ["person_id", "profile_full_name", "current_job_title", "email_N_address"],
"caps": { "emails": 1 }
}'The reply is a 202 with a job id. Poll GET /v1/exports/{id}, then fetch /v1/exports/{id}/download. An export takes up to 50,000 rows and bills 1 record per row.
FAQ
Does a company with no revenue cost a record?
Yes. A 200 with company_revenue_in_usd: null costs 1 record, because the company came back. A 404, where we hold nothing for the company, costs nothing. You pay for the company, not the field.
Can I look up revenue by company name?
Not on this endpoint. Search companies by name, take the domain from the result, then send it here. Search costs 1 record per company returned, so use count=true with limit=1 to size first. That call is free.
Can an agent call it without an API key?
Yes. The MCP server at https://api.leadocean.io/mcp signs in with OAuth 2.1, so no key is pasted. The tool is leadocean_get_company, 1 record. Setup is on Company Revenue over MCP. For a wider view of the category, see company enrichment APIs and the best company data APIs.
Get your company revenue API key free
Free to start. No credit card. 1,000 records to spend whenever you like.
Get your free API key →