From 24f522a7934d40537ce1fe7cf55249412fb2b58f Mon Sep 17 00:00:00 2001 From: Mlaz-code <68407656+Mlaz-code@users.noreply.github.com> Date: Fri, 24 Apr 2026 11:11:51 -0400 Subject: [PATCH] docs(odds): document offset cap, removed_truncated, since_clamped MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Companion to sharp-api-go PR #44. Documents the three new safety bounds on /odds and /odds/delta: - offset > 500 now returns 400 offset_too_large on both endpoints. /odds clients are directed to cursor= for deeper pagination; /odds/delta clients are directed to advance `since` to the previous response's `updated_at`. - /odds/delta responses may include removed_truncated: true when the `removed` array hits the 1000-entry cap, and since_clamped: true when the client's `since` was older than the 10-minute removal retention window. Both flags are optional — healthy polling clients (rolling `since` forward each call) never see them, which is called out in a new Polling note on the delta reference page. Co-Authored-By: Claude Opus 4.7 (1M context) --- content/en/api-reference/odds-delta.mdx | 28 +++++++++++++++++++++++-- content/en/api-reference/odds.mdx | 16 +++++++++++--- 2 files changed, 39 insertions(+), 5 deletions(-) diff --git a/content/en/api-reference/odds-delta.mdx b/content/en/api-reference/odds-delta.mdx index f965a62..8f2edbf 100644 --- a/content/en/api-reference/odds-delta.mdx +++ b/content/en/api-reference/odds-delta.mdx @@ -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. | The `since` parameter is **required**. Omitting it returns a `400 validation_error` error. + +**`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. + + ## Example Requests @@ -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 { @@ -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: diff --git a/content/en/api-reference/odds.mdx b/content/en/api-reference/odds.mdx index 1b24ae5..94fb981 100644 --- a/content/en/api-reference/odds.mdx +++ b/content/en/api-reference/odds.mdx @@ -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. | Use comma-separated values to filter by multiple sportsbooks: `sportsbook=draftkings,fanduel,betmgm` @@ -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