Skip to content

Commit a034ccd

Browse files
Mlaz-codesharp-resolver[bot]claude
authored
docs(odds): reality-audit — flat envelopes, no-op params dropped, live-reverified odds family (#275)
* docs(odds-comparison): rewrite to live response contract Fixes audit findings H10/C4/C5/C6/H11/H12/H13 (verified vs live :3003): - event= -> event_id in param table, callout, and all examples, ahead of the 2026-07-01 deprecated-alias sunset - envelope {success,data,meta} -> flat {data,pagination,updated_at} - data is a flat array of {market_type,selection,line,books[],book_holds[]}, not a single nested event object - books is an array of {sportsbook,odds_american,odds_decimal,timestamp} sorted best-first, not an object keyed by book id - per-entry book_holds[{sportsbook,hold}] replaces the nonexistent hold.{best_available,by_book} - drop best_book/best_odds/worst_book/worst_odds/spread (never emitted) and the "Understanding the spread Field" section - drop phantom selection_type + event-metadata fields; document the real pagination object; fix the X-Request-Id example format EN only; de/es/pt-BR backfill tracked separately. Type: docs Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * docs(odds-delta): rewrite to real flat envelope; fix examples + 401 Fixes audit C1/C2/H7/H8 + phantom-field findings (verified vs live :3003): - envelope {success, meta:{count,total,books_changed,pagination,filters}} -> real flat {data, removed, pagination, updated_at, meta:{server_time}, overflow?} - remove meta.books_changed (does not exist — broke JS .join()/Python KeyError) - document the top-level removed[] array ({id,sportsbook,removed_at}) + overflow flag - drop phantom data-row field status:"upcoming" - JS/Python examples read pagination.count + removed.length; still chain on meta.server_time (real) - 401: code unauthorized -> missing_api_key / invalid_api_key, real message + docs URL - param table: event -> event_id (deprecated alias, sunset 2026-07-01), live -> is_live - fix X-Request-Id example format Deferred pending the no-op-param decision (S4): sort/fields/min_odds/max_odds are silently ignored by the delta handler but left in the param table for now. EN only; de/es/pt-BR backfill tracked separately. Type: docs Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * docs(odds-delta): drop no-op params sort/fields/min_odds/max_odds (S4 #1-3) The delta handler ignores all four (always chronological, full row, no odds-range filter). Removed from the param table per the approved case-by-case S4 decision; they remain implement-candidates for engineering. min_odds/max_odds still work on /opportunities/* and stay documented there. Type: docs Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * docs(odds): flat envelope, drop no-op params, fix tiers/errcodes/fields Fixes audit H1/H2/H3/H4/H5/H6 + MEDIUM/LOW (verified vs origin/main + live): - envelope {success, meta:{count,total,books_available,books_returned,pagination, filters}} -> real flat {data, pagination, updated_at}; pagination omits total on /odds (omitempty); same fix applied to the group_by=event example - remove no-op params + sections (S4, approved): sort, fields, min_odds, max_odds - param table: event -> event_id (alias sunset 2026-07-01), live -> is_live; filtering examples updated to is_live= / event_id= - 401: unauthorized -> missing_api_key/invalid_api_key + real docs URL - 403 tier_restricted: add tier/required_tier, fix docs URL (sharpapi.io/pricing). Pinnacle=Sharp+ is correct (sportsbooks.go RequiresTier "sharp") - 429: retry_after documented as Unix-ms timestamp + Retry-After header note - schema: document emitted fields is_player_prop, is_stale_pregame_price, market_segment; market_type description uses canonical values - Book Access table: Hobby books corrected (BetRivers/Bet365 US are hobby), Sharp "All 32" -> 25, Enterprise -> All (unlimited) - group_by event_name "A vs B" -> "Away @ Home"; nested-ref logo host cdn.opticodds.com -> cdn.sharpapi.io; X-Request-Id format EN only; de/es/pt-BR backfill tracked separately. Type: docs Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * docs(odds-best): flat envelope; document consensus_odds/market_hold; fix fields Fixes audit C3/H9 + MED/LOW (verified vs live :3003): - envelope {success, data, meta} -> real flat {data, pagination, updated_at} (pagination includes total here); JS example stops destructuring meta - document emitted-but-undocumented fields: consensus_odds, market_hold, all_books[].sportsbook (alias of book) - remove all_books[].line (never emitted) - best_odds.probability noted as also emitted as odds_probability (example aligned) - 401 unauthorized -> missing_api_key/invalid_api_key; 429 retry_after = Unix-ms - param table: event -> event_id (alias sunset 2026-07-01), live -> is_live; offset max 5000 -> 500 (odds-family cap); X-Request-Id format EN only; de/es/pt-BR backfill tracked separately. Type: docs Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * docs(odds-batch, odds-closing): fix envelopes + book-value shape Fixes audit H14/H15/H16/H17 + MED/LOW (verified vs live :3003): odds-batch: - envelope {success, ..., meta:{requested,found,missing,max_batch,filters}} -> flat {data:{events,missing_events}, updated_at}; examples derive counts from data.events.length / data.missing_events.length (no meta.found/requested) - remove phantom event game_state field; drop docs field from validation_error examples; 401 -> missing_api_key/invalid_api_key odds-closing: - data.books[<book>] is a BookClosing OBJECT {odds, captured_at, seconds_before_kickoff, capture_trigger, is_final}, not a bare ClosingOdd[]; fix JS/Python to read payload.odds; add BookClosing shape table - ClosingOdd: document real CLV fields (market_id, selection_id, canonical_key, implied_probability, no_vig_probability, fair_close_decimal, closing_probability, source_updated_at); odds_decimal is full precision; fix internal historical links EN only; de/es/pt-BR backfill tracked separately. Type: docs Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * docs(odds): 2026-07-02 live re-verification fixes on top of the 06-26 audit Re-verified every odds-family endpoint against the live API (OVH :3003) and the sharp-api-go source; fixed drift the 06-26 audit predates or missed: - odds: market category alias table corrected to real expansion (main = 7-market exact set incl. run_line/puck_line/total_goals/total_runs; spread/total are substring matches); documented the moneyline_3-way carve-out; grouped-mode pagination carries total + no cursor; nested-ref absence also depends on per-book adapter coverage. - odds-delta: 400 missing-since example now matches the real message (no docs field); confirmed removed[] cap 1000, since clamp 10m, overflow flag, limit cap 500 live. - odds-comparison: market and sportsbook query params are NO-OPs in oddsComparisonHandler — removed from the params table, examples now filter client-side; documented the functional market_segment filter. - odds-best: min_odds/max_odds parsed but never applied — dropped; offset cap not enforced on this endpoint — "(max 500)" removed; all_books[].edge is percent vs the CONSENSUS decimal (can be negative), not "over the worst odds"; example replaced with a real live row. - odds-batch: real 400 messages ("event_ids array required" / "Maximum N event_ids per request for X tier"); nested odds rows carry id/event_uuid/external_event_id/market_label/odds_probability — added to schema; example replaced with a real live response. - odds-closing: ClosingOdd rows carry market_segment — added; market_type examples corrected to canonical values. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --------- Co-authored-by: sharp-resolver[bot] <3736210+sharp-resolver[bot]@users.noreply.github.com> Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
1 parent 718df7a commit a034ccd

6 files changed

Lines changed: 393 additions & 528 deletions

File tree

content/en/api-reference/odds-batch.mdx

Lines changed: 53 additions & 79 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ curl -X POST "https://api.sharpapi.io/api/v1/odds/batch" \
5151
-H "X-API-Key: YOUR_API_KEY" \
5252
-H "Content-Type: application/json" \
5353
-d '{
54-
"event_ids": ["nba_celtics_lakers_2026-01-26_b3", "nba_heat_warriors_2026-01-26_b3"],
54+
"event_ids": ["mlb_phillies_pirates_2026-07-02_b2", "mlb_athletics_marlins_2026-07-03_b3"],
5555
"sportsbook": "draftkings",
5656
"market": "moneyline"
5757
}'
@@ -66,14 +66,14 @@ const response = await fetch('https://api.sharpapi.io/api/v1/odds/batch', {
6666
'Content-Type': 'application/json'
6767
},
6868
body: JSON.stringify({
69-
event_ids: ['nba_celtics_lakers_2026-01-26_b3', 'nba_heat_warriors_2026-01-26_b3'],
69+
event_ids: ['mlb_phillies_pirates_2026-07-02_b2', 'mlb_athletics_marlins_2026-07-03_b3'],
7070
sportsbook: 'draftkings',
7171
market: 'moneyline'
7272
})
7373
});
7474

