← The guide library

Start here

The ICP map

A company list to a segmented, tiered map. Four passes, no-credit sources first.

By Mike Hurley, founder of WarmUp. This runs our own pipeline. It is not a demo build.
Version 1.5. Last updated . About 8 minutes.

Board for the ICP map: the four ICP facts and three sources, the rule, four passes with the tools used at each, and the verdict.

Left to right, following the numbers: the four ICP facts and the three sources, the rule, passes 1 to 4 (one column each), the verdict. The box at the foot of each stage names the tools we used there. The card at the top of pass 3 lists the sources that cost no provider credits. Open the board to see it full size.

Why it helps

Every other workflow reads the map. The hiring feed asks it whether a company is one we want. The social play reads its flags. The daily list reads its tiers. The map is one table of companies, keyed on domain, holding every company we have ever looked at with a status, a segment, a tier and a reason: assigned to a segment, undecided with the missing fact named, or disqualified with the word that removed it. Nothing is deleted. A status changes when a fact does.

It stays cheap because of one rule applied four times: a company leaves at the cheapest check that can remove it, and paid enrichment runs only on what the sources with no provider credits could not answer. Every exit carries a reason, and every reason is a candidate filter for the source query, so the next pull is cleaner than the last.

Who counts as ICP

Four facts, and each one needs a known answer. A fact known to fail decides, even when another fact is unknown. An unknown fact reads "needs enrichment" and waits for a source. It is never counted as ICP.

  1. Headquartered in the US.
  2. 25 to 200 employees.
  3. Privately owned. Public companies, nonprofits, schools, government bodies and investor firms are out.
  4. Sells to businesses. A company that sells to both businesses and consumers counts.

The tools at each stage

These are the ones we used. The shape holds with others in their place.

StageTools we used
The sourcesClay Search, SaasyDB, DiscoLike
The ruleFreckle and Clay run it. Our own Postgres database (Supabase) holds it.
Pass 1, exclusionsA code step in Freckle. No provider is called.
Pass 2, org structureApollo basic people search, as a Freckle step. Prospeo as the second source.
Pass 3, the real bandNo provider credits: AI Ark previews, Clay Search, the LinkedIn company type, DiscoLike records, HubSpot enrichment. Paid: Apollo organization enrichment.
Pass 4, the qualifierA research agent in Freckle. For agencies, a site-reading script on our own OpenAI key.
The verdictSupabase holds the map. HubSpot shows it to the rep.

Why we built it this way

  • The ICP is four facts we can actually fill. In late September we cut it to two, country and headcount, because ownership and business model were blank on most rows. Two days later ownership was filled on 98% of companies and business model on 78%, and the other two facts went back in. A rule can only use a fact that is known on nearly every row.
  • The source query is written literally. When we gave a natural-language builder the same words, it returned nearly three times as many companies as the literal query.
  • Every source lands in one table keyed on domain. With a rule for which source wins, a re-import updates a row instead of adding a twin.
  • Sources with no provider credits go first. Country and exact headcount come from a company preview by domain. Size band, revenue band and industry come from Clay Search, which draws a result quota and not credits. Private or public comes from the company type on its LinkedIn page. Sells-to-businesses comes from labels on records we had already bought. Paid enrichment runs only on a fact those leave unknown.
  • A missing sales role is recorded as a risk and never removes a company. We had that rule the other way round for two weeks and reversed it when it removed agencies we wanted.
  • "Sells to businesses" needs evidence. A label from a source we trust, or a quoted sentence from the company's own site saying businesses pay. No headline or industry name counts.
  • A person reads fifty after every run. A random fifty per segment, beside the agent's fields. A wrong call becomes a rule, a prompt fix or a source filter, never a hand edit to one row.

