Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 26 additions & 2 deletions content/en/api-reference/odds-delta.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -36,13 +36,17 @@ Use the `server_time` from each response as the `since` value for your next requ
| `min_odds` | number | - | Minimum American odds filter (e.g., `-110`) |
| `max_odds` | number | - | Maximum American odds filter (e.g., `+200`) |
| `state` | string | β€” | US state code for sportsbook deep links (e.g., `nj`, `ny`, `il`). When set, `deep_link` URLs include `?state=XX` so the redirect targets the correct state-specific sportsbook domain. |
| `limit` | integer | 50 | Max results per page (max 200) |
| `offset` | integer | 0 | Pagination offset |
| `limit` | integer | 50 | Max results per page (max 500) |
| `offset` | integer | 0 | Pagination offset. **Must be ≀ 500.** For older changes, advance `since` to the previous response's `server_time` rather than using offset. |

<Callout type="warning">
The `since` parameter is **required**. Omitting it returns a `400 validation_error` error.
</Callout>

<Callout type="info">
**`since` has a 10-minute retention window for removals.** Odds removals older than 10 minutes are pruned from memory. If you send a `since` older than that, the `data` array still honors your timestamp, but the `removed` list can only contain odds removed within the last 10 minutes β€” and `since_clamped: true` will be set on the response. Advance `since` at the cadence in the Polling Pattern section below to avoid this.
</Callout>

## Example Requests

<Tabs items={['cURL', 'JavaScript', 'Python']}>
Expand Down Expand Up @@ -171,6 +175,17 @@ X-Request-Id: req_delta_abc123
}
```

***400 Offset too large***
```json
{
"error": {
"code": "offset_too_large",
"message": "offset must be <= 500; advance `since` to the previous response's `updated_at` to retrieve older changes",
"max_offset": 500
}
}
```

***401 Unauthorized***
```json
{
Expand All @@ -193,6 +208,15 @@ The `meta` object includes two additional fields:
| `meta.server_time` | string | ISO 8601 server timestamp. Use as the `since` value for your next request |
| `meta.books_changed` | array | List of sportsbook IDs that had updates in this delta |

### Truncation and Clamping Flags

Two optional top-level boolean flags appear only when the server had to apply a safety limit to the response. Well-behaved clients polling at the recommended cadence never see them.

| Field | Type | Description |
|-------|------|-------------|
| `removed_truncated` | boolean | Present and `true` when the `removed` array hit the 1000-entry server cap. There are more removed odds the server didn't include. Usually means your `since` is too old or your filters are too broad β€” narrow the window or filter set and re-poll. |
| `since_clamped` | boolean | Present and `true` when `since` was older than the server's 10-minute removal retention. The `data` array still honors your original `since`, but `removed` is restricted to the last 10 minutes of removals. Advance `since` to `meta.server_time` each call to avoid this. |

## Polling Pattern

The recommended polling pattern uses `server_time` to chain requests:
Expand Down
16 changes: 13 additions & 3 deletions content/en/api-reference/odds.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,8 @@ The sportsbooks returned in your results depend on your subscription tier. Free
| `group_by` | string | β€” | Group results by field (e.g., `event`) |
| `state` | string | β€” | US state code for sportsbook deep links (e.g., `nj`, `ny`, `il`). When set, `deep_link` URLs include `?state=XX` so the redirect targets the correct state-specific sportsbook domain. Only affects books with state-dependent URLs (BetMGM, Caesars, BetRivers). |
| `limit` | integer | 50 | Max results per page (max 200) |
| `offset` | integer | 0 | Pagination offset. May produce duplicate rows when live data updates between requests β€” use `cursor` for multi-page scans. |
| `cursor` | string | β€” | Opaque cursor from `next_cursor` in a previous response. **Recommended for multi-page scans** β€” stable against live data changes. Takes precedence over `offset` when both are provided. |
| `offset` | integer | 0 | Pagination offset. **Must be ≀ 500.** Values above 500 return `400 offset_too_large` β€” use `cursor` for deeper pagination. May produce duplicate rows when live data updates between requests. |
| `cursor` | string | β€” | Opaque cursor from `next_cursor` in a previous response. **Required for deep pagination (past offset 500)** and recommended for any multi-page scan β€” stable against live data changes. Takes precedence over `offset` when both are provided. |

<Callout type="info">
Use comma-separated values to filter by multiple sportsbooks: `sportsbook=draftkings,fanduel,betmgm`
Expand Down Expand Up @@ -159,7 +159,17 @@ curl "https://api.sharpapi.io/api/v1/odds?league=nfl&limit=200&cursor=eyJlIjoiMz

Cursors are opaque β€” do not parse or construct them. They encode the sort position of the last item on the current page and are only valid for the same filter parameters.

`?offset=N` continues to work and is appropriate for single-page requests or direct position access. It is not recommended for iterating through live data.
`?offset=N` continues to work for shallow pagination (up to offset 500) and is appropriate for single-page requests or direct position access. Beyond 500, the API returns `400 offset_too_large` β€” the server would otherwise have to sort the full filtered result set on every page, which is much cheaper to avoid than to optimize per-request. Use `cursor` for anything deeper.

```json
{
"error": {
"code": "offset_too_large",
"message": "offset must be <= 500; use `cursor=` from the previous response for deeper pagination",
"max_offset": 500
}
}
```

## Response

Expand Down