Datacircle

Why SQL is superior when building your list

“A lot of people ask us what we actually sell if we don't make any markup. The answer is not official, but I can tell you here: it's a live database.”

Wayne, Datacircle's founder

Why a database? Because of SQL. A lead-gen operator asked our founder what they'd be missing: they already filter leads in their tools and export them through their APIs, and it works. The answer: “there is stuff very annoying to do without SQL, like the fast growing companies by hires.”

Here are five of them, each a list a seller wants and each one SQL query. We ran every one on the October 2026 release of the 50M+ U.S. dataset, 43.6M people and 6.7M companies, on a laptop: each took under a second.

What an API can ask, and what it can't

A B2B data API's search takes filters on one record: a person's title and location, their company's size and industry. It returns pages of the records that match, and each page or record costs a call or a credit. That's enough for “VPs of sales at construction companies of 51 to 200 people.”

The questions that make a list good are about other records. How many people did this company hire? Is that a lot for its size? Who isn't there? Who is already in my CRM? No filter on one record answers them:

What a list-building question needs, and the SQL that does it
The question needsIn SQLThrough an API
A count of other records (hires at the company)group bynot a filter
Two counts compared (hires per head)hires >= 0.1 * peoplenot a filter
Someone who isn't there (no head of sales)not existsnot a filter
Your own list (your accounts, your CRM)join read_csv(...)one call per account
The whole list at onceone querypage by page

Some APIs give a growth flag. “I just don't trust it,” says our founder: you don't see how it's made, and you can't make it hires per head over three quarters. The only other way is to pull every record and count them yourself, and a place to keep every record and count it is a database.

1. The companies growing fastest, by hires