How to build it

  1. Write the ICP as a short list of facts, and the source query literally. Use only facts your sources fill on nearly every row. Then read the whole result, not the first page: the first page is ranked and looks nothing like the rest.
  2. Land every source in one table keyed on the company's main domain. Add a column for the source and a rule for which source wins on a duplicate. Never keep one table per cut. Cut with views.
  3. Pass 1, no provider credits. Dedupe, probe for dead and redirected domains, run a word list over name and description, and apply the segment's industry exclusions. Write a reason word on every exit.
  4. Pass 2, no provider credits. A people search restricted to the domain for sales, marketing-leader and owner titles. Record what you find. Disqualify nobody on it.
  5. Pass 3, fill the facts. Country and exact headcount from a company preview by domain (ask ten at a time, then retry each miss alone). Size band and revenue band from Clay Search. Ownership from the LinkedIn company type. Pay for one company enrichment only where a fact is still unknown. Revenue is recorded and never filtered.
  6. Pass 4, the paid read. One research call over the home, about and product pages only, filling a fixed set of fields: sells to businesses (with a quoted sentence), what they sell in one sentence, segment. Unknown means undecided, never a pass.
  7. Run ten rows and read the spend forecast. Then a hundred. Then the rest. Queued work cannot be recalled, so admit only what you are willing to wait out.
  8. Feed back what you learned. The top exit reasons become filters on the source query, the wrong calls from the fifty become prompt or rule fixes, and the counts go into the ledger.

Copy this

The ICP rule, the sources, the qualifier's answer and the exit reasons

# the ICP rule: four facts, decided in this order
disqualified:      the map already says so, with its reason
not_icp:           any fact KNOWN to fail
                   (HQ not US | headcount outside 25-200 | public, nonprofit, school, government or investor firm | sells only to consumers or government)
needs_enrichment:  country, headcount, ownership or business model unknown, and nothing known fails
icp:               HQ US, 25 to 200 employees, privately owned, sells B2B   # B2B and B2C together counts as B2B

# where each fact comes from: sources with no provider credits first, paid last
hq_country:   company preview by domain -> the CRM's own enrichment -> Clay Search
headcount:    company preview by domain (exact) -> Clay Search (a band, stored as its midpoint)
ownership:    the company type on its LinkedIn page
sells_b2b:    labels on records already bought -> the site read in pass 4
rules:        accept a preview only when the returned domain equals yours; an empty answer is not a no

# the qualifier's answer, one fixed shape (unknown is allowed; a guess is not)
sells_b2b:       yes | no | unknown     # yes only when businesses are the paying customer for the main offer
evidence_url:    the page on the company's own site that shows who pays
evidence_text:   the sentence on that page that shows it (quoted, not paraphrased)
what_they_sell:  one plain sentence naming the offer and who buys it
segment:         saas | manufacturing | agencies | professional_services | startup | unknown

# the exit reasons we write, one word per row, so a filter can read them back
duplicate  dead_domain  excluded_word  excluded_industry  headcount_out_of_band  public_company  too_young  not_b2b
undecided reasons name the missing fact: unknown_headcount, unknown_ownership, unknown_b2b, segment_unknown

The same guide as plain Markdown, for an agent or a notes app: /library/icp-map.md

Words we use

The map:
One table of companies, keyed on domain, with a status, a segment, a tier and a reason for each. Nothing is deleted from it. A status changes when a fact does.
Tier:
The rank a company holds on the map because of its fit. A signal moves a company up inside its tier and never changes the tier.
Unknown:
The answer a check gives when it cannot tell. An unknown is never counted as a pass. It waits for a person or for a missing fact.
No provider credits:
A source that answers without drawing down a data provider's paid credits. It is not the same as free: it can still use a search quota or an allowance.
Ledger:
The place the receipts are read. One row per run, read by a person every morning.

The full build guide

The same guide with the board at full size, kept on our Notion site with the rest of the library.

Open the build guide →

Want your list mapped before you buy data?

We build it, run it with you, and hand it over, with an engineer who can keep it running. Or hire that engineer directly.

30 minutes to scope it. Engagements are contracted through WarmUp, Inc.