Why a rank tracker should store snapshots, not recompute them
Store each rank check as an immutable snapshot — position, URL and the timestamp it was seen — rather than deriving history on read. A recomputed history can change without the SERP changing, which makes it useless as evidence.
There are two ways to build the chart rank tracking exists to draw — a keyword’s position over time. You can store a row every time you look, or you can store the current state and derive the past from it. The first is more rows. The second is cheaper, tidier, and quietly wrong.
What “recomputed” means in practice
A tracker that derives history keeps one authoritative record per keyword and reconstructs earlier positions from whatever it has now — a cached SERP, an index snapshot, a rolling aggregate. It looks the same on screen. The difference shows up on the day something upstream changes.
Re-run the derivation after the vendor backfills a dataset, or after your own normalisation rules change, and last month’s chart moves. Nobody edited anything. The SERP in March did not change. But the line that described March did.
That is fatal for the one job the chart has. You look at a ranking history to answer did the thing I did work. If the history can move without the SERP moving, the chart cannot answer that question, and you cannot tell the two cases apart from inside the interface.
What a snapshot is
netweft’s rank tracking writes a row every time it looks:
- the keyword and the project it belongs to
- the position that was seen, or the fact that nothing was
- the URL that held the position
- the moment the check ran
Nothing in that row is derived. Nothing recalculates it later. A month-old row says what was seen a month ago, and if it disagrees with your memory, one of you is wrong in a way that can be investigated rather than argued about.
The cost is rows, and rows are the cheapest thing in the system. A hundred tracked keywords checked daily is thirty-six thousand rows a year per project, which the database finds uninteresting.
The missing week
The design decision that follows from snapshots is what to do when a check comes back with nothing.
There are three tempting answers and two of them are wrong. Writing a zero is wrong: zero is a position, and some interfaces genuinely use it, so a reader cannot tell a rank of zero from an absence of rank. Writing nothing is also wrong: a gap in the series is indistinguishable from a check that never ran, which is a different fact with a different fix.
The right answer is that not ranking is a value. netweft stores it as a category and draws it as one. The line does not skip the week, and it does not dip to the bottom of the axis as though the domain were sitting at position 101. It says the domain was not in the results, which is what happened.
The same row is what an agent reads back through the API, at no charge, because the credits were spent when the observation was taken rather than when it is read — see reading an SEO workspace from an agent.
What this buys you
Three things, all of them boring, which is the point.
A history you can quote. When you tell a client the page moved from 14 to 9 in February, that is a claim backed by two rows with timestamps, not by a reconstruction that might render differently next quarter.
A diff you can trust. If a row changes, something changed in the world. There is no second explanation.
An audit trail for your own tooling. When a normalisation bug ships, snapshots let you find the checks that ran while it was live. Derived history absorbs the bug into the past and never mentions it again.
The trade you are actually making
Snapshots cost storage and they make backfilling awkward: you cannot retroactively improve a measurement you have declared to be an observation. That is not a limitation to be engineered around. It is the property you wanted. A measurement you can improve after the fact is a measurement, and a measurement you cannot is a record.
netweft chose the record.