PostHog Handbook Library / Growth

1,057 words. Estimated reading time: 5 min.

ICP fit score

We score every work-email signup on how well it matches who we build for: AI-pilled software teams at any scale, backed by leading investors or real revenue. This is the ICP fit score. "Fit" because it answers exactly one question: does this company match our definition?

The fit score is definitional, not a revenue prediction. A four-person seed-stage AI startup paying us $50/month can score high, and a large non-software enterprise paying us a lot can score low. A separate expected-revenue score (planned) will answer "what is this signup likely to be worth?" The two are consumed together as a fit × revenue quadrant and will disagree on purpose for some accounts.

Where it lands

The score is stamped on the organization in our internal PostHog project as three properties, written at signup and refreshed whenever the org is re-enriched:

| Property | Values | |---|---| | icp_fit_status | scored / disqualified / insufficient_data / not_found. Check this first: a score is only current when the status is scored or disqualified | | icp_fit_score | 0–100. Never read a missing score as 0 | | icp_fit_version | The formula version the score was computed under (e.g. v0.5) |

An organization with no icp_fit_status at all was never evaluated. Personal-email signups are not enriched, so they never get one; that is different from not_found.

Not to be confused with the legacy icp_score property: that is the old Clay-era formula on a different scale, still written in parallel until its existing consumers (cohorts, flags, scanners) migrate to icp_fit_score. Thresholds do not translate between the two; roughly, legacy > 8 corresponds to fit > 40.

How it works

Each signup's company is looked up in Harmonic by the domain of the work email, and the profile is scored in three steps. This page describes the intent and shape of v0.5. The exact rules, thresholds, and Harmonic fields live in fit_score.py, and every score carries the version that produced it.

1. Hard disqualifiers: score 0, with a reason code

2. Insufficient data: no numeric score

A profile that matched but has no headcount, no funding, no tags, and no web traffic gets insufficient_data instead of a number. These are mostly brand-new companies. We retry them automatically over the following months rather than treating "no data yet" as "not ICP".

3. Weighted components, summing to 100

| Component | Points | What it reads | |---|---|---| | Traction | 35 | Monthly web traffic level (up to 15) plus 90-day traffic growth (up to 20). Growth only counts above a minimum traffic base, because small-base percentages are noise | | Capital | 30 | Total funding tier (up to 20; a raise Harmonic knows exists but not the amount gets the base tier), plus 10 for a quality investor: a fund on our curated list, any YC batch, or AI Grant. Capped at 30 | | AI-pilled | 15 | Any of: an AI tag, AI language in the company description, or a .ai signup domain | | Headcount growth | 10 | 180-day headcount change, by percentage or by net hires | | Software relevance | 10 | Engineering headcount present (10), else software-product tags or software language in the description (7) |

Metadata flags (don't affect the score)

| Flag | Meaning | |---|---| | low_confidence | Scored from at most one of the four core signals (headcount, traffic, funding, tags). Read this as "unknown", not "bad" | | agency_flag | Consultancy or agency. Qualifies on its merits, but downstream teams may route differently | | nonprofit_flag | Non-profit | | quality_investor | Backed by a fund on the curated list, YC, or AI Grant |

Deliberate design choices

How we validated it

We validated the score as a definition, not a predictor: companies that are obviously who we build for should score high, and obvious non-fits should score low.

Known limitations

Canonical URL: https://posthog.com/handbook/growth/revops/icp-fit-score

GitHub source: contents/handbook/growth/revops/icp-fit-score.md

Content hash: 30c75e406f6088c7