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

How-to guide

How to Source a TAM (With Real Queries, Not a Spreadsheet)

Size the market with a free count, page the accounts out with a cursor, then pull buyers only where the list is worth it.

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

TL;DR

  • TAM sourcing is turning a market definition into a list of named accounts. Do it as three calls: a free count, a company search that pages by cursor, and a people pull for the accounts you keep.
  • It takes about 30 minutes. Counting spends 0 records. The company list in this example is 728 records, so it fits the 1,000 on the Free plan.
  • Count first, pull second. A count is free, so you can tighten the filters until the list is the size you will actually work.

Prerequisites

  • A LeadOcean account from app.leadocean.io. Free is 1,000 records for life, no card, 5 requests a second.
  • An API key in your shell: export LEADOCEAN_API_KEY=...
  • curl and jq. Python 3 for the paging loop.
  • Optional: the hosted MCP server at https://api.leadocean.io/mcp, if you would rather ask Claude to run the counts.

Step 1: Write the market as filters

Write the TAM as filters before you touch the API. Each filter is a field the data holds, so the definition is testable.

Our example: US software companies with 51 to 500 employees. In LeadOcean terms that is industry, hqCountry and employeeRange.

json
{
  "industry": ["Computer Software"],
  "hqCountry": ["US"],
  "employeeRange": ["51-200", "201-500"]
}

Enum values are exact strings. industry has 534 of them and hqCountry takes ISO codes. A wrong value gives an empty result, not an error. Look values up first. The enum endpoints need no key.

bash
curl -s "https://api.leadocean.io/v1/enums/industry?q=software" | jq '.data.values'

In Claude, the MCP tool leadocean_list_enum_values does the same with a substring search, for example name: "industry", q: "software".

Step 2: Count the accounts for free

A company count returns an exact total and breakdowns by industry, headcount band, HQ country, HQ state and NAICS. It spends no records.

bash
curl -s -X POST "https://api.leadocean.io/v1/companies/count" \
  -H "x-api-key: $LEADOCEAN_API_KEY" \
  -H "content-type: application/json" \
  -d '{
    "industry": ["Computer Software"],
    "hqCountry": ["US"],
    "employeeRange": ["51-200", "201-500"]
  }' | jq '{total: .data.total, state: .data.facets.hq_state[:5]}'

Run the same filters through the MCP tool leadocean_count_companies and you get the same answer. This is the output we got on 2026-10-21 (see "What you get" below).

Add hasMailableContacts: true to keep only companies with at least one person whose best email is verified, catch_all_valid or catch_all. Use it when the list feeds outbound. Skip it when you are sizing a market.

Step 3: Tighten until the number is workable

Read the breakdown, then cut. If 204 of 728 accounts sit in California, you can split the list by state and work it in waves. If a headcount band is too thin to matter, drop it.

Each change is another free count. There is no reason to pull a list you have not sized.

bash
curl -s -X POST "https://api.leadocean.io/v1/companies/count" \
  -H "x-api-key: $LEADOCEAN_API_KEY" -H "content-type: application/json" \
  -d '{"industry":["Computer Software"],"hqCountry":["US"],"employeeRange":["201-500"]}' \
  | jq '.data.total'

Do not quote a people count that uses funding, revenue, HQ city or HQ state filters: the people count does not apply them reliably. On any count, a filter that cannot apply is named in unsupportedFilters and the total is withheld. Read that field every time.

Step 4: Page the companies out with a cursor

Now spend records. Pull accounts only after the count says the list is the size you want. POST /v1/companies/search takes the same flat JSON body. limit runs 1 to 100, and you page with cursor from meta.nextCursor. One record is one company returned.

python
import os, requests

URL = "https://api.leadocean.io/v1/companies/search"
H = {"x-api-key": os.environ["LEADOCEAN_API_KEY"], "content-type": "application/json"}
body = {
    "industry": ["Computer Software"],
    "hqCountry": ["US"],
    "employeeRange": ["51-200", "201-500"],
    "limit": 100,
    "fields": ["company_data.company_name", "company_data.company_domain"],
}

rows, cursor = [], None
while True:
    if cursor:
        body["cursor"] = cursor
    r = requests.post(URL, headers=H, json=body, timeout=60)
    r.raise_for_status()
    j = r.json()
    rows += j["data"]
    cursor = j["meta"].get("nextCursor")
    if not cursor:
        break

