Asian Handicap API: Lines, hdp and Live Odds

James Whitfield
Asian handicap is the default way to price football in Southeast Asia, and it is the market most arbitrage and value-bet builders end up modelling first. It removes the draw, it prices favourites in fractions of a goal, and the line itself moves as much as the odds do. That last part is what trips up integrations: you are not tracking two prices, you are tracking a price and a line together.
This guide covers how Asian handicap data arrives from the Odds-API.io v3 API, how to read quarter-goal lines correctly, how to compare books in a single request, and how to follow line moves live instead of polling.
Asian handicap in developer terms
A handicap is a signed goal adjustment applied to one side before settlement. A line of -0.5 on the home team means the home team starts half a goal down, so a 1-0 home win pays and a draw loses. A line of +1 on the away team means the away team starts a goal up, so a 1-0 loss is a push and the stake comes back.
Three families matter in code:
- Whole-goal lines (0, -1, -2). A result that lands exactly on the line is a push and the stake is returned.
- Half-goal lines (-0.5, -1.5). No push is possible, every outcome is a win or a loss.
- Quarter-goal lines (-0.25, -0.75, -1.25). The stake is split across the two nearest half-goal outcomes, so half of it can win while the other half pushes.
Quarter-goal settlement is bookmaker behaviour, not something the API models for you. The API tells you the line and the price; how the book settles a split stake is the book's rule, and you replicate it in your own settlement or expected-value code.
How the lines arrive: Spread plus hdp
Asian handicap comes through the Spread market. Every entry in that market carries an hdp field holding the line, along with home and away prices. There is no separate "Asian handicap" market name to look for, and the same Spread shape is used across sports, so basketball point spreads and football goal handicaps parse identically.
Odds for a single event come from /v3/odds, which requires both an eventId and a bookmakers list. The bookmakers parameter is mandatory and takes up to 30 comma-separated names.
curl -G "https://api.odds-api.io/v3/odds" \
--data-urlencode "apiKey=YOUR_KEY" \
--data-urlencode "eventId=123456" \
--data-urlencode "bookmakers=Sbobet"The response is an event object whose bookmakers property is keyed by bookmaker name, with each value being a list of markets. A trimmed Spread block looks like this:
The block below is a real response captured from a live Argentine league fixture, trimmed to one book:
{
"id": 73846346,
"home": "Ferro Carril Oeste",
"away": "Gimnasia Y Esgrima La Plata",
"bookmakers": {
"Sbobet": [
{
"name": "Spread",
"updatedAt": "2026-08-24T12:41:08.11Z",
"odds": [
{ "hdp": 0, "home": "1.640", "away": "2.250" },
{ "hdp": -0.25, "home": "2.720", "away": "1.420" }
]
}
]
}
}Two things to note. The market list is an array, so a book can expose several lines at once and you pick the one you want rather than assuming a single main line. And hdp is always expressed from the home team's point of view, so an away handicap is just the sign flipped.
Reading quarter-goal lines
For pricing work you usually want the line closest to the true balance of the match, and you want to know whether a quarter line is in play so your settlement model splits the stake. Both fall out of the hdp value:
def line_kind(hdp):
quarter = abs(hdp * 4) % 2 == 1
if quarter:
return "quarter"
return "half" if abs(hdp * 2) % 2 == 1 else "whole"
def split_legs(hdp):
"""The two half-goal outcomes a quarter line settles across."""
if line_kind(hdp) != "quarter":
return [(hdp, 1.0)]
return [(hdp - 0.25, 0.5), (hdp + 0.25, 0.5)]
spread = odds["bookmakers"]["Sbobet"][0]["odds"]
main = min(spread, key=lambda o: abs(o["hdp"]))
print(main["hdp"], line_kind(main["hdp"]), split_legs(main["hdp"]))
# -0.25 quarter [(-0.5, 0.5), (0.0, 0.5)]A -0.25 home line means half the stake sits on -0.5 and half on the draw-no-bet leg at 0. A 1-0 home win pays both halves, a draw returns half the stake and loses the other half. Getting that split right is the difference between a backtest that matches the book and one that quietly overstates returns on every drawn match.
Comparing books in one request
Because the bookmakers parameter accepts up to 30 names, a single call gives you a cross-book snapshot of the same event with no fan-out and no stitching on your side.
curl -G "https://api.odds-api.io/v3/odds" \
--data-urlencode "apiKey=YOUR_KEY" \
--data-urlencode "eventId=123456" \
--data-urlencode "bookmakers=Sbobet,12bet,M88,22Bet,1xbet"Sbobet hdp 0 home 1.640 away 2.250
Sbobet hdp -0.25 home 2.720 away 1.420
SingBet hdp -0.25 home 2.51 away 1.48
SingBet hdp 0 home 1.56 away 2.35
M88 hdp 0 home 1.570 away 2.230
1xbet hdp -1.5 home 6.37 away 1.08That is a real snapshot of one fixture. Read it carefully, because it contains three traps at once. Sbobet is quoting two lines simultaneously, so picking odds[0] would give you the pick-em line when the market has moved to -0.25. SingBet lists the same two lines in the opposite order, so index-based parsing breaks across books. And 1xbet is on -1.5, a completely different bet: its away price of 1.08 looks worthless next to SingBet 1.48 until you notice they are not the same wager. Group by hdp, then compare inside each group, and the only real comparison here is Sbobet 1.420 against SingBet 1.48 on the away side at -0.25.
Compare on the pair, not the price. Two books quoting 1.95 on different lines are not offering the same bet, and a naive best-price scan across mixed hdp values produces arbitrage that does not exist. Group by hdp first, then compare within each group.
SBOBET is the usual reference price for Asian markets. It takes serious volume on Asian handicap, its lines move early, and other books in the region tend to follow rather than lead, which makes it a sensible anchor when you are deciding whether a soft book is mispriced. See the SBOBET odds API integration page for coverage details and market availability.
One spelling gotcha that costs people an afternoon: the brand is SBOBET, but the bookmakers parameter value is Sbobet. Passing SBOBET does not match, and you get a response missing the book you asked for rather than an error telling you why. Bookmaker names are exact strings from the /v3/bookmakers list, so read them from there instead of typing brand names by hand.
Live lines over WebSocket
Asian handicap lines move constantly in play, and polling a handicap market is the wrong shape of solution. By the time you see -0.75 on a poll it may have been -1 for twenty seconds, and the interesting moment, the move itself, is exactly what a poll loses.
The WebSocket odds channel pushes updates as they happen. It requires a markets list on subscription, so ask for Spread explicitly:
// Everything is set on the connection URL. There is no subscribe frame.
const params = new URLSearchParams({
apiKey: "YOUR_API_KEY",
markets: "Spread", // required for the odds channel
channels: "odds", // allowlist: you get only what you list
eventIds: "123456"
});
const ws = new WebSocket(`wss://api.odds-api.io/v3/ws?${params}`);
ws.onmessage = (event) => {
const msg = JSON.parse(event.data);
if (msg.type !== "updated") return;
if (msg.bookie !== "Sbobet") return; // one message per bookmaker
const spread = msg.markets.find((m) => m.name === "Spread");
if (!spread) return; // suspended: dropped, not flagged
state[msg.id] = spread.odds; // replace the market, do not merge
};Two behaviours to build around. An update replaces the full market set rather than patching individual lines, so treat each message as the new truth and overwrite your local state instead of merging into it. And suspended markets are dropped from the payload rather than flagged, so a line that disappears is a line you can no longer bet, not a line that stayed where it was. Code that merges will happily keep serving a handicap the book pulled minutes ago. The WebSocket guide covers reconnects and subscription management in full.
Backtesting closing lines
Closing line value is the standard way to check whether an Asian handicap model is finding anything real. If your picks consistently beat the closing line, the edge is probably genuine; if they do not, a profitable sample is more likely variance.
Two historical endpoints cover this. /v3/historical/events takes sport, league, from and to, and returns the events in that window. /v3/historical/odds takes an eventId from that list plus a bookmakers list, and returns the recorded odds.
curl -G "https://api.odds-api.io/v3/historical/events" \
--data-urlencode "apiKey=YOUR_KEY" \
--data-urlencode "sport=football" \
--data-urlencode "league=england-premier-league" \
--data-urlencode "from=2026-01-01T00:00:00Z" \
--data-urlencode "to=2026-01-31T23:59:59Z"
curl -G "https://api.odds-api.io/v3/historical/odds" \
--data-urlencode "apiKey=YOUR_KEY" \
--data-urlencode "eventId=123456" \
--data-urlencode "bookmakers=Sbobet"Walk the events for a league and season, pull the Spread market for each, and store the hdp and price together with a timestamp. Compare your entry line and price against the last recorded pair before kickoff. Comparing prices alone is meaningless here, because a move from -0.5 at 1.90 to -0.75 at 1.90 is a real line move that a price-only backtest reads as no change at all.
Gotchas worth knowing up front
- The bookmakers parameter is required on /v3/odds and /v3/historical/odds, and caps at 30 names per request.
- Use Sbobet, not SBOBET. Names are exact strings from /v3/bookmakers, and a wrong case silently returns nothing for that book.
- hdp is signed from the home team's perspective. Flip the sign for the away side rather than looking for a separate field.
- A book can quote several lines at once. Do not assume the first entry in the array is the main line.
- Quarter-goal settlement is the book's rule, not an API field. Implement the stake split yourself.
- On the WebSocket, updates replace the market set and suspended markets vanish. Overwrite state, never merge.
- Group by hdp before comparing prices across books, or you will find arbitrage between two different bets.
Getting started
Odds-API.io covers 365+ bookmakers across 34 sports and 12,000+ leagues, with Asian handicap available through the Spread market wherever the book prices it. The free tier gives you 100 requests per hour with no credit card, which is enough to pull a full event, inspect the Spread block and check the lines you care about are there before writing any integration code.
If you are building on top of this, the Python arbitrage bot tutorial walks through the request patterns and the maths for turning cross-book snapshots into signals, and the same structure applies once you swap moneyline comparisons for handicap-grouped ones.
