Skip to main content
Turn structured ICP filters into an exportable company list. This workflow is company-only — add Outbound pipeline when you need people and emails.

When to use it

  • Building a TAM list for outbound or ABM
  • Seeding a warehouse, CRM import, or internal prospecting tool
  • Validating ICP filters before spending on full pagination

Endpoint sequence

StepEndpointCost
1POST /company-search-countFree
2+POST /company-searchPer company returned — see GET /pricing

Step 1 — Preview size (free)

curl --request POST \
  --url "${HUNTR_BASE_URL}/company-search-count" \
  --header 'content-type: application/json' \
  --header "x-api-key: ${HUNTR_API_KEY}" \
  --data '{
    "query": {
      "industry": { "include": ["Software Development"] },
      "location": { "include": ["US"] },
      "headcount": { ">": 50, "<": 500 }
    }
  }'
If total is larger than your budget or export cap, tighten filters before paginating.

Step 2 — Fetch pages

curl --request POST \
  --url "${HUNTR_BASE_URL}/company-search" \
  --header 'content-type: application/json' \
  --header "x-api-key: ${HUNTR_API_KEY}" \
  --data '{
    "query": {
      "industry": { "include": ["Software Development"] },
      "location": { "include": ["US"] },
      "headcount": { ">": 50, "<": 500 }
    },
    "pagination": { "size": 100 }
  }'

Step 3 — Paginate

  • Default page size: 100 (max 200)
  • Pass pagination.token from the response into the next request
  • Keep the same query on every page
  • Tokens expire — do not pause indefinitely mid-list
{
  "pagination": { "size": 100, "token": "eyJ..." }
}

Engineering notes

Filter rules

  • At least one filter required
  • Fields are AND’d; include values within a field are OR’d
  • industry and type must be exact LinkedIn labels — invalid values return 400 with accepted_values
  • Prefer domain over name when you know the domain

Rate limits

Authenticated endpoints: 5 req/s per key. Add 200ms+ delay between pages in scripts. See Rate limits.

Partitioning large lists

If total exceeds your export cap, partition by geography, headcount band, or industry — run separate queries rather than paginating millions of rows.

Common mistakes

MistakeFix
Changing query between pagesKeep query identical; only update pagination.token
Guessing industry valuesUse accepted_values from a 400 response or test with count first
Using /research for “500 SaaS companies”Use this workflow — research rejects bulk enrichment

Node.js pagination sketch

const BASE = process.env.HUNTR_BASE_URL ?? "https://api.tryhuntr.com";
const KEY = process.env.HUNTR_API_KEY;
const QUERY = {
  industry: { include: ["Software Development"] },
  location: { include: ["US"] },
  headcount: { ">": 50, "<": 500 },
};

async function huntr(path, body) {
  const res = await fetch(`${BASE}${path}`, {
    method: "POST",
    headers: { "content-type": "application/json", "x-api-key": KEY },
    body: JSON.stringify(body),
  });
  if (!res.ok) throw new Error(`${res.status}: ${await res.text()}`);
  return res.json();
}

const all = [];
let token;
do {
  const page = await huntr("/company-search", {
    query: QUERY,
    pagination: { size: 100, ...(token ? { token } : {}) },
  });
  all.push(...page.companies);
  token = page.pagination?.token;
  if (token) await new Promise((r) => setTimeout(r, 250));
} while (token);

Next step