print(len(rows), "companies")

fields trims each record to the dotted paths you name, which keeps the payload small. A search pages to 10,000 rows. If meta.depthCapped is true there is no nextCursor, and the fix is narrower filters, not a longer walk.

728 companies at 100 a page is 8 requests. That is well inside the 5 a second on Free.

A note on what the list is not. It is a snapshot of the database on the day you pull it, refreshed monthly, and each record carries its own fetched_at date. Store the domains, and re-run the free count each month to see whether the market moved.

Step 5: Pull buyers at the accounts you keep

A TAM list is accounts. Outbound needs people. Search people with the same company filters plus the roles you sell to, and count first.

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 '{
    "industry": ["Computer Software"],
    "hqCountry": ["US"],
    "employeeRange": ["51-200", "201-500"],
    "jobLevel": ["VP", "C-Team"],
    "jobFunction": ["Sales & Business Development"]
  }' | jq '.meta'

The same count through leadocean_count_leads returned 145 people with a mailable email across 66 companies on 2026-10-21. Without the email condition it matched 681. Say which one you mean when you quote it.

To get people at one account, call GET /v1/companies/{domain}/people. To export a finished list, POST /v1/exports takes up to 50,000 rows a request and 500,000 a day. Bulk CSV export is not on Free.

Before you send anything, check email_status on each person. Bounced emails are not refunded or credited back, so filter on verified and catch_all_valid first.

What you get

These are the free counts we ran on 2026-10-21 through the MCP tools. No records were spent.

text
leadocean_count_companies
  industry:       Computer Software
  hqCountry:      US
  employeeRange:  51-200, 201-500

  total: 728 (exact)
  employee_range: 51-200 = 460, 201-500 = 268
  hq_state (top 5): California 204, Massachusetts 51, New York 51, Texas 44, Florida 37
  credits: 0
text
leadocean_count_leads
  same company filters + jobLevel VP, C-Team + jobFunction Sales & Business Development

  total (verified, catch_all_valid or catch_all): 145
  totalAll (no email condition): 681
  distinctCompanies: 66
  credits: 0

Below is a company page from Step 4, trimmed with fields. Label: response shape from the API reference, placeholder values.

json
{
  "success": true,
  "data": [
    {
      "company_data": {
        "company_name": "Acme",
        "company_domain": "acme.com"
      }
    }
  ],
  "meta": {
    "count": 100,
    "limit": 100,
    "total": 728,
    "nextCursor": "opaque-cursor-string"
  }
}

Troubleshooting

ErrorCauseFix
401Missing or wrong x-api-key headerCheck that $LEADOCEAN_API_KEY is set in the shell that runs the call.
429Above the plan's rate limit (5 a second on Free, 200 on Agency)Wait for the Retry-After header, then slow the loop.
402Out of records. Free is 1,000 for lifeCount first, narrow the list, or move to a paid plan on pricing.
Empty dataA filter value is not an exact enum stringLook it up with leadocean_list_enum_values or GET /v1/enums/{name}.
No nextCursor, meta.depthCapped trueOne search pages to 10,000 rowsSplit by state, headcount band or industry and run each slice.
Export refusedOver 50,000 rows a request or 500,000 a day, or you are on FreeExport in slices. Bulk CSV export needs a paid plan.

FAQ

What is TAM sourcing?

TAM sourcing is building the actual list of companies (and later people) that make up your total addressable market. Sizing gives you a number. Sourcing gives you names you can work. For the sizing math, see TAM, SAM and SOM and the TAM sizing use case.

How many records does it cost?

Counting costs nothing. Pulling the 728 companies in this example costs 728 records, one per company returned. People are a separate pull, one record per person. See how records are counted.

Do I need a data enrichment tool on top?

Not to get the list. Tools such as Clay describe combining several data providers into one workflow, per their TAM sourcing page (October 2026). Use that route when you want waterfalls across vendors. If one database covers your filters, a filter, a count and a cursor are enough.

Can I run this from Claude instead of curl?

Yes. The hosted MCP server has 13 tools. leadocean_count_companies and leadocean_count_leads are free, so you can size a market in a chat before you spend a record.

Is there a calculator for the sizing step?

Yes: the TAM calculator. For the broader TOFU read, start at from TAM to a working list, and browse the blog hub for related guides.

Source your TAM with one filter and one cursor

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

Get your free API key →