Stop Scraping, Start Querying: Building Account Lists with SQL-Style Search over a 70-Million-Company Database
Every outbound motion starts the same way: someone has to produce a list of companies worth targeting. And in most teams that step is quietly the worst part of the stack. You either click through the network's search filters and export page by page, rent seats in a database tool priced per user per month, or scrape the live network — slowly, against rate limits, with an account you'd rather not burn.
The structural problem is that the network's built-in search was designed for a human browsing, not for a pipeline. It caps results, throttles bursts, and only lets you filter on what the UI exposes. Meanwhile the thing you actually want — "US agencies, 11–200 people, that describe themselves as doing lead generation, founded after 2015, with a real website" — is a database query.
/linkedin/search/sql/companies makes it exactly that. It runs against Anysite's cached database of ~70 million public company pages, filterable on any combination of fields, returning up to 1,000 full company records per call. Price: 1 credit per 10 results.
The endpoint at a glance: a cached 70M-company database behind a query API, priced at 1 credit per 10 results on the API plans.
Why querying a cache beats searching live
1. No rate limits, no fragility. Live people-search endpoints (premium people search, employee lists) go through the network's real infrastructure — they're throttled, they're slow, and every provider on the market has the same constraint. A cached-DB query touches nothing live. You can fire it in a loop, in parallel, at 2 a.m., inside a cron job. It just returns.
2. You can filter on fields the network won't let you filter on. The native UI gives you location, industry, headcount. The SQL endpoint additionally queries:
- full-text over
descriptionandshort_description— how the company describes itself specialities— the self-declared tag list ("B2B Lead Generation", "Appointment Setting", "RevOps"…)hashtagsthe page usesfounded_onranges — "startups founded after 2020" is one parameterwebsitepresence,min_description_length— instant data-quality gates that drop abandoned stub pagessimilar_organizations— the network's own lookalike graph, exposed as datalast_modified_at— when the page last changed
3. Boolean logic that the native search never had. Every text field accepts a mini-DSL: whitespace = AND, | = OR, "..." = exact phrase, -token = NOT.
POST /api/linkedin/search/sql/companies
{
"description": "\"lead generation\"|\"lead gen\" agency|agencies -recruiting",
"country_hq": ["US"],
"employee_count_min": 11,
"employee_count_max": 200,
"has_website": true,
"min_description_length": 200,
"count": 1000
}
That's a real query: US-headquartered companies, 11–200 employees, with a website and a non-stub description that mentions "lead generation" or "lead gen" alongside "agency", excluding recruiting firms. It returns in about a second, and each hit comes back as a complete record — name, URN, website, all office locations, full description, headcount, founding year, industry, specialities, hashtags, similar organizations, even a Crunchbase link where known.
4. Country filters that actually work. A classic bug in text-based location filtering: searching "US" as a substring also matches "RUSsia" and "AUStin, TX" in ambiguous ways. The endpoint has dedicated country_hq and country_any parameters that do token-aware ISO2 matching — country_hq: ["US", "GB"] means what you think it means.
5. It's 15× cheaper per record than live search. Live people search costs 150 credits per 100 results (1.5 credits/record) — because it consumes the live network's real capacity. The SQL endpoint is 0.1 credits per record. A 10,000-company universe costs 1,000 credits. That changes what you can afford to do: you can over-fetch broadly and filter downstream, instead of agonizing over every search.
We put live company search to the test
"Live search is worse" is easy to claim, so we measured it. Same intent, both tools: US lead-generation agencies, 11–200 employees. The live company search got keywords: "lead generation agency", location: "United States", employee_count: ["11-50", "51-200"]. Then we took the ten companies it returned and batch-looked their URNs up in the 70M database to see the actual fields. The scorecard:
Ten live-search results for "US lead-generation agencies, 11–200 employees", checked field by field against the database.
The live filter trusts the self-declared size range, so a one-person shop that ticks "11–50 employees" on its page sails right through. Another result listed a Pakistani phone number and linkedin.com as its website. And not one established mid-size firm made the list — the 197-person and 111-person agencies our database query surfaced never appear, because the live ranking is essentially name matching: it rewards companies with "lead generation agency" literally in their page name, not companies that do lead generation.
You can't see any of this from the live results themselves
Live search returns name, logo, and industry — no headcount, no description, no website. Every violation above is invisible until you pay to enrich each record individually.
Live company search is a find this specific company by name tool. As a build me a market segment tool it fails on relevance, fails on filters, and hides both failures behind thin result cards. The database query filters on the actual employee_count integer, actual locations, and full description text — the fields the live filter only pretends to check.
The economics reshape the funnel
This is the part that matters more than any single feature. Outbound data pipelines have a cost gradient: company discovery is cheap, people search is expensive, enrichment (emails, mobile numbers) is the most expensive per record. The mistake most teams make is spending expensive credits on unqualified accounts — running people-search or email lookup across a raw, unscored list.
A cheap, wide top-of-funnel inverts that. Here's the shape of a pipeline we run in production for Anysite's own outbound:
The pipeline Anysite runs for its own outbound. Discovery is wide because it is cheap; the expensive live-network stages only ever touch the qualified slice.
The SQL endpoint is what makes step one wide enough to matter: because discovery costs a tenth of a credit per company, you can pull a generous superset and let a $15 LLM pass do the precision work. In our run, LLM scoring over the raw pull found ~2,000 hot-fit accounts that keyword filters alone would have ranked identically to noise — and the expensive live-network stages only ever touched the qualified slice.
Compare that to the traditional order of operations — carefully hand-tuning a narrow search because every result costs real money — and you see the shift: the query doesn't have to be perfect anymore, because filtering intelligence moved downstream where it's better and cheaper.
A note on that: in the example query above, one of the ten sample results was a Michigan real-estate brokerage whose description mentions their "lead generation coordinator." Cross-field keyword search will produce these. You have two levers: tighten the query (move terms from keywords into specialities, add industry_name), or — usually better — keep the query broad and let the scoring pass eat the false positives. At 0.1 credits per company, a 20% false-positive rate costs you 2 cents per hundred records. A too-narrow query costs you the deals you never saw.
Four pipeline recipes
1. ICP discovery → score → outreach
The pipeline above. Use keywords/description/specialities DSL queries per ICP segment, union the results, dedupe by URN, anti-join against your CRM, then score with an LLM. The full company description in every result is exactly the context an LLM scoring prompt needs — no extra enrichment call to get it.
2. Lookalike expansion from your best customers
Every result includes similar_organizations — the network's own related-companies graph. And the endpoint accepts batch URN lookup:
{
"urn": ["fsd_company:35493842", "fsd_company:3834967", "fsd_company:11740840"],
"count": 100
}
So the loop is: take your 50 best customers → look them up by URN or alias → collect their similar_organizations → batch-fetch those (one call per ~1,000 URNs) → filter by your headcount/geo/description criteria → feed into scoring. Fair warning: the similarity graph is associative, not strictly ICP-shaped — one hop from a lead-gen agency can surface a tax-software vendor that shares an office park. The graph proposes; your filters and scoring dispose. Two hops out from a seed list of 50, you're typically looking at thousands of candidates that no keyword query would have found — companies that describe themselves in words you didn't think to search for.
3. CRM hygiene and re-qualification
Your CRM has 8,000 accounts loaded over two years. Which still exist? Which grew into your ICP? Which shrank out of it? Batch-URN lookup returns current employee_count, is_active, and locations for all of them in a handful of calls. Diff against what you stored:
- headcount doubled since last touch → re-qualify, this is a warm signal
is_active: false→ archive, stop wasting sequence slots- new office in your service region → territory trigger
Run it as a monthly job. Total cost for 8,000 accounts: 800 credits.
4. Segment monitoring for new entrants
last_modified_after takes a timestamp, and results are ordered by last_modified_at descending. Store your query set, run it weekly with last_modified_after set to your last run, and you get a feed of companies in your segment whose pages just changed — including brand-new pages. Combine with founded_on_min for a standing "new startups in my category" monitor that costs a few credits a week.
Practical notes
- Max 1,000 results per call. For bigger universes, slice the query — by
country_hq,employee_countbands,founded_onranges, or one ICP keyword group per call — and union client-side by URN. Ten sliced calls = 10,000 companies. - Use
country_hq/country_any, not text inlocations, for country filtering (see above). min_description_length: 200is the cheapest quality filter you have. Stub pages with empty descriptions are disproportionately dead companies, and they're useless for LLM scoring anyway.hashtagsis exact-match per element, not substring — good for precise community targeting ("revops", "salesdevelopment").- Results order is
last_modified_atdesc, which conveniently biases toward companies actively maintaining their page — itself a weak liveness signal. - Same endpoint is available in the Anysite MCP server, so an AI agent can compose these queries conversationally — "find me US RevOps consultancies under 50 people founded since 2020" becomes one tool call.
The takeaway
Account-list building has always been treated as a research task: a human (or an expensive tool seat) slowly assembling rows. A 70M-company database behind a query API makes it an engineering primitive instead — something a pipeline calls, the way it calls any other function.
The compounding effect comes from where that primitive sits: at the top of the funnel, where volume is highest and per-record budget is lowest. Make that step 15× cheaper and rate-limit-free, and everything downstream gets better — your scoring sees more candidates, your expensive live-search calls only run against qualified accounts, and your list refreshes weekly instead of whenever someone finds the time.
Full parameter reference: /linkedin/search/sql/companies.