serpcurve 0.1.0
Fit a search click-through curve to your own measured position/impression/click rows, then convert ranking positions into expected clicks. No built-in industry CTR table.
To use this package, run the following command in your project's root directory:
Manual usage
Put the following dependency into your project's dependences section:
serpcurve
Fit a search click-through curve to your own measured data, then use it to turn ranking positions into expected clicks.
The library deliberately ships no built-in "industry average" CTR table. A
published curve is somebody else's SERP, with their brands, their intent mix and
their ad load. Hand serpcurve the (position, impressions, clicks) rows you
already have — a Search Console export is exactly the right shape — and every
number it hands back traces to a measurement you own.
Written for the pipeline behind toolsthatrank.com, which refuses to publish a figure it cannot check against a source.
Model
ctr(p) = ctr1 * p^(-alpha)
Two parameters, fitted by impression-weighted least squares on ln(ctr) against
ln(position). Weighting by impressions stops a single 3-impression row at
position 2 from dominating the fit.
Usage
import serpcurve;
Observation[] rows = [
Observation(1.8, 9_100, 2_410),
Observation(4.2, 6_300, 540),
Observation(11.6, 4_800, 62),
];
auto curve = fitCtrCurve(rows); // CtrCurve(ctr1, alpha)
auto quality = logFitQuality(curve, rows);
auto ctr = ctrAt(curve, 3.0); // modelled CTR at position 3
auto gain = clickDelta(curve, 4_800, 11.6, 4.0); // clicks won by that move
auto p = positionForCtr(curve, 0.05); // where the curve predicts a 5% CTR
fitCtrCurve throws CurveFitException rather than returning a guess when the
rows cannot support a fit: fewer than two usable rows, or every usable row
sitting at one position, so the slope is undefined.
API
| function | what it returns |
|---|---|
fitCtrCurve(rows) | the impression-weighted power-law fit |
logFitQuality(curve, rows) | weighted R² in the log space where the fit ran |
ctrAt(curve, position) | modelled CTR, clamped to 0..1 |
expectedClicks(curve, impressions, position) | impressions × modelled CTR |
clickDelta(curve, impressions, from, to) | clicks gained or lost by a move |
positionForCtr(curve, ctr) | the inverse of ctrAt |
blendedPosition(rows) | impression-weighted average position |
aggregateCtr(rows) | total clicks ÷ total impressions |
observedCtr(row) | one row's measured CTR |
isFittable(row) | whether a row can enter the fit at all |
Rows that cannot be true are dropped rather than silently distorting the result: a position below 1, non-positive impressions, or more clicks than impressions.
Tests
dub test --compiler=ldc2
Nine unittest blocks, including a round trip that generates rows from a known
curve and checks the fit recovers both parameters, and a check that
positionForCtr inverts ctrAt.
License
MIT.
- 0.1.0 released 2 days ago
- theluckystrike/serpcurve-d
- toolsthatrank.com/
- MIT
- Copyright (c) 2026, ToolsThatRank
- Authors:
- Dependencies:
- none
- Versions:
-
Show all 2 versions0.1.0 2026-Aug-08 ~main 2026-Aug-08 - Download Stats:
-
-
0 downloads today
-
1 downloads this week
-
1 downloads this month
-
1 downloads total
-
- Score:
- 0.0
- Short URL:
- serpcurve.dub.pm