The question from one of our demos: take every sales leader at a U.S. construction company and put first the companies that hired the most people in the last 12 months, so a rep calls from the top. One query counts the people whose current job started since October 2025 at every company (754,149 of the file's 43,598,308), then hangs that count on each sales leader (head of sales, sales director, director of sales, sales manager, VP of sales, CRO or chief sales officer):

with hires as (
  select CURRENT_JOB_COMPANY_LINKEDIN_ID as company_id, count(*) as hires
  from people
  where CURRENT_JOB_START_DATE >= date '2025-10-01'
  group by company_id
)
select people.FULL_NAME, people.CURRENT_JOB_TITLE, people.EMAIL, companies.NAME as COMPANY,
       coalesce(hires.hires, 0) as HIRES_LAST_12_MONTHS
from people
join companies on companies.LINKEDIN_ID = people.CURRENT_JOB_COMPANY_LINKEDIN_ID
left join hires on hires.company_id = companies.LINKEDIN_ID
where companies.LINKEDIN_INDUSTRY = 'Construction'
  and regexp_matches(people.CURRENT_JOB_TITLE, '\b(head of sales|sales manager|sales director|director of sales|vp,? (of )?sales|vice president,? (of )?sales|chief revenue officer|chief sales officer)\b', 'i')
order by HIRES_LAST_12_MONTHS desc

12,402 sales leaders at 7,035 construction companies, 5,032 with an email, sorted, in 0.43 s. Through an API you'd first fetch the 1,258,553 people at U.S. construction companies to count their start dates.

It isn't done, though. The top of that list is the biggest builders: the first has 7,536 people on file and 287 hires, which at that size says little about growth. What you want is hires per head, kept up over time, at a company the file knows well enough to judge:

with staff as (
  select CURRENT_JOB_COMPANY_LINKEDIN_ID as company_id,
         count(*) as people,
         count(*) filter (where CURRENT_JOB_START_DATE >= date '2025-10-01') as hires,
         count(distinct date_trunc('quarter', CURRENT_JOB_START_DATE)) filter (where CURRENT_JOB_START_DATE >= date '2025-10-01') as hiring_quarters
  from people
  group by company_id
),
growing as (
  select staff.*, companies.NAME
  from staff join companies on companies.LINKEDIN_ID = staff.company_id
  where companies.LINKEDIN_INDUSTRY = 'Construction'
    and staff.people >= 20                -- enough of the company on file to judge
    and staff.hires >= 0.1 * staff.people -- hires: 10% or more of its people
    and staff.hiring_quarters >= 3        -- and in 3 of the last 4 quarters, not one wave
)
select people.FULL_NAME, people.CURRENT_JOB_TITLE, people.EMAIL, growing.NAME as COMPANY,
       growing.people as PEOPLE, growing.hires as HIRES_LAST_12_MONTHS
from people
join growing on growing.company_id = people.CURRENT_JOB_COMPANY_LINKEDIN_ID
where regexp_matches(people.CURRENT_JOB_TITLE, '\b(head of sales|sales manager|sales director|director of sales|vp,? (of )?sales|vice president,? (of )?sales|chief revenue officer|chief sales officer)\b', 'i')
order by growing.hires / growing.people desc
U.S. construction companies by hires in the last 12 months, 50M+ US dataset, October 2026
StepCount
Construction companies with 20+ people on file8,705
Their hires in 12 months: 10% or more of those people252
And hiring in 3 of the last 4 quarters80
Their sales leaders: the list89
With an email48

Three counts per company, compared with each other, then joined back to the people: 0.68 s. Change 'Construction' to your industry, 0.1 to your idea of fast, and the title pattern to the people you sell to.

2. Teams hiring SDRs with no one leading sales

A company that hires sales development reps and has no sales leader needs one, or the training, playbook and tools one would bring. The list is every company of 11 to 500 people that hired two or more SDRs or BDRs since October 2025, minus those with a sales leader:

with sdr_hires as (
  select CURRENT_JOB_COMPANY_LINKEDIN_ID as company_id, count(*) as sdrs
  from people
  where CURRENT_JOB_START_DATE >= date '2025-10-01'
    and regexp_matches(CURRENT_JOB_TITLE, '\b(sales development|business development representative|sdr|bdr)\b', 'i')
  group by company_id
  having count(*) >= 2
)
select companies.NAME, companies.URL, companies.EMPLOYEE_COUNT_RANGE, companies.LINKEDIN_INDUSTRY, sdr_hires.sdrs as SDRS_HIRED
from sdr_hires
join companies on companies.LINKEDIN_ID = sdr_hires.company_id
where companies.EMPLOYEE_COUNT_RANGE in ('11-50', '51-200', '201-500')
  and not exists (
    select 1 from people leader
    where leader.CURRENT_JOB_COMPANY_LINKEDIN_ID = sdr_hires.company_id
      and regexp_matches(leader.CURRENT_JOB_TITLE, '\b(head of sales|sales manager|sales director|director of sales|vp,? (of )?sales|vice president,? (of )?sales|chief revenue officer|chief sales officer)\b', 'i')
  )
order by SDRS_HIRED desc

227 companies of 11 to 500 people hired two or more SDRs; 113 of them have nobody on file with a sales leader's title. not exists is the part no API has: you can filter on who works somewhere, never on who doesn't. Nobody on file isn't proof that nobody is there, so check the top of the list before you call, or ask for a minimum of people on file as the first query does.

3. A new sales leader at a company that's hiring

A sales leader in their first months chooses the team's tools, and one who joined a company that's hiring has budget. Two conditions on two levels: the person started since October 2025, and their company's hires are 10% or more of its people on file:

with staff as (
  select CURRENT_JOB_COMPANY_LINKEDIN_ID as company_id,
         count(*) as people,
         count(*) filter (where CURRENT_JOB_START_DATE >= date '2025-10-01') as hires
  from people
  group by company_id
)
select people.FULL_NAME, people.CURRENT_JOB_TITLE, people.CURRENT_JOB_START_DATE, people.EMAIL, people.MOBILE_PHONE,
       companies.NAME as COMPANY, staff.people as PEOPLE, staff.hires as HIRES_LAST_12_MONTHS
from people
join staff on staff.company_id = people.CURRENT_JOB_COMPANY_LINKEDIN_ID
join companies on companies.LINKEDIN_ID = staff.company_id
where staff.people >= 20
  and staff.hires >= 0.1 * staff.people
  and people.CURRENT_JOB_START_DATE >= date '2025-10-01'
  and regexp_matches(people.CURRENT_JOB_TITLE, '\b(head of sales|sales manager|sales director|director of sales|vp,? (of )?sales|vice president,? (of )?sales|chief revenue officer|chief sales officer)\b', 'i')
order by people.CURRENT_JOB_START_DATE desc

1,172 sales leaders at 749 companies, 710 with an email and 784 with a mobile phone, newest first. An API that filters on a job's start date still can't filter on what the person's colleagues did.

4. What a company doesn't have

Security vendors sell to software companies that have engineers and nobody in security. Count both at each company, keep those with 20 or more engineers and no one whose title says security:

with teams as (
  select CURRENT_JOB_COMPANY_LINKEDIN_ID as company_id,
         count(*) filter (where regexp_matches(CURRENT_JOB_TITLE, '\b(software engineer|developer|devops|sre|site reliability)\b', 'i')) as engineers,
         count(*) filter (where regexp_matches(CURRENT_JOB_TITLE, '\b(security|ciso|infosec|soc analyst)\b', 'i')) as security
  from people
  group by company_id
)
select companies.NAME, companies.URL, companies.EMPLOYEE_COUNT_RANGE, teams.engineers as ENGINEERS
from teams
join companies on companies.LINKEDIN_ID = teams.company_id
where companies.LINKEDIN_INDUSTRY = 'Software Development'
  and companies.EMPLOYEE_COUNT_RANGE in ('11-50', '51-200', '201-500')
  and teams.engineers >= 20
  and teams.security = 0
order by ENGINEERS desc

Of 88 software companies of 11 to 500 people with 20 or more engineers on file, 55 have no one in security. The same shape finds the 200-person company with no marketer, the clinic group with no IT manager, the retailer with no e-commerce lead: whoever you'd replace or help.

5. Your own list, joined

The list that matters most is yours: your target accounts, in a CSV with a domain column. Join it to the dataset and ask for everyone who started a job at one of them since October 2025, newest first: the new people to meet at accounts you already work.

with accounts as (
  select distinct lower(trim(domain)) as domain from read_csv('my_accounts.csv')
),
by_domain as (
  select LINKEDIN_ID, NAME, regexp_replace(lower(URL), '^https?://(www\.)?|/.*$', '', 'g') as domain
  from companies
  where URL is not null
)
select people.FULL_NAME, people.CURRENT_JOB_TITLE, people.CURRENT_JOB_START_DATE, people.EMAIL, by_domain.NAME as COMPANY
from accounts
join by_domain using (domain)
join people on people.CURRENT_JOB_COMPANY_LINKEDIN_ID = by_domain.LINKEDIN_ID
where people.CURRENT_JOB_START_DATE >= date '2025-10-01'
order by people.CURRENT_JOB_START_DATE desc

Add and people.EMAIL not in (select email from read_csv('my_crm.csv')) and you get everyone at your accounts that your CRM doesn't have yet. Through an API it's one search per account, then a diff in a spreadsheet. We ran it on 500 company domains drawn at random from the file, as a stand-in for yours: 0.5 s.

A file is a snapshot

The dataset sees a hire once the person's profile shows it, and its profiles were read months before the release. So the last quarters are thin, and every count above is a floor:

People in the 50M+ US dataset (October 2026) whose current job started in each quarter
Job startedPeople
Oct to Dec 2025353,138
Jan to Mar 2026321,742
Apr to Jun 202671,781
Jul to Sep 20267,488

A hire from last month is barely in a file yet; it's on the person's LinkedIn profile. Is each request live, or cached? Live. Each request goes to the provider and gets the profile as it is today. The co-op files keep every past job with the month it started and ended, so there a query sees who left, too (every column).

Datacircle is a data co-op. Step 1: Query your favorite B2B data APIs through us. Same request, same price, no markup. Step 2: You're DONE. Every morning, you get the flat file of your data plus everyone else's.

Run them yourself

DuckDB runs every query above on the two Parquet files as they are, on a laptop, with nothing to set up (pip install duckdb):

import duckdb

duckdb.sql("create view people as select * from 'us_50m/person_us.parquet'")
duckdb.sql("create view companies as select * from 'us_50m/company_us.parquet'")

leads = duckdb.sql(open("query.sql").read())  # any query below
leads.write_csv("list.csv")

How do I get the flat file? Add $50 to your account: you get $50 of API PLUS the flat file. Or invite 3 people who sign up. The 50M+ U.S. dataset unlocks once 3 people you invited sign up. The free 10M+ U.S. dataset has the same columns, so every query runs on it once the two file names change, on companies of 11 to 500 people; this post downloads it and cuts a list from it.

You don't have to write the SQL. “Coding agents are very good at SQL,” as our founder put it: describe the list in a sentence to Claude Code, Cursor or Codex, point it at the files and at the dataset's fields, and read the query it writes before you trust the count. Questions: wayne@datacircle.dev.

Ran on Apple M4 Pro, 48 GB, DuckDB 1.5.6, the October 2026 files read in place. Counts only: no person or company on this page.

Questions

Why is SQL better than an API for building a lead list?

An API search filters one record at a time: a person's title and location, their company's size and industry. The questions that make a list good are about other records: how many people a company hired, how that compares to its size, who isn't there, who is already in your CRM. SQL counts, compares, finds what's missing and joins your own list, over the whole dataset in one query, with no pages and no credit per call. Each query in this post ran on 43,598,308 U.S. people in under a second on a laptop.

How do I find fast-growing companies by hires?

Count, for each company, the people whose current job started in the last 12 months, and divide by the people the dataset holds there. In the 50M+ U.S. dataset (October 2026), 8,705 construction companies have 20 or more people on file; 252 hired 10% or more of that in 12 months, and 80 did it in 3 of the last 4 quarters. Their 89 sales leaders are one query away.

What does Datacircle sell if there's no markup?

A lot of people ask us what we actually sell if we don't make any markup. The answer is not official, but I can tell you here: it's a live database.

Is each request live, or cached?

Live. Each request goes to the provider and gets the profile as it is today.

How do I get the flat file?

Add $50 to your account: you get $50 of API PLUS the flat file. Or invite 3 people who sign up.

Sign up at datacircle.dev with your work email: a $5 credit, that's 4,000 LinkedIn profiles at $1.25 per 1,000. Free: 10M+ U.S. B2B leads, as a flat file. Download it at datacircle.dev.

Sign up