75-
const { data, meta } = await response.json();
76-
console.log(`Fetched odds for ${meta.found} of ${meta.requested} events`);
75+
const { data } = await response.json();
76+
console.log(`Fetched ${data.events.length} events (${data.missing_events.length} missing)`);
7777

7878
// Events are nested under data.events
7979
for (const event of data.events) {
@@ -96,14 +96,14 @@ response = requests.post(
9696
'Content-Type': 'application/json'
9797
},
9898
json={
99-
'event_ids': ['nba_celtics_lakers_2026-01-26_b3', 'nba_heat_warriors_2026-01-26_b3'],
99+
'event_ids': ['mlb_phillies_pirates_2026-07-02_b2', 'mlb_athletics_marlins_2026-07-03_b3'],
100100
'sportsbook': 'draftkings',
101101
'market': 'moneyline'
102102
}
103103
)
104104
result = response.json()
105-
meta = result['meta']
106-
print(f"Fetched odds for {meta['found']}/{meta['requested']} events")
105+
data = result['data']
106+
print(f"Fetched {len(data['events'])} events ({len(data['missing_events'])} missing)")
107107

108108
# Events are nested under data.events
109109
for event in result['data']['events']:
@@ -121,73 +121,52 @@ if result['data']['missing_events']:
121121

