How to Score Companies by Revenue Band
To score companies by revenue band, set five to seven bands that widen as revenue grows, give each band a fixed number of points on a 0 to 100 scale, and look up every company’s revenue against the band edges, with the lower edge inclusive. Unknown revenue stays blank. Below: the table, the formulas, tested code, and the edge cases that break most models.
Why the bands have to widen
Revenue is not spread evenly. A list of private companies usually holds far more companies under $10M than over $100M, and the gap between $1M and $2M (the company doubled) matters more than the gap between $50M and $51M (2 percent). Equal-width bands, like $0 to $20M, $20M to $40M and so on, put nearly the whole list in the first band and make the score useless.
The fix is bands that widen as revenue grows. Each band starts where the last one ended. No gaps, no overlaps.
The standard seven-band table
| Band | Revenue | Size score (bigger is better) | Fit score (ideal is $5M to $20M) |
|---|---|---|---|
| 1 | Under $1M | 10 | 0 |
| 2 | $1M to $5M | 25 | 50 |
| 3 | $5M to $10M | 40 | 100 |
| 4 | $10M to $20M | 55 | 100 |
| 5 | $20M to $50M | 70 | 50 |
| 6 | $50M to $100M | 85 | 20 |
| 7 | $100M and up | 100 | 0 |
Size score. Points climb with revenue. Use it when bigger simply means better: deal size, market power, ability to pay.
Fit score. Points peak at the band you sell to best and fall off on both sides. Use it for lead scoring and ideal customer profiles, where a $300M company is as wrong for a $97 offer as a $300K one is for a $50,000 retainer. Move the 100s to wherever your best customers actually sit.
A common mistake shows up in the band list itself: 0 to 1 million, 1 to 5 million, 10 to 20 million, 50 to 100 million. That list skips $5M to $10M and $20M to $50M, so a $7M company has no band at all. Check that every band’s lower edge equals the previous band’s upper edge.
How to build a revenue band score
- Pick five to seven bands that widen as revenue grows. Use edges like $1M, $5M, $10M, $20M, $50M and $100M so every band holds a real share of the list. Leave no gaps and no overlaps between bands.
- Decide what the score means. A size score gives bigger companies more points. A fit score gives the most points to your ideal band and fewer the further a company sits from it.
- Assign points to each band. Spread the points from 0 to 100 so the score can be weighted against other factors on the same scale.
- Normalize the revenue text. Strip dollar signs and commas, turn dashes and the word “to” into one separator, read K, M and B suffixes, and take the midpoint of a reported range.
- Look up each company against the band edges. Use LOOKUP or XLOOKUP in a spreadsheet, or bisect_right in code, with the lower edge inclusive.
- Score unknown revenue as blank. Keep missing revenue out of the math instead of scoring it as zero, and flag the record.
The no-bands option: a log formula
If you would rather not pick bands at all, score revenue on a log scale. Every tenfold jump earns the same number of points:
score = 100 × (log10(revenue) - 5) ÷ 4, capped between 0 and 100
With a floor of $100K (log10 = 5) and a ceiling of $1B (log10 = 9), each decade is worth 25 points:
| Revenue | Score |
|---|---|
| $100K | 0 |
| $1M | 25 |
| $5M | 42.5 |
| $10M | 50 |
| $20M | 57.5 |
| $50M | 67.5 |
| $100M | 75 |
| $1B | 100 |
Change the floor and ceiling to fit your list. The formula always becomes 100 × (log10(revenue) - log10(floor)) ÷ (log10(ceiling) - log10(floor)).
Excel and Google Sheets formulas
All three formulas below were run in Microsoft Excel on October 12, 2026, with revenue in A2. LOOKUP and the log formula work the same in Google Sheets.
Band points with LOOKUP (the edges must be in ascending order; LOOKUP returns the points for the largest edge that is less than or equal to the revenue):
=LOOKUP(A2,{0,1000000,5000000,10000000,20000000,50000000,100000000},{10,25,40,55,70,85,100})
$5,000,000 returns 40 and $999,999 returns 10.
Band points with XLOOKUP from a table, lower edges in D2:D8 and points in E2:E8 (the -1 means exact match or the next smaller edge):
=XLOOKUP(A2,$D$2:$D$8,$E$2:$E$8,0,-1)
Log score:
=IF(A2<=0,0,MAX(0,MIN(100,100*(LOG10(A2)-5)/4)))
$20,000,000 returns 57.5.
Python: parse the range, then score it
Revenue rarely arrives as a clean number. Data vendors and forms hand you text like “$1M-$5M”, “1 to 5 million” or “100M+”. Normalize first, then look up.
import bisect
import re
# Lower edge of every band after the first. A value equal to an edge
# belongs to the band that starts there.
EDGES = [1_000_000, 5_000_000, 10_000_000, 20_000_000, 50_000_000, 100_000_000]
POINTS = [10, 25, 40, 55, 70, 85, 100]
UNITS = {"k": 1e3, "thousand": 1e3, "m": 1e6, "mm": 1e6, "million": 1e6,
"b": 1e9, "bn": 1e9, "billion": 1e9}
def band_points(revenue):
if revenue is None:
return None # unknown is not the same as small
return POINTS[bisect.bisect_right(EDGES, revenue)]
def parse_revenue(text):
"""'$1M-$5M', '1 to 5 million', '20 \u2013 30M', '100M+' -> dollars (range midpoint)."""
s = text.lower().replace(",", "").replace("$", "").replace("usd", "")
s = re.sub(r"\s*(?:-|\u2013|\u2014|to)\s*", "-", s)
parts = re.findall(r"(\d+(?:\.\d+)?)\s*(thousand|million|billion|mm|bn|k|m|b)?", s)
if not parts:
return None
unit = next((UNITS[u] for _, u in reversed(parts) if u), 1)
values = [float(n) * (UNITS[u] if u else unit) for n, u in parts[:2]]
if s.strip().endswith("+") or len(values) == 1:
return values[0]
return sum(values) / 2
What it handles, each case tested:
| Input | parse_revenue | band_points |
|---|---|---|
| $1M-$5M | 3,000,000 | 25 |
| 1 to 5 million | 3,000,000 | 25 |
| 20 to 30M | 25,000,000 | 70 |
| 50 - 100 million | 75,000,000 | 85 |
| $500K-$1M | 750,000 | 10 |
| 100M+ | 100,000,000 | 100 |
| unknown | None | None |
Long dashes pasted from Word or a PDF are treated the same as a hyphen. Two choices in that code are worth knowing about. A reported range scores at its midpoint, so “$1M-$5M” counts as $3M. If your data source reports the band a company falls in rather than an estimate, score the band directly instead. And a unit written only once (“1-5 million”) applies to both numbers, which is how people write ranges.
JavaScript version
const EDGES = [1e6, 5e6, 10e6, 20e6, 50e6, 100e6];
const POINTS = [10, 25, 40, 55, 70, 85, 100];
export function bandPoints(revenue) {
if (revenue == null) return null; // unknown is not the same as small
let i = 0;
while (i < EDGES.length && revenue >= EDGES[i]) i++;
return POINTS[i];
}
Mistakes that quietly break the score
Gaps between bands. “10 to 20 million” followed by “50 to 100 million” leaves $20M to $50M with no score. Every lower edge must equal the previous upper edge.
Overlapping edges. “1 to 5 million” and “5 to 10 million” both claim $5M. State the rule (lower edge inclusive) and let the code enforce it.
Scoring unknown as zero. It turns missing data into a claim that the company is tiny. Keep it blank.
Letting revenue carry the whole score. Revenue says how big a company is, not how healthy it is. A $20M company with one client holding 60 percent of revenue is a different risk from a $20M company with 400 clients. Weight revenue next to the factors that change the answer, for example 30 percent revenue band, 30 percent concentration, 20 percent growth, 20 percent margin, all on the same 0 to 100 scale.
Where revenue bands stop being enough
A band score ranks a list. It cannot tell you why a particular company at $8M is stuck or where it will break next. That needs the structure behind the number: who the revenue comes from, how much depends on the owner, and what one bad quarter would do. The Growth Readiness Diagnostic scores those factors for your own business, the owner dependency analysis shows how a structural score is built, and a Structural Read runs the full premortem on the business behind the revenue.