Arbitrage Opportunities
Find guaranteed-profit arbitrage opportunities across sportsbooks.
GET /api/v1/opportunities/arbitrageAuthentication
Requires API key. Hobby tier or higher required. Your account must have the arbitrage feature enabled.
Query Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
sport | string | all | Filter by sport(s), comma-separated (e.g. basketball, football) |
league | string | all | Filter by league(s), comma-separated (e.g. nba, nfl, nhl) |
sportsbook | string | tier-allowed | Filter by sportsbook(s), comma-separated. Tier limits enforced. |
market | string | all | Filter by market type(s), comma-separated. Supports category aliases — see Odds: Market Category Aliases. |
min_profit | number | 0.5 | Minimum profit percentage (e.g. 1.0 = 1%) |
live | boolean | — | true = live only, false = prematch only, omit = both |
state | string | pa | US state code appended as ?state= to each leg’s deep_link resolver URL (e.g. pa, nj, co, mi) |
limit | integer | 50 | Results per page (max 200) |
offset | integer | 0 | Pagination offset (max 5000) |
Profit cap: Opportunities with profit above 10% are automatically filtered out. These are almost always phantom arbs caused by stale or suspended odds rather than real opportunities.
Breaking change: The game_state field (score/period/clock) was removed from this response. Live game state is now served exclusively by the Game State endpoint and the gamestate stream channel. Join rows to game state by event_id.
Filtering Multiple Values
Use comma-separated values for multi-select filters:
?sport=basketball,football&league=nba,nflExample Requests
cURL
curl -X GET "https://api.sharpapi.io/api/v1/opportunities/arbitrage?league=nba&min_profit=1" \
-H "X-API-Key: YOUR_API_KEY"Response
Success (200)
Real response for GET /api/v1/opportunities/arbitrage?limit=1 (live soccer, captured 2026-07-02). Note deep_link is null on legs with no resolver link (e.g. exchanges) and carries the ?state= suffix (default pa) otherwise:
{
"data": [
{
"detected_at": "2026-07-02T18:24:11.629938125Z",
"estimated_net_profit_percent": 9.51,
"ev_available": false,
"ev_percentage": null,
"event_id": "iceland_-_urvalsdeild_krreykjavik_thorakureyri_2026-07-02_b2",
"event_name": "KR Reykjavik @ Thor Akureyri",
"id": "d66158994938bfb8",
"implied_total": 91.15,
"is_alternate_line": false,
"is_live": true,
"is_player_prop": false,
"league": "iceland_-_urvalsdeild",
"league_label": "Iceland - Urvalsdeild",
"legs": [
{
"deep_link": null,
"external_event_id": "20260704F0815C7E",
"line": 0.75,
"market_id": "asian_handicap",
"odds_american": 143,
"odds_decimal": 2.43,
"odds_id": "105606613889332",
"odds_probability": 0.4115,
"selection": "KR Reykjavik",
"selection_id": "19851-27567-2026-07-02:betfair_exchange:asian_handicap:kr_reykjavik_+0_75",
"sportsbook": "betfair",
"stake_percent": 45.15
},
{
"deep_link": "https://api.sharpapi.io/api/v1/deeplink/146200835628306?state=pa",
"external_event_id": "34333512",
"line": -0.75,
"market_id": "2_85317286",
"odds_american": 100,
"odds_decimal": 2,
"odds_id": "146200835628306",
"odds_probability": 0.5,
"selection": "Thor Akureyri",
"selection_id": "0HC85317286P75_1",
"sportsbook": "draftkings",
"stake_percent": 54.85
}
],
"line": 0.75,
"market_label": "Asian Handicap",
"market_type": "asian_handicap",
"oldest_odds_age_seconds": 29.013184226,
"player_name": null,
"possibly_stale": false,
"profit_percent": 9.71,
"sport": "soccer",
"start_time": "2026-07-02T17:00:00Z",
"stat_category": null,
"warnings": [
"LIVE_HIGH_PROFIT_SUSPICIOUS",
"LIVE_GAME"
]
}
],
"pagination": {
"count": 1,
"has_more": true,
"limit": 1,
"next_offset": 1,
"offset": 0,
"total": 71
},
"updated_at": "2026-07-02T18:24:11.672298584Z"
}Response Headers
X-RateLimit-Limit: 300
X-RateLimit-Remaining: 297
X-RateLimit-Reset: 1707401000
X-Data-Delay: 0
X-Request-Id: 1782526326424224-82519Error Responses
401 Unauthorized
No key supplied returns missing_api_key; a bad key returns invalid_api_key.
{
"error": {
"code": "missing_api_key",
"message": "API key required. Pass via X-API-Key header, api_key query parameter, or Bearer token.",
"docs": "https://sharpapi.io/docs/authentication"
}
}403 Tier Restricted
{
"error": {
"code": "tier_restricted",
"message": "This endpoint requires Hobby tier or higher",
"docs": "https://sharpapi.io/pricing",
"tier": "free",
"required_tier": "hobby"
}
}429 Rate Limited
retry_after is a Unix-millisecond timestamp of the next window (not a duration); the Retry-After header carries the equivalent seconds value.
{
"error": {
"code": "rate_limited",
"message": "Rate limit exceeded for hobby tier (120/min)",
"retry_after": 1737853260000,
"tier": "hobby",
"limit": 120,
"docs": "https://sharpapi.io/pricing"
}
}Response Fields
| Field | Type | Description |
|---|---|---|
id | string | Unique arbitrage identifier (hash) |
event_id | string | Event identifier |
event_name | string | Human-readable event name |
sport | string | Sport identifier (lowercase) |
league | string | League identifier |
market_type | string | Market type (moneyline, point_spread, total_points, etc.) |
line | number|null | Spread/total line (e.g., -3.5) |
profit_percent | number | Guaranteed profit as a percentage (e.g., 1.83 = 1.83%) |
estimated_net_profit_percent | number | Profit % after estimated bet-placement friction (rounding/limits) |
league_label | string | Human-readable league name |
market_label | string | Human-readable market name |
implied_total | number | Sum of implied probabilities across all legs (below 100 = arb exists) |
is_live | boolean | Whether the event is currently live |
start_time | string|null | ISO 8601 event start time |
is_alternate_line | boolean | Whether this uses a non-standard line |
possibly_stale | boolean | Whether odds may have moved since detection |
oldest_odds_age_seconds | number|null | Age of the stalest leg’s odds in seconds |
warnings | string[] | Warning flags. Possible values: LIVE_GAME (any leg live), LIVE_HIGH_PROFIT_SUSPICIOUS (live and profit > 3%), HIGH_PROFIT_SUSPICIOUS (prematch and profit > 5%), LIVE_STALE_ODDS (live, stale leg), POTENTIALLY_STALE_ODDS / VERY_STALE_ODDS (prematch staleness tiers). LOW_IMPLIED_TOTAL (2-leg arb with implied total below 96% — verify the prices before betting) is Reserved — not currently emitted on this endpoint. |
ev_available | boolean | Whether an EV opportunity exists on this market |
ev_percentage | number|null | EV percentage if available |
is_player_prop | boolean | Whether this is a player prop market |
player_name | string|null | Player name (if player prop) |
stat_category | string|null | Stat type (if player prop, e.g., points, rebounds) |
legs | array | Array of bet legs that form the arbitrage |
detected_at | string | ISO 8601 timestamp when the arb was first detected |
Leg Object
| Field | Type | Description |
|---|---|---|
sportsbook | string | Sportsbook for this leg |
selection | string | The selection (team name, Over/Under, etc.) |
odds_american | number | American odds for this leg |
odds_decimal | number | Decimal odds for this leg |
odds_probability | number | Implied probability of this leg’s odds (0.0 to 1.0) |
line | number|null | Spread/total line for this leg |
stake_percent | number | Percentage of total stake to place on this leg |
odds_id | string | Underlying odds row ID (joins back to /odds) |
deep_link | string|null | Resolver URL to this leg’s bet slip (pass state= for state subdomains). The key is always present — explicit null when no resolver link exists for the leg. |
external_event_id | string|null | Sportsbook’s native event ID |
selection_id | string|null | Sportsbook’s selection/outcome ID |
market_id | string|null | Sportsbook’s market ID |
home | object|undefined | Reserved — not currently emitted on this endpoint. Nested home-team reference: {id, numerical_id, name, abbreviation, logo, city, mascot, conference, division}. See Entity reference IDs. |
away | object|undefined | Reserved — not currently emitted on this endpoint. Nested away-team reference (same shape as home). |
sport_ref | object|undefined | Reserved — not currently emitted on this endpoint. Nested sport reference: {id, numerical_id, name}. |
league_ref | object|undefined | Reserved — not currently emitted on this endpoint. Nested league reference: {id, numerical_id, label}. |
market_ref | object|undefined | Reserved — not currently emitted on this endpoint. Nested market reference: {id, numerical_id, label}. |
sportsbook_ref | object|undefined | Reserved — not currently emitted on this endpoint. Nested sportsbook reference for this leg’s book: {id, numerical_id, label}. |
Nested reference blocks on each leg (reserved)
The leg schema reserves six nested reference objects (home, away, sport_ref, league_ref, market_ref, sportsbook_ref) mirroring the blocks on /odds. As of 2026-07-02 the arbitrage engine does not stamp them, so live legs never include these blocks — today they appear only on /odds rows for atlas-mapped books. Treat every block (and every inner field) as optional and never require them; when emitted they will be purely additive — the flat top-level fields (sport, league, market_type) and per-leg sportsbook / selection strings are unchanged. See Entity reference IDs for the shape and guarantees.
How Arbitrage Works
Arbitrage exploits price differences between sportsbooks to guarantee a profit regardless of the outcome. When the combined implied probabilities across all legs of a market total less than 100%, an arbitrage opportunity exists.
The Core Principle
Book A: Team X +150 (implied 40.0%)
Book B: Team Y -130 (implied 56.5%)
Total implied: 96.5% < 100% = ARBITRAGEWhen the total is below 100%, you can bet proportionally on every outcome and guarantee a profit.
Profit Calculation
Profit % = (100 / implied_total) - 100Using the example above:
Profit % = (100 / 96.5) - 100 = 3.63%This means for every $1,000 wagered, you are guaranteed approximately $36.30 in profit.
Calculating Optimal Stakes
To guarantee equal returns regardless of outcome, allocate stakes proportionally:
// Decimal odds: Team X = 2.50, Team Y = 1.77
const impliedX = 1 / 2.50; // 0.400
const impliedY = 1 / 1.77; // 0.565
const total = impliedX + impliedY; // 0.965
const stakeX = impliedX / total; // 41.5% of bankroll
const stakeY = impliedY / total; // 58.5% of bankrollFull Stake Example
Total bankroll for this arb: $1,000
| Leg | Stake | Odds | Payout if Wins |
|---|---|---|---|
| Team X @ DraftKings | $415 | +150 | $1,037.50 |
| Team Y @ Pinnacle | $585 | -130 | $1,035.00 |
Guaranteed profit: ~$35-38 (3.5-3.8%) regardless of outcome.
Three-Way Arbitrage
Sports like soccer and hockey have three-way markets (home/draw/away), which can also produce arbitrage:
Home Win: +200 (33.3%)
Draw: +250 (28.6%)
Away Win: +180 (35.7%)
Total implied: 97.6% -> 2.4% arbitrage profitThe same stake calculation applies — divide each leg’s implied probability by the total to determine the stake allocation.
Why Arbs Exist
- Timing differences - Sportsbooks update odds at different speeds
- Differing opinions - Books have different risk models and exposure
- Promotions - Boosted odds from one book create artificial gaps
- Errors - Occasionally, a book posts incorrect odds
Best Practices
Account Risk: Sportsbooks actively monitor for arbitrage activity. Accounts that exclusively place arb bets may be limited or restricted. Consider mixing arbitrage bets with recreational activity.
- Act fast - Arb windows are often short-lived (seconds to minutes)
- Round your stakes - A bet of $47.32 looks suspicious; round to $50
- Use pre-calculated stakes - The
stake_percentfield gives you optimal allocation - Verify odds before placing - Always confirm the odds at the sportsbook have not moved
- Start with larger arbs - Focus on
min_profit=1or higher to account for line movement - Track the
implied_total- Lower totals mean larger guaranteed profits - Diversify sportsbooks - Spreading activity across many books reduces individual account risk
Arb vs +EV Betting
| Aspect | Arbitrage | +EV Betting |
|---|---|---|
| Risk | Zero (guaranteed profit) | Variance exists |
| Profit per bet | 1-3% typical | 2-10% edge |
| Account risk | High (limits likely) | Lower |
| Capital required | High (bet both sides) | Lower |
| Sustainability | Limited by account health | Long-term viable |
Many experienced bettors prefer +EV betting over arbitrage because accounts last longer and the expected edges are often larger. See the +EV Opportunities endpoint for details.
Related Endpoints
- +EV Opportunities - Positive expected value bets
- Middles - Line discrepancy opportunities
- Arbitrage Concepts - Detailed arbitrage theory
- Arbitrage Scanner Example - Build a real-time scanner