122122
```json
123123
{
124-
"success": true,
125124
"data": {
126125
"events": [
127126
{
128-
"event_id": "nba_celtics_lakers_2026-01-26_b3",
129-
"event_name": "Los Angeles Lakers @ Boston Celtics",
130-
"sport": "basketball",
131-
"league": "nba",
132-
"start_time": "2026-01-26T19:30:00Z",
133-
"is_live": false,
127+
"event_id": "mlb_phillies_pirates_2026-07-02_b2",
128+
"event_name": "Pittsburgh Pirates @ Philadelphia Phillies",
129+
"sport": "baseball",
130+
"league": "mlb",
131+
"start_time": "2026-07-02T16:35:00Z",
132+
"is_live": true,
134133
"odds": [
135134
{
135+
"id": "92183869662116",
136136
"sportsbook": "draftkings",
137+
"event_uuid": "217ee1d7227d1ad1",
138+
"external_event_id": "34343268",
137139
"market_type": "moneyline",
138-
"selection": "Boston Celtics",
139-
"selection_type": "home",
140-
"odds_american": -210,
141-
"odds_decimal": 1.476,
142-
"line": null,
143-
"timestamp": "2026-01-26T10:15:00.000Z"
144-
},
145-
{
146-
"sportsbook": "draftkings",
147-
"market_type": "moneyline",
148-
"selection": "Los Angeles Lakers",
140+
"market_label": "Moneyline",
141+
"selection": "PIT Pirates",
149142
"selection_type": "away",
150-
"odds_american": 175,
151-
"odds_decimal": 2.75,
143+
"odds_american": 104,
144+
"odds_decimal": 2.04,
145+
"odds_probability": 0.4902,
152146
"line": null,
153-
"timestamp": "2026-01-26T10:15:00.000Z"
154-
}
155-
]
156-
},
157-
{
158-
"event_id": "nba_heat_warriors_2026-01-26_b3",
159-
"event_name": "Golden State Warriors @ Miami Heat",
160-
"sport": "basketball",
161-
"league": "nba",
162-
"start_time": "2026-01-26T20:00:00Z",
163-
"is_live": false,
164-
"odds": [
147+
"timestamp": "2026-07-02T18:21:35.800662605Z"
148+
},
165149
{
150+
"id": "47445279247340",
166151
"sportsbook": "draftkings",
152+
"event_uuid": "217ee1d7227d1ad1",
153+
"external_event_id": "34343268",
167154
"market_type": "moneyline",
168-
"selection": "Miami Heat",
155+
"market_label": "Moneyline",
156+
"selection": "PHI Phillies",
169157
"selection_type": "home",
170-
"odds_american": -130,
171-
"odds_decimal": 1.769,
158+
"odds_american": -135,
159+
"odds_decimal": 1.741,
160+
"odds_probability": 0.5745,
172161
"line": null,
173-
"timestamp": "2026-01-26T10:15:05.000Z"
162+
"timestamp": "2026-07-02T18:21:35.800662605Z"
174163
}
175164
]
176165
}
177166
],
178-
"missing_events": []
167+
"missing_events": ["mlb_athletics_marlins_2026-07-03_b3"]
179168
},
180-
"meta": {
181-
"requested": 2,
182-
"found": 2,
183-
"missing": 0,
184-
"max_batch": 50,
185-
"filters": {
186-
"sportsbook": "draftkings",
187-
"market": "moneyline"
188-
},
189-
"updated_at": "2026-01-26T10:15:05.000Z"
190-
}
169+
"updated_at": "2026-07-02T18:21:36Z"
191170
}
192171
```
193172

