How to Detect Odds Drops Without Alert Fatigue

How to detect odds drops that actually mean something: filter on limit and no-vig price, add a cooldown, and cut alert volume hard. With SSE and REST code.
An odds drop is a bookmaker admitting it was wrong. The decimal price on a selection falls, implied probability rises, and on a book that prices off order flow instead of off marketing, somebody paid real money for that revision. That is the entire signal. Everything after it — the alerting, the thresholds, the cooldowns — exists so you can hear that one thing without also hearing ten thousand things that sound like it.
The short definition
An odds drop is a fall in the decimal price offered on a specific selection, in a specific market, measured against that selection's own recent price history. Over 2.5 moving 2.10 → 1.95 is a drop: implied probability went from roughly 47.6% to 51.3%.
Two properties decide whether it's worth anything.
Who moved it. A soft book shortens a price to balance its liability or because it saw someone else shorten first. Pinnacle shortens because it accepted bets it respects, at high limits, and updated its estimate. Same arithmetic, completely different information content.
How far, and from what base. Three percent on a market with a $250 max stake at 3am is a trader tidying up. Three percent on a $15,000 limit forty minutes before kickoff is an opinion with money behind it.
So: a drop is an estimate revision, weighted by the credibility of the estimator and the size it was willing to take. Hold onto that sentence — the rest of this piece is just turning it into filters.
Odds drop vs steam move vs line movement
These three get used interchangeably in forum posts and they are not the same thing.
Line movement is the umbrella term. Any price change, either direction, any reason — weather, a scratch, a limit raise, a margin adjustment, a trader nudging a number that went stale overnight. Most line movement is housekeeping.
A steam move is fast, correlated movement on the same selection across many books at once. When people ask about steam move meaning, what they're describing is the aftershock: sharp money hit the originating market and everyone downstream repriced within seconds. Steam is inherently a cross-book phenomenon. You need a multi-book feed to observe it directly.
An odds drop is single-book and directional: this selection got shorter against its own history. Watch the book that originates the move and the drop is the cause; the steam elsewhere is the effect arriving a few seconds later.
That distinction has a practical consequence I'll state plainly, because it's the whole reason this product exists in the shape it does: I would take sub-second push from one sharp source over one-minute polling across a dozen books, every time. By the time the aggregator agrees with you, the number you wanted is gone.
Why it matters
Closing line value. If you can spot moves in the direction the price is already heading, you take the earlier number. Beating the close is the only in-sample metric that reliably predicts long-run edge, and drops are the visible mechanism by which the closing price gets built.
Information you don't have. Lineup news, a keeper pulling up in warmups, a wind revision. The price frequently knows before your data pipeline does. A sudden drop on a total is often a weather report you haven't read.
Live model calibration. Your in-play model prices a market at 1.88; the book drops 2.05 → 1.90 over ninety seconds. You haven't found an edge. You've found agreement, arriving late. Worth knowing before you stake it.
The honest caveats
Most drop-alert content stops being useful right about here, so this section is the long one.
Most drops are noise. Prematch markets open days out with low limits and wide margins, and traders adjust them constantly. Overnight and early-morning moves are disproportionately a stale line being tightened, not money arriving. Alert on every price fall and you'll have a firehose you stop reading inside a week.
A drop is not an arb and not a value bet. It tells you the price moved. It does not tell you the new price is wrong, and the market you'd actually bet into has usually moved with it. Treating a raw drop alert as a bet trigger is a fast way to donate money to a soft book with a low limit.
Direction alone is thin. Without the stake limit you can't tell a $500 market from a $20,000 one. Without a no-vig price you can't tell whether the offer moved or the margin moved. A drop from 2.10 to 2.05 where the opposing side also shortened is a book widening its hold, not changing its mind.
Reversion is real. Live markets whip around goals, cards and momentum. A 6% drop in the ninety seconds after a corner routinely unwinds completely. With no cooldown you'll log the drop, the reversion and the re-drop as three independent "signals."
One source is one source. pinnodds carries Pinnacle. If your strategy depends on price dispersion across forty books to find the outlier, this is the wrong feed and an aggregator is the right one. What you get here is depth and origination, not breadth. Both are legitimate; know which one your strategy needs.
No historical archive. The feed is real-time. There's no endpoint that hands you last month's drops. If you want backtestable history you record it yourself, and the sensible time to start is now.
How to detect odds drops in code
Two surfaces, and the difference between their payloads genuinely matters.
SSE — /odds-drop and /odds-drop-prematch. The push channel, firing the moment a price falls against its recent history. The payload names the market in sect, the moving selection in outcome, the prices in from_price and to_price, the event in id, and carries limit (Pinnacle's max stake on that market) plus nvp (the no-vig fair price). It does not include a drop percentage. You compute that yourself.
REST — /api/drops with mode=live or mode=prematch. The enriched form of the same events: market, designation, from, to, event_id, and a precomputed drop_pct. Reach for it on dashboard refresh, on reconnect backfill, or when you'd rather not do arithmetic.
Here's a Node consumer with the filters that carry their weight. There's an official SDK (npm install pinnodds) but raw EventSource shows the shape better:
import EventSource from "eventsource";
const MIN_DROP_PCT = 2.5; // sub-noise wobble, discard
const MIN_LIMIT = 2000; // low-limit early markets, discard
const COOLDOWN_MS = 90_000; // one alert per selection per 90s
const lastSeen = new Map();
const es = new EventSource(
`https://pinnodds.com/odds-drop?key=${process.env.PINNODDS_KEY}`
);
es.onmessage = (msg) => {
const d = JSON.parse(msg.data);
// SSE gives prices, not a percentage. Do the maths.
const dropPct = ((d.from_price - d.to_price) / d.from_price) * 100;
if (dropPct < MIN_DROP_PCT) return;
if (Number(d.limit) < MIN_LIMIT) return;
const key = `${d.id}:${d.sect}:${d.outcome}`;
const now = Date.now();
if (now - (lastSeen.get(key) ?? 0) < COOLDOWN_MS) return;
lastSeen.set(key, now);
// nvp is the no-vig price. If it barely moved, the book widened
// its margin rather than changing its opinion.
console.log(
`${d.sect} / ${d.outcome}: ${d.from_price} -> ${d.to_price} ` +
`(${dropPct.toFixed(2)}%, limit ${d.limit}, nvp ${d.nvp})`
);
};
es.onerror = (e) => console.error("stream error, will reconnect", e);
The limit floor on line 2 is the single highest-value filter in that file and almost nobody applies it. A low max stake is the book telling you it has low confidence and wants low exposure. Drop those alerts and your volume collapses immediately, with essentially no loss of signal.
Minimum drop percentage comes second. Start around 2–3% and tune per sport — live basketball totals move constantly, prematch soccer handicaps do not, and one global threshold will be wrong for both.
The cooldown on id + sect + outcome exists purely to kill the drop/revert/re-drop triple fire. Ninety seconds is a starting guess, not a law.
The no-vig check is the subtle one, and it's what separates people who read the feed from people who react to it. Track nvp alongside the offered price. If the raw price fell 3% but nvp barely moved, nothing happened except the book taking more margin. That single comparison filters more false odds movement alerts than any threshold you can set on price alone.
A worked example
Two alerts land inside the same minute:
sect: "Total", outcome: "Over 2.75", from_price: 2.12, to_price: 2.02, limit: 480, nvp: 2.19
sect: "Total", outcome: "Over 2.75", from_price: 1.98, to_price: 1.93, limit: 14000, nvp: 2.01
The first is a 4.7% fall and looks dramatic. It's on a $480 limit and the no-vig price sits well above the offer — a wide, early, low-confidence market being tightened. Bin it.
The second is a 2.5% fall on a $14,000 limit, with nvp tracking the offer closely, which means the fair price moved rather than the margin. Smaller number, far more information. If your alerting ranks by drop_pct you will see the first one and miss the second, which is exactly the failure mode this article exists to prevent.
For enriched rows without holding a socket open, the REST equivalent (pip install pinnodds, or plain requests):
import requests
rows = requests.get(
"https://pinnodds.com/api/drops",
params={"mode": "prematch"},
headers={"x-api-key": KEY},
timeout=10,
).json()
sharp = [r for r in rows if r["drop_pct"] >= 3.0]
for r in sharp:
print(r["event_id"], r["market"], r["designation"],
r["from"], "->", r["to"], r["drop_pct"])
Field-by-field reference lives in the docs, including the prematch variants and the raw WebSocket passthrough at /ws/feed if you want the underlying price stream rather than the derived alerts.
Where the depth changes the answer
Drops on main lines are the most watched and therefore the least informative. The interesting movement often sits on a quarter line that thinner feeds flatten or discard — soccer totals at 1.75, 2.25, 2.75 and 3.25, quarter-ball handicaps at -0.75, -1.25, -1.75, -2.25. Every line Pinnacle prices comes through, on every plan. Plans differ in rate limit and push access, not in depth (pricing).
Want props, exact scores, futures and outrights in the same shape? Pass include_specials=1 and they arrive as their own event rows carrying special, special_category, special_markets and a parent_id back to the parent fixture. Be deliberate: soccer prematch is roughly 1,500 events without the flag and roughly 12,400 with it. Specials are noisier and lower-limit as a class, so if you enable them, raise your limit floor at the same time.
Build the recorder before the strategy
The instinct is to write trading logic first. Don't.
Point an SSE consumer at /odds-drop-prematch, write every raw alert to a table with a timestamp, and let it run for two weeks with no filtering whatsoever. Then look at the distribution: what share of alerts fell under your limit floor, how many reverted inside five minutes, which sports and market types produced moves that held. You'll finish with thresholds derived from your own data instead of the numbers I guessed at above.
That recording step is also your answer to the missing archive. The feed is live; the history is yours to keep.
Takeaway
The signal in an odds drop isn't the direction — it's the direction weighted by limit and confirmed by nvp. A 5% fall on a $300 market means nothing. A 2% fall on a $20,000 market where the no-vig price moved with the offer means the book changed its mind and paid to learn why. Filter on those two fields before you filter on percentage, add a per-selection cooldown, and your alert volume falls hard while the alerts that matter survive intact. Grab a trial key, log everything raw for a fortnight, then set your thresholds from your own data rather than anyone's defaults.
Frequently asked questions
What does an odds drop actually mean?
It means a bookmaker lowered the decimal price on a selection relative to its own recent history, which raises the implied probability. On a book that prices off order flow, that revision usually means it accepted bets it respected at a meaningful limit.
What is the difference between an odds drop and a steam move?
An odds drop is single-book and directional — one selection getting shorter against its own price history. A steam move is fast, correlated movement on the same selection across many books at once, so you need a multi-book feed to see it. Watch the originating book and the drop is the cause, the steam is the effect.
How do I stop getting too many odds drop alerts?
Filter on stake limit first, then on drop percentage, then add a cooldown keyed on event, market and selection. Finally compare the no-vig price (nvp) against the offered price — if nvp barely moved, the book only widened its margin and the alert is noise.
Does the pinnodds SSE alert include a drop percentage?
No. The SSE payload on /odds-drop and /odds-drop-prematch gives you from_price and to_price, plus limit and nvp, and you compute the percentage yourself. The REST endpoint /api/drops returns the enriched form with a precomputed drop_pct.
Can I backtest historical odds drops with pinnodds?
Not from the API — the feed is real-time and there is no historical odds archive to query. If you want a backtestable history, record the SSE stream to your own storage from day one.
Get real-time Pinnacle odds in your code
Live & prematch markets with instant odds-drop alerts over SSE and WebSocket. Free trial key in seconds — no card.
Start free trial