Steam Move Betting: What It Is and How to Detect It

Steam move betting explained: what a steam move is, how it differs from ordinary line movement and odds drops, plus a working Python detector on a pushed Pinnacle feed.
A steam move is a fast, one-directional price move on the same side of a market, appearing across multiple books inside a short window — minutes, sometimes seconds. That's it. Not "a big move", not "a move against the public": speed, direction, and correlation across books. Everything else people attach to the phrase is folklore.
If you're building anything around steam move betting, the useful part is that the definition is mechanical enough to code. The hard part is deciding what to do once you've detected one, and that's where most of this article lives.
The short definition
Three conditions, all of which have to hold:
- Direction. Price on one selection moves consistently one way. Home -0.5 goes from 1.95 to 1.83, not 1.95 → 1.83 → 1.94.
- Speed. It happens inside a tight window. A handicap drifting two cents over six hours is a market breathing; two cents in ninety seconds is somebody hitting it.
- Correlation. More than one book does the same thing shortly after, usually with a sharp book leading and softer books following.
Drop the third condition and you have a single-book move. Drop the second and you have ordinary line movement. Most people who say "steam" mean condition 1 plus a vague feeling about condition 2, which is why the term gets used to describe every price change on a Tuesday.
Steam is caused by respected money — the accounts a sharp book will actually accept size from. That's the whole mechanism behind the term "sharp money" being treated as informative. It isn't magic; it's a bookmaker updating a probability estimate because someone with a track record just told it something with their stake.
What it is not: normal line movement
Prices move constantly, and most of that movement carries no information you can trade against.
- Balancing. A book has too much on one side and shades the price to attract the other. Nothing about the true probability changed.
- News and lineups. A keeper is out. Everyone reprices at once. This looks exactly like steam by the three conditions above — same direction, fast, correlated — but the cause is public. If you saw the team sheet at the same time as the market, you have no edge; you're just a slower version of everybody else.
- Live game state. In-play prices move violently after every shot on target. Treating that as steam is a category error. A goal at 78' is not sharp money.
- Vig and limit changes. A book widening its margin near kickoff moves both sides. Direction test fails on the pair, but it will fire on a naive one-sided detector.
The practical upshot: your detector needs to be able to say "both sides moved" and "the event has started" before it says "steam".
What it is not: an odds drop
This distinction matters if you're consuming any drop-alert feed, ours included.
An odds drop is a fact about one book: a price fell relative to its own recent history. It's measurable, unambiguous, and computable from a single price series. A steam move is an interpretation across books: the same side fell fast almost everywhere.
Every steam move contains at least one drop. The reverse is nowhere near true. A drop on a corners-total in a Norwegian second-division fixture at 3am is usually a limit adjustment or a stale line being cleaned up.
So: drops are the raw event, steam is the pattern you infer from them plus timing plus confirmation elsewhere. Build the drop layer first.
Why the sharp book's line is the reference
You need one price series to be the leader, otherwise you're just correlating noise with noise.
Pinnacle is that series because of its business model, not its branding. It runs on low margin and high volume and takes sharp action rather than restricting it. When an informed bettor wants size, that's where they go, and the price absorbs the information immediately. Soft books do the opposite: they limit winners, so informed money never reaches their line, so their line has to be copied from somewhere.
Which gives you the reference architecture:
- Pinnacle = leading indicator. Where the information enters the market.
- Everyone else = lagging. Where the price you can actually get sits, for a while.
Pinnacle closed its own public API in July 2025. pinnodds carries the same market data as an independent service — live and prematch, pushed rather than polled, which for steam detection is the difference between seeing a move and seeing its aftermath. Full endpoint reference is in the docs.
Where steam actually pays off
Two honest uses, in order of how well they still work.
As a model check. You have a number. The market moves hard against it. Either your number is stale or you know something the market doesn't. Most of the time it's the former, and that's valuable information delivered free.
As a closing-line proxy. Steam tells you which way the closing line is heading. If you're evaluating your own bets against closing prices — and you should be — knowing the direction of travel early tells you whether you're beating the close or getting run over by it.
The third use, actually chasing the steam onto a soft price, is the one everybody talks about and the one that has decayed the most.
The honest caveats
Steam chasing is mostly dead at soft books. They solved it. Bet delay on submission, price re-quote, stake reduction to pocket change, and in a lot of cases straight-up automated copying of the sharp line within seconds of it moving. The window that made steam chasing a business in 2012 is now measured in seconds and the accounts that exploit it get limited within weeks. If your plan is "detect steam, hammer the stale book", budget for account churn as a line item.
News is indistinguishable from information at the wire. Your detector cannot tell you why the price moved. Confirmed lineups, a suspension, a weather report — all produce textbook steam signatures. You need a news source or a lineup feed to filter, and even then you're racing people who have both.
One book is one opinion. pinnodds is Pinnacle only. That's deliberate — it's the reference line — but it means you cannot verify correlation across books from our feed alone. If you need "did 15 books follow?", you need an aggregator alongside us. We're not that, and pretending otherwise would waste your integration time.
There's no historical odds archive. The feed is real-time. If you want to backtest whether steam-following would have worked on last season's Serie A, you have to record the stream yourself starting today. Persist everything from day one; you cannot ask us for last month.
SSE alerts don't include a drop percentage. An alert on /odds-drop gives you sect (the market), outcome (the moving selection), from_price, to_price, id (the event), plus limit (Pinnacle's max stake) and nvp (the no-vig fair price). No drop_pct. If you want the precomputed percentage, that's the REST shape on /api/drops, where the fields are market, designation, from, to, event_id and drop_pct. Two different shapes for two different jobs; know which one you're parsing.
Small markets lie. A drop on a low-limit prop is noise more often than signal. The limit field is your best single filter — Pinnacle risking real money on a price is a stronger statement than Pinnacle nudging a market it barely takes action on.
Detecting it in code
Here's a working detector on the Python SDK. It consumes the SSE drop stream, keeps a short per-selection history, and only fires when several drops land on the same selection inside a window and in the same direction — the mechanical version of the three conditions above.
# pip install pinnodds
import time
from collections import defaultdict, deque
from pinnodds import Client
client = Client(api_key="YOUR_KEY")
WINDOW_S = 120 # how tight the burst has to be
MIN_EVENTS = 2 # distinct drops on the same selection
MIN_TOTAL_PCT = 2.0 # cumulative move, in percent
MIN_LIMIT = 500 # ignore markets Pinnacle won't take size on
history = defaultdict(lambda: deque(maxlen=20))
def pct_drop(from_price, to_price):
return (float(from_price) - float(to_price)) / float(from_price) * 100.0
def stream_drops():
for a in client.odds_drop(): # SSE: /odds-drop
key = (a["id"], a["sect"], a["outcome"])
now = time.time()
if float(a.get("limit") or 0) < MIN_LIMIT:
continue
move = pct_drop(a["from_price"], a["to_price"])
if move <= 0: # price went out, not in
continue
h = history[key]
h.append((now, move))
recent = [(t, m) for t, m in h if now - t <= WINDOW_S]
total = sum(m for _, m in recent)
if len(recent) >= MIN_EVENTS and total >= MIN_TOTAL_PCT:
print(
f"STEAM {a['sect']} / {a['outcome']} "
f"event={a['id']} {a['from_price']}->{a['to_price']} "
f"total={total:.2f}% over {int(now - recent[0][0])}s "
f"limit={a['limit']} nvp={a.get('nvp')}"
)
h.clear() # don't re-fire on the same burst
if __name__ == "__main__":
stream_drops()
Four things worth calling out in that code, because they're where naive detectors break:
The limit filter does more work than any threshold you tune. It's the cheapest way to separate "Pinnacle changed its mind" from "Pinnacle tidied up a market nobody bets".
h.clear() after firing prevents the classic bug where a five-tick move produces four alerts. You want one signal per burst, then a fresh window.
nvp is on the alert for a reason. Compare the no-vig fair price to your own model number after the move — if you still disagree, that's interesting. If the move brought the market to your number, the market just caught up with you and there's nothing to do.
And the window is a genuine tuning knob, not a constant. Prematch soccer 24 hours out tolerates WINDOW_S = 600. Live basketball needs 20 seconds or you'll classify every possession as steam. Split your config by event_type.
If you'd rather not maintain the history buffer, poll GET /api/drops?mode=live and use the precomputed drop_pct — simpler, coarser, and you'll see the move slightly later. For steam specifically I'd take the push. Both surfaces are on every plan; the plan tiers differ on rate limit and push access, not on line depth — every line Pinnacle prices comes through either way, quarter lines included.
Takeaway
Build the drop layer before the steam layer, and keep them conceptually separate: a drop is a measurement on one book's price series, steam is an inference about the market. Filter on limit before you filter on percentage, because size-backed movement is the only kind that carries information. And record your stream from day one — there's no archive to go back for, and the only way to know whether your steam threshold is any good is to check what happened after it fired.
Frequently asked questions
What is a steam move in betting?
A steam move is a fast, one-directional price move on the same side of a market that shows up across multiple books in a short window, usually triggered by respected money hitting a sharp book first. The defining features are speed, direction and correlation — not the size of the move alone.
What is the difference between a steam move and an odds drop?
An odds drop is a measurable fact about one book: a price fell against its own recent history. A steam move is an interpretation across books: the same side moved fast in the same direction almost everywhere. Every steam move contains a drop, but most drops are not steam.
Does steam chasing still work?
Rarely at soft books, because they limit or bet-delay anyone who consistently takes stale prices, and many now copy the sharp line within seconds. Steam detection is still useful as a signal for deciding which side you want and whether your own model disagrees with the market.
Why use Pinnacle's line as the reference for steam moves?
Pinnacle takes sharp action rather than restricting it, so its price is a consensus of informed money and it moves first. That makes it the leading indicator other books follow, which is exactly what you need as a reference series.
Is there still a public Pinnacle API for line movement?
Pinnacle closed its own public API in July 2025. pinnodds is an independent paid service that carries the same live and prematch market data over REST, a WebSocket passthrough at /ws/feed and SSE odds-drop streams.
How do I get the drop percentage for a steam alert?
SSE alerts on /odds-drop carry from_price, to_price, limit and nvp but no precomputed percentage — you compute it yourself. The REST /api/drops rows are the enriched form and include drop_pct directly.
Get real-time Pinnacle odds in your code
The Pinnacle APIby pinnodds: live & prematch markets with instant odds-drop alerts over SSE and WebSocket. See plans and pricing, or get a free key in seconds — no card, no Pinnacle account.
Get your free API key