@@ -198,7 +177,7 @@ X-RateLimit-Limit: 300
198177
X-RateLimit-Remaining: 296
199178
X-RateLimit-Reset: 1737853200
200179
X-Data-Delay: 0
201-
X-Request-Id: req_batch_abc123
180+
X-Request-Id: 1782526326424224-82519
202181
```
203182

204183
### Error Responses
@@ -208,8 +187,7 @@ X-Request-Id: req_batch_abc123
208187
{
209188
"error": {
210189
"code": "validation_error",
211-
"message": "Batch limit exceeded. Your tier (hobby) allows 10 events per batch, but 25 were requested.",
212-
"docs": "https://docs.sharpapi.io/en/api-reference/odds-batch"
190+
"message": "Maximum 10 event_ids per request for hobby tier"
213191
}
214192
}
215193
```
@@ -219,26 +197,28 @@ X-Request-Id: req_batch_abc123
219197
{
220198
"error": {
221199
"code": "validation_error",
222-
"message": "The 'event_ids' field is required and must be a non-empty array of event IDs.",
223-
"docs": "https://docs.sharpapi.io/en/api-reference/odds-batch"
200+
"message": "event_ids array required"
224201
}
225202
}
226203
```
227204

228205
***401 Unauthorized***
206+
207+
No key supplied returns `missing_api_key`; a bad key returns `invalid_api_key`.
208+
229209
```json
230210
{
231211
"error": {
232-
"code": "unauthorized",
233-
"message": "Invalid or missing API key",
234-
"docs": "https://docs.sharpapi.io/en/authentication"
212+
"code": "missing_api_key",
213+
"message": "API key required. Pass via X-API-Key header, api_key query parameter, or Bearer token.",
214+
"docs": "https://sharpapi.io/docs/authentication"
235215
}
236216
}
237217
```
238218

239219
## Response Structure
240220

241-
The batch response nests events and missing IDs under `data`, with counts in `meta`:
221+
The batch response nests events and missing IDs under `data`. There is **no** `meta` block — derive counts client-side from `data.events.length` and `data.missing_events.length`.
242222

243223
### Data Object
244224

@@ -247,18 +227,8 @@ The batch response nests events and missing IDs under `data`, with counts in `me
247227
| `data.events` | EventOdds[] | Array of event objects with nested odds |
248228
| `data.missing_events` | string[] | Event IDs that were not found or had no odds |
249229

250-
### Meta Object
251-
252-
| Field | Type | Description |
253-
|-------|------|-------------|
254-
| `meta.requested` | integer | Number of event IDs in your request |
255-
| `meta.found` | integer | Number of events that had odds data |
256-
| `meta.missing` | integer | Number of events not found |
257-
| `meta.max_batch` | integer | Maximum batch size for your tier |
258-
| `meta.filters` | object | Applied filters (`sportsbook`, `market`) |
259-
260230
<Callout type="info">
261-
If an event ID in your request has no odds (e.g., the event has ended or doesn't exist), it will appear in `data.missing_events` rather than causing an error. The response will still include odds for all valid events.
231+
If an event ID in your request has no odds (e.g., the event has ended, doesn't exist, or has no rows matching your `sportsbook`/`market` filters), it will appear in `data.missing_events` rather than causing an error. The response will still include odds for all valid events.
262232
</Callout>
263233

264234
## Response Schema
@@ -275,19 +245,23 @@ Each item in the `data.events` array is an event object with nested odds:
275245
| `league` | string | League slug |
276246
| `start_time` | string | ISO 8601 event start time |
277247
| `is_live` | boolean | Whether event is live |
278-
| `game_state` | object \| undefined | Live game state (only for live events with scores) |
279248
| `odds` | array | Array of odds for this event |
280249

281250
### Odds Object (nested in event)
282251

283252
| Field | Type | Description |
284253
|-------|------|-------------|
254+
| `id` | string | Unique odds identifier (same `id` as `/odds` rows) |
285255
| `sportsbook` | string | Sportsbook ID |
286-
| `market_type` | string | Market type |
256+
| `event_uuid` | string\|undefined | Stable canonical event UUID from the SharpAPI atlas, when mapped |
257+
| `external_event_id` | string\|undefined | The sportsbook's own native event ID, when distinct from `event_id` |
258+
| `market_type` | string | Canonical market type (e.g., `moneyline`) |
259+
| `market_label` | string\|undefined | Human-readable market display label (e.g., `Moneyline`) |
287260
| `selection` | string | Selection name |
288-
| `selection_type` | string | `home`, `away`, `over`, `under` |
261+
| `selection_type` | string | `home`, `away`, `over`, `under`, etc. — see [Selection types](/en/api-reference/odds/#selection-types) |
289262
| `odds_american` | number | American odds |
290263
| `odds_decimal` | number | Decimal odds |
264+
| `odds_probability` | number | Implied probability |
291265
| `line` | number \| null | Line value |
292266
| `timestamp` | string | ISO 8601 time SharpAPI last refreshed this odd through its pipeline — advances every ingest cycle. A feed-freshness / liveness signal (matches OpticOdds' `timestamp`); it is NOT when the price last changed. See [understanding the `timestamp` field](/en/concepts/pinnacle-odds-changed-at/). |
293267

@@ -329,7 +303,7 @@ async function refreshWatchlist(eventIds) {
329303
body: JSON.stringify({ event_ids: eventIds })
330304
});
331305

332-
const { data, meta } = await response.json();
306+
const { data } = await response.json();
333307

334308
if (data.missing_events.length > 0) {
335309
console.warn('Missing events:', data.missing_events);

0 commit comments

Comments
 (0)