▶ Rules of thumb how the curve is built, and what it is allowed to say
rule 3
parented?rule 19
the rest = benchrules 3–4
at real unitsrule 9
rule 13
label stepped 600+→700+14 days · counts 5 · rule 24
50 units and up · rule 24
units ← positionrule 25
+130 points +50% fitlabels only · rules 15–16
0 · This card is the contract
Every change to the logic is written here in the same commit, and every rule here is checked against the code before the work is called done.
Not a summary written afterwards — the record. If a rule and the code disagree, the rule is the bug report.
It is rule 0 because breaking it is what made this project go round in circles: decisions were argued twice because nobody could see what had already been settled.
1 · The problem
Amazon never tells us how many units a product sells. It tells us two things:
A rank — "#8,432 in Patio, Lawn & Garden". Moves every day.
A badge — "500+ bought in past month". Only on some listings, and it only changes about once a week.
Collect both from hundreds of products in one category and you can draw a line between them: in Patio, rank 10,000 usually means about 450 units a month. Then any rank we read today becomes a units figure today.
Why we bother: the market tables today turn a badge into units by the floor-plus-a-third guess (rule 2). The line is being built to replace that guess in the revenue estimates. Until it is proven, its answer sits in a reference-only column beside the badge estimate and feeds nothing — flipping that switch is a user decision.
2 · What one data point is
One product, one day: its rank, and its badge. That pair is a point. The curve is the line drawn through all of them.
The badge is a range, not a number. It is a floor in steps — 50 below 100, 100 up to 1,000, 1,000 above. "1,000+" means somewhere between 1,000 and 1,999.
We turn it into a number by taking a third of the way up: "1,000+" becomes 1,333. A third, not a half, because listings cluster toward the bottom of their step.
3 · SETUP — choosing the list once per category
We ask Keepa for the units, not for a rank. It answers directly: "which products in Patio sell 800–899 a month?" One question per step, 100 answers.
That replaced guessing a rank window, which was circular — rank-to-units is the very thing we are trying to learn. Measured: guessing bought 4,583 products for 28 useful ones. Asking by units keeps about 60% of what it buys.
a. Ask for each of the 13 unit steps in turn — and keep digging in a step until it is filled or Keepa truly has no more (an empty page; a page of products we already know is not "no more"). One bite per step per run was a bug, found and fixed 2 Aug 2026: a step was declared finished at 6 products seen while Keepa held 300+.
b. Check every answer once: does it show a badge? is its main rank in our category? is it part of a variation family (rule 13)?
c. Keep 20 per step. That list is the roster.
d. Every other clean product the search returned goes on the bench. We already paid to check them; throwing that away is what made replacements expensive.
4 · Why 20 per step, and why a bench
Not for statistics — 10 well-spread points draw a line perfectly well.
It is for attrition. Products go under a variation parent, get discontinued, go out of stock, or lose their badge. A step holding exactly 10 fails the day one drops out.
The spares are read from day one, not found when one dies. This is the whole trick: a brand-new product has no rank history, and a point without a month of history is a weak point. Bringing in a cold replacement would swap a good point for a bad one.
So all 20 are read every day and all 20 stay warm. When one drops, its replacement already has its month of history and the curve never dips.
5 · DAILY — reading the list every day, 05:05 and 05:20
05:05 — the free half. Everything already in our own database: our tracked markets, our whole portfolio at its real units (rule 9), and any badge flips (rule 10). No tokens.
05:20 — the paid half. Read the roster and the bench from Keepa, about 1 token per product. No searching, no discovery, no guessing.
Then draw the line. The same products as yesterday, so the curve moves because the market moved — not because we happened to look at different products.
05:40 — discovery, one category at a time, where WE sell first (migrations 150–151): curves are built only for categories our own products sell in — a category none of our products sells in gets no curve at all (greyed in the category table, its roster no longer read). The rest are worked in order of our own last-30-days revenue there, biggest first, and the run finishes one category's 13 sizes before touching the next — never several at once. It shops by unit range (singles only, digging until each size fills or Keepa runs dry) and turns the finds into that category's roster + bench automatically — no hand steps. The whole free pass also repeats at 23:30 so late badge flips are caught (rule 10).
6 · REPLACING a product as it happens
A product whose read comes back unusable twice in a row is retired, and the reason is kept on its row — went under a parent, discontinued, badge gone, moved category. Any good reading resets the count to zero, and only reads that actually happened count: a night where the token budget ran dry strikes nobody.
This was broken until 2 Aug 2026, recorded on purpose: the count never reset, so "in a row" was really "ever" — with a third of the list failing on any normal day, the whole roster would have retired itself within a week while looking healthy.
Its replacement comes off the bench, free. Already checked, already in the right step, already carrying its month of history.
The spares restock themselves (2 Aug 2026): the daily read counts each size's roster and spares, and if a size is under 20 main products or under 5 spares it goes shopping for that one size with the tokens it has left, then absorbs the finds. Before this, nothing restocked the bench — every refill had been run by hand.
Open "The tracked bucket" above to see all of it: every product, its step, when it joined, and whether it made it into today's line.
7 · The pairing rule
A point pairs a rank with a units figure. Both sides must describe the same month, or we are comparing two different things.
The badge counts the last 30 days. A rank is today. So for building the line we use each product's average rank over the last 30 days.
That also explains something that looks wrong: two products at the same rank today can honestly show different badges — one ranked high all month and just slid, the other just climbed.
Reading uses the same 30-day average (changed 2 Aug 2026 — it used to feed today's rank). The market tables estimate the last 30 days' units and revenue, so the rank fed in must cover that same month — and measured on the line's own products, reading at the average missed by 16% against 22% at today's rank. Today's rank is used only for a product with under 7 days of history, and the cell's hover says which one it used.
8 · The maths
We fit units = a × rankb. Take the logarithm of both sides and it becomes a straight line, so it is an ordinary least-squares fit. No black box.
b is negative: a better (smaller) rank means more units.
The line is fitted from units to rank and then flipped (changed 2 Aug 2026). Rule 3 deliberately buys the sample by its units range; fitting the straight way round on a sample picked like that provably flattens the line — measured on Patio's 241 points: slope −0.88 straight, −1.09 flipped, answers almost 2× apart at rank #500. Buying by units stays — it is the efficient way to fill every step — only the maths direction had to match it. A line sloping the wrong way (more units at worse ranks) is refused outright.
Every product pulls equally (same change). The old certainty weights let the 600–999 steps hold 64% of the pull with only 37% of the points — the expensively-bought ends of the range were being ignored — and the weight floor made a vague "900+" badge count exactly as much as one of our own measured sales. One product, one vote; a measured point still tells exactly where it sits.
The fit uses the last 3 days, taking each product's newest point. Deliberately newest-only: the same product on three days is one product's worth of information, not three, and counting it three times would fake both the coverage and the line.
9 · Our own products go in at their real units
For our own products we do not have to read a badge — we know exactly what we sold. So before any line is drawn, every product we sell whose main rank is in that category is added at its real 30-day units.
Not just the ones on the Market list — the whole catalogue. We already hold a daily rank for everything we sell, so these cost nothing.
Our own variation families are grouped for the whole catalogue (fixed 2 Aug 2026): the hourly ratings fetch now stores every catalog product's variation parent, so children sharing one rank are summed onto ONE family point (kept out of the line — rule 13) instead of landing as several fake "standalone" dots. Found the hard way: our own Bonsai family sat as four separate points, each carrying one child's units against the whole family's shared rank.
They are the only points with no guesswork on the units side. They anchor everything else, and they are the honest check: if the line disagrees with our own sales, the line is wrong.
10 · Badge flips — the day a product's sales are known exactly
The day a label moves 900+ → 1,000+, that product's 30-day total just crossed 1,000. Not a range — a number.
Two things use this, and they are separate.
1 · A flip point is written down at ±2% (294–306 for a crossing of 300) and joins the line as an ordinary reading. About 1–3 a day across all categories. Checked twice a day (05:05 and 23:30 UTC, migration 149) — a flip only visible in the evening used to be lost by morning.
2 · Since 7 Sep 2026 the fit finds the steps itself out of the daily roster readings and counts them five times over for 14 days — many more, and much heavier. That is rule 24, and it is where the weight now lives.
The obvious objection, and the answer: a 30-day total crossing a line says nothing about any single day — the product may have sold 5 one day and 80 the next. That is exactly why a flip is paired with the same 30-day average rank as every other point. Both sides describe the month, never the day.
11 · We buy history, we never wait for it
Keepa sells both histories for any product — checked directly, not assumed: the whole rank history (2,460 readings on the product tested) and the whole badge history (210 readings).
So nothing here ever waits to ripen. A product we met today can have its 30-day average rank filled in today. 117 such points cost 117 tokens.
Honest result: that particular top-up moved the line barely at all (86.0 → 86.4%, and 62.0 → 63.5%). Written down so nobody re-runs the same hope.
The same two histories are what make rule 12 possible on day one.
12 · Each product's own offset — the real lever
We tested whether a product sits on the same side of the line day after day: 316 product-days · correlation 0.99 · 95% on the same side.
So the leftover scatter is not random. It is each product's own steady offset, because Amazon weights sales history into a rank and every product carries a different history.
The line says what a rank usually means. The offset says what it means for THIS product.
How it is measured: for every badge reading of the last 90 days, pair it with that product's 30-day average rank at that moment, and divide by what the line says for that rank. The middle of those ratios is the offset. Then answer = line(the rank) × offset. One token per product; 118 of 150 Patio products got one immediately.
If a product's ratio swings by more than half it has no steady personality, the offset is ignored, and the line answers alone.
Not yet proven to help. The only test available today compares against the same badges the line was drawn through, which it cannot win — it scored 22% either way. A proper test needs days held back, and is still owed.
13 · Variation families are NOT data points
Amazon gives a variation family one rank and prints it on every child. Two things follow, and the second one took us two goes to see.
1. One child alone is far too low. Each child's badge is its own units, not the family's. Measured on our own siblings — same day, same rank: 24 family-days, 20 showed different badges between siblings, the bigger averaging 3× the smaller. So one child's badge next to the family's rank under-counts by about 3×.
2. The complete family is wrong too — and this is the one we got wrong. Adding up every child fixes problem 1, and that is what we used to do. But it answers a different question: "what does this whole family sell at rank #2,000?" is not "what does one product sell at rank #2,000?", because the rank was earned by all of them together. So a family sits above the single-product line, and adding more families does not average out — it drags the line.
Measured 1 Aug 2026, held out three ways (line built on two thirds of the products, scored on the third it never saw, always scored on single products):
Patio, Lawn & Garden — with families 29.3% typical miss,
single products only 20.5%
Home & Kitchen — with families 43.2%, single products only
43.3%, but the bad case fell from 146% to 131%
And family points sat 6.5% (Patio) / 26.9% (Home & Kitchen) above a single-products line — the high side 65% and 57% of the time. That is a lean, not random scatter.
So: only single products build the line. Anything with a parent is dropped — we no longer buy its siblings, and the daily read no longer completes it. Every family point already collected is kept on record, shown under "read but not used" with the reason, and counted in nothing.
What it cost, plainly. About half of every category's points were families, and they were concentrated at the top — Patio lost 68 of its 80 points in the 1,000–1,999 step. Both curves went dark the day this landed and stay dark until the harvester refills each size with single products. A line that leans is worse than a gap you can see.
14 · Variation families — READING one off the line
Rule 13 is about not letting a family corrupt the line. This is the opposite direction: using a finished line on a product that has a parent. It is the "Units sold (curve)" column on the market tables.
In one line: child units = line(family rank) × this child's share of the siblings' summed badges.
The two steps spelled out:
1. line(the rank) → the whole family's units
2. × this child's share of the family's badges → this
child's units
Example. Family ranks #5,000 → the line says 1,000. The children's badges are A 600, B 300, C 100. A's share is 60% → 600 units.
If we do not hold every sibling's badge the cell shows a dash — never the family's size. Showing the family as if it were the child was a real bug here, and it over-reported every parented competitor.
A standalone product skips step 2: it is its family.
⚠️ Known gap, open since 1 Aug 2026. Since rule 13 the line is built from single products only, so step 1 no longer gives the family's units — it gives what one product at that rank sells, which is lower. Measured on the day it changed: about 7% low in Patio, 27% low in Home & Kitchen. So a parented product's figure is understated, and the cell says so. Building a proper family correction is the next job — until then, trust these figures less than a single product's.
15 · The minimum: every step, 130 points
A total on its own means nothing — 200 points all between 200 and 300 units say nothing about the line anywhere else.
The steps are Amazon's own badge steps, so a product showing "500+" sits in exactly one of them:
50–99 · 100–199 · 200–299 · 300–399 · 400–499 · 500–599 · 600–699 · 700–799 · 800–899 · 900–999 · 1,000–1,999 · 2,000–2,999 · 3,000–3,999
The rule: 10+ points in EVERY one of the 13 steps — not most of them — 130 in total, and a fit of 50% or better.
Enforced up to 4,000 only. 2,000–3,000 a month is the top of what we sell, and even that is rare. Points above 4,000 still feed the line; they are just never required.
This was wrong until 31 Jul 2026, recorded on purpose: it shipped as "3 of 4 wide bands, 60 points", which let a whole step sit empty. Patio passed that way with its 2,000–3,000 step short, and was reported as finished. It was not.
16 · If it misses: no line that day
Nothing is drawn. The points are kept and shown; only the line is withheld.
Why not a rough one? A bad line is invisible — it looks exactly like a good one and quietly poisons every number read off it. A missing day is visible and says why.
It breaks nothing: each day is drawn on its own, so a skipped day spoils no average. The market table falls back to the newest day that did pass (up to 14 days back), or shows a dash.
17 · "Fit 86%" is not a margin of error
Fit is how much of the scatter the line explains. It is a statistician's number and it does not tell you how wrong an answer will be.
Typical error is the one to read. For every point behind the line we compare the line's answer to the number Amazon printed on that listing — "1,000+" becomes 1,333, rule 2 — and take the middle of those gaps. Since 2 Aug 2026 the comparison is made at the same 30-day average rank the line was built on — scoring it at a different rank than it was drawn on had inflated the figure by about a third.
"±22%" means half the products land within 22%. The tile also says what 1 in 5 is off by.
Both are shown side by side, so the plain number leads and the technical one is still there to check.
18 · When the line beats the badge — and when it does not
The badge measures that product. The line is an average over other products at that position. So the badge wins whenever its step is tight:
100–999 a month → step 100 wide → badge off by 10–33%. Badge wins.
1,000+ a month → step jumps to 1,000 wide → "1,000+" could be
1,001 or 1,999, off by up to 67%. Here the line wins — that is the
whole reason it exists.
No badge (under 50) → only the line can answer.
Judged only by real referees — products whose true monthly units we know. Two pools, two columns on this tab: our own data (our products over 100 a month, families counted once) and badge flips (truth pinned within ~2% that day). Scored against its own day's line, then pooled: our products get one vote each (their median), flips one vote per flip. The winner is the smaller median miss — how wrong, not how often. Green = line wins, red = badge+⅓, yellow = no referee. Click any cell for the breakdown.
Where it stands, last 30 days: flips — line off by 13.7%, badge 11.1%. Our families — line 10.8%, badge 7.6%. The badge is still ahead, and rule 24 explains why: everything we can check sells 43–769 a month, exactly where the badge is sharpest. Above 1,000 the line has never been tested, because we do not sell there. That test is owed.
Rebuilt twice, on purpose. The first scoreboard scored the line against the badge's own +⅓ figures — the badge marking its own homework — and was thrown out (2 Aug 2026). Rebuilding history from Keepa's stored badges was tried and dropped too: Keepa samples badges sparsely, so 8,913 reconstructed points all fit at 42–49% and dragged a good 85% day down to 43%. The scoreboard builds forward from live days only.
19 · Always ask "is it parented?" first
Before a product becomes a point, and before a product is read off a line: does it sit under a variation parent? If yes, the rank on it belongs to the whole family and the badge on it belongs to one child. Those two can never be paired.
Building → rule 13. Reading → rule 14.
This was broken and is recorded so it is not repeated. The harvester always obeyed it — 0 bad points. The free leg reading our own tracked markets did not: it only merged a family when TWO tracked children shared a rank, so a child whose siblings we happen not to track slipped through alone. 85 points in Patio, 65 in Home & Kitchen.
Removing them raised the fit (86 → 88%, 65 → 68%) and dropped both curves below the minimum — those bad points had been propping the steps up.
20 · Where the points come from — Keepa only
Keepa is the only source. Decision 1 Aug 2026; the scraper is out of this pipeline entirely until we say otherwise.
Free (no tokens): our tracked markets, our whole portfolio at its real units, and badge flips — all already in our own database.
Paid (tokens): the roster read, discovery when a step runs short, history top-ups, and the per-product offsets. (Families are no longer bought at all — rule 13.)
Why not the scraper: Amazon guards product pages hard, one bad proxy window stops a whole run, and Keepa answers "who sells 800–899 a month in this category?" directly — which no amount of scraping does.
The tile's free/paid split reads the two figures the fit actually records: free = points from products we already track, paid = points bought from Keepa. It used to read two leftover scraper-era figures that are always zero, so a curve with 357 bought points printed "nothing harvested on top yet". Fixed 1 Aug 2026.
20b · What this tab shows, and in what order
Three cards, one question each (re-laid-out 2 Aug 2026): first the category, the day walker and the one-sentence answer; then "The curve & its evidence" — the colour-coded coverage table (green = a size has its 10, red = short; click a size to filter the product list) above a full-width graph of every point against the fitted line, red diamonds being our own catalog's real-unit points, each dot clickable for its story, with the calculator and the formula sitting next to the graph they describe; last, "Every product behind this line" — one flat, filterable table, no per-size sections. The formula and R² stay small print: they are for checking the number, not for reading it.
Words, never symbols. Every column in the list answers in plain words — Yes / No — family not complete / read daily / spare — not ticks and dashes the reader has to decode. The full reason stays on the hover.
ONE list of products, never two. There used to be a second table repeating the same ASINs, titles and ranks — two near-identical tables on one screen, and the reader left to work out which was which. Gone.
The one list holds everything behind the line: the roster, the bench, and any product that produced a point without being on either (our own markets, our portfolio, badge flips). Its Role column names which.
Every count on this tab uses the fit's own window — the last 3 days, each product counted once at its newest reading (rule 8). A product missed today but read yesterday still counts, and its row shows a faded tick with "(carried)" next to the day it was actually read. The list used to load a single day, so the headline said 394 while the list underneath showed 320 — the same question answered two different ways. Any number shown here must be the number the fit used.
"In this curve?" is its own column because tracked is not the same as counted — a product can be read successfully and still be left out (a family we could not complete, a step already full).
Filter it by All / In this curve / Not in it / Standalone / Parented, and the row count is shown next to the chips — a filter without a count is half an answer.
The tab itself sits apart from "Our markets" and "Research markets": it is behind the scenes, not a third market to browse. The tab you were last on is remembered across a reload.
21 · What this does NOT do
Wherever Amazon shows a badge, the badge wins: the line never overwrites a badge's units, our market-share figures, or our real sales. The line's answer appears in the tinted "Units sold (curve)" column as a cross-check, so the two methods can be compared in the open.
One exception, added 3 Aug 2026 (rule 23): a variation child with no badge at all has nothing from Amazon to win — there the line sizes the family's leftover, and only there does it feed a units/revenue figure.
22 · Why a rank is not a number of units
A rank is a position, not a quantity. #1 in a huge category can be thousands of units a month; #1 in a tiny one can be a handful.
That is why a line is fitted per category, and why one category's line must never be used on another.
It is also why two products at the same rank can genuinely sell different amounts: Amazon weights sales history into a rank, and every product carries a different history. That gap is what rule 12's offset is for.
23 · Badge-less variation children — the family split
Since "no partial families" (3 Aug 2026) every sibling is tracked, so some tracked children show no badge. The split:
1. A child with a badge keeps its own badge+⅓ number — real information is never overwritten (rule 21).
2. The line is read once, at the family's shared 30-day-average rank → the whole family's units. Minus the badged children's badge+⅓ numbers = the leftover.
3. The leftover is split among the badge-less children in proportion to their Keepa rank-drop counts — the only per-child signal a badge-less child has.
4. When at least one sibling shows a badge, each badge-less child is capped at 49: the listing provably shows badges, so no badge means under Amazon's smallest step (50+).
5. If the badged siblings alone already reach the line's family total, the line has nothing to hand out — and the shared family rank gives a badge-less child no signal of its own — so it shows ≈ 25, the middle of the only range we know (0–49), never a zero we cannot back and never the automatic maximum (updated 4 Aug 2026; it used to always show the 49 ceiling).
6. No qualified line for the category → no split; the honest ≈ rank-drop fallback stands.
7. The "Units sold (curve)" comparison column splits families too (4 Aug 2026): the line is read once at the family's shared rank = the family total, and every child gets a slice of it — badges set the ratios, a badge-less child's slice is its under-50 figure from rules 4–5 — so the children always sum to the line's family total. Example: curve 800, badges "500+" + two badge-less → slices 533:49:49 → 676 · 62 · 62. That column (and "real sales") is hidden behind the ⚖ Compare-columns toggle — comparison only, feeds nothing.
24 · What the line is drawn from, and what each reading counts
Three kinds of reading go into every line. Each carries a WEIGHT — how many ordinary readings it is worth.
| Reading | How the sold figure is got | Kept for | Counts |
|---|---|---|---|
| The label | Amazon prints "600+ bought in past month". We read it as the floor plus a third of the step: 600+ → 633, 1K+ → 1,333. A guess. | 3 days | 1 |
| A label step | We read the same roster every day. The label goes 600+ → 700+ overnight, so it sold exactly 700. Only a step to the NEXT number counts — a jump over two boundaries does not say which was crossed. | 14 days | 5 |
| Our own products | The figure comes from our own sales, so it is exact and known every day. Ignored under 50 units. | never expires | 5 |
One reading per product in each of the last two — the newest. A product whose label stepped twice in the 14 days is in once, not twice.
Why 14 days. A reading does not go stale about its own day — it goes stale because the category moves. Measured over 56 days, Patio, Lawn & Garden fell steadily and six-week-old readings carried double the error of week-old ones. Our own products are exempt: we can re-read them for any day, exactly, for ever.
⚠ Be honest about what they buy. Adding both kinds moved the typical miss from 0.194 to 0.191 — almost nothing. The reason: the label guess was never far wrong, and the rounding they remove was not the problem. They do fix the deep end of the list, from 0.90× to 0.94×. The line's real improvement on 7 Sep 2026 came from the fit direction (rule 25), not from these.
⚠ A product we know exactly ALSO stays in as a label reading. That looks wrong and it was tried: taking ~90 products a category out of the label pool pushed Toys & Games under the coverage rule (rules 15–16) and its line went dark, for no measured gain. The two readings are different DAYS, not the same measurement twice, and the exact one already counts five times over.
Both kinds are drawn on the graph as their own dots — green for a label step, red for our own — so you can see what the line was actually drawn from.
25 · The shape: one straight line, fitted units ← position
units = a × positionb, with b negative so a better position means more units. One line per category per day.
It is fitted units against position — the plain way round. Until 7 Sep 2026 it was fitted the other way (position against units) and then turned round, on the argument that the products behind it were bought by sales range. That was costing us: reading by reading, the plain way is closer on 972 of 1,666 (58%), and it took the top of the list from 1.45× too high to 1.05×. That was the whole of the "reads too high for the best sellers" problem.
A curved line was tried the same day and removed. It looked 12% better on the average miss, but read by read it beat the straight line on 836 of 1,589 — 52.6%, a coin toss — and a curve has a high point, past which a better position reads as fewer units. Two shapes that cannot turn (two straight pieces with one bend; a curve pinned at position 1) were fitted on the same readings and bought nothing. Do not try a curve again without measuring it read by read — an average that looks better can still be a coin toss on the product in front of you.
Where it is weak: past position 50,000 is the worst band under every shape tried, and Home & Kitchen is poor under all of them — its readings run from position 498 to 335,000, ten times the range of a smaller category.