You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
* docs: deprecate last_seen_at + wire_received_at on the odds response; document odds_changed_at
Phase 1 of internalizing the two non-betting response timestamps (sharp-api-go
issue #743). Customer-facing deprecation signal:
- openapi.json (Odds schema): mark `last_seen_at`, `wire_received_at`, and the
stale `timestamp` prop `deprecated: true`; ADD the canonical `odds_changed_at`
prop (it was missing) as the freshness field to read.
- Field tables (odds / odds-batch / odds-best / odds-comparison .mdx): mark the
rows ⚠️ Deprecated and redirect to `odds_changed_at`. Also fixes odds-best's
mislabeled "best odds determination" description for last_seen_at.
Fields still emit during the deprecation window — no contract break yet; removal
is tracked in Mlaz-code/sharp-api-go#743.
Type: docs
🤖 Generated with [Claude Code](https://claude.com/claude-code)
* docs: bump OpenAPI spec to 2.3.0 + changelog (odds_changed_at add + deprecations)
Satisfies the openapi-version-check policy: schema changed (added odds_changed_at,
deprecated last_seen_at/wire_received_at/timestamp) → MINOR bump + CHANGELOG entry.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Codex <codex@sharpapi.local>
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Copy file name to clipboardExpand all lines: CHANGELOG.md
+4Lines changed: 4 additions & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -6,6 +6,10 @@ to bump. Every change to API paths or response schemas gets a one-line entry her
6
6
the [OpenAPI Version Check](.github/workflows/openapi-version-check.yml) CI job
7
7
enforces that a bump has a matching entry.
8
8
9
+
## 2.3.0 — 2026-06-01
10
+
11
+
- Add `odds_changed_at` to the `Odds` schema — the canonical per-row freshness field (previously undocumented; also the only per-odd freshness timestamp OpticOdds exposes). Deprecate `last_seen_at`, `wire_received_at`, and the stale `timestamp` prop (`deprecated: true`) — being internalized; read `odds_changed_at` for freshness. Removal tracked in sharp-api-go #743.
12
+
9
13
## 2.2.0 — 2026-05-31
10
14
11
15
- Add `is_active` field to the `Odds` schema (`false` = market suspended/closed, price frozen; mirrors OpticOdds locked-odds; absent treated as `true`). SHA-3803.
Copy file name to clipboardExpand all lines: content/en/api-reference/odds-batch.mdx
+1-1Lines changed: 1 addition & 1 deletion
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -289,7 +289,7 @@ Each item in the `data.events` array is an event object with nested odds:
289
289
|`odds_american`| number | American odds |
290
290
|`odds_decimal`| number | Decimal odds |
291
291
|`line`| number \| null | Line value |
292
-
|`last_seen_at`| string |ISO 8601 timestamp when our pipeline last observed this row. Use this as your pipeline freshness signal. |
292
+
|`last_seen_at`| string |**⚠️ Deprecated** — being internalized; will be removed in a future release. Liveness heartbeat (advances every cycle even when the price is unchanged), not a price-freshness signal. Read `odds_changed_at` for freshness. |
293
293
|`odds_changed_at`| string | ISO 8601 timestamp of when the price, line, or `is_live` flag last actually changed. Sportsbook-provided when available; on Pinnacle it carries forward across unchanged refreshes — see [Understanding Pinnacle's `odds_changed_at`](/en/concepts/pinnacle-odds-changed-at/). |
Copy file name to clipboardExpand all lines: content/en/api-reference/odds-best.mdx
+1-1Lines changed: 1 addition & 1 deletion
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -215,7 +215,7 @@ X-Request-Id: req_best_789xyz
215
215
|`all_books[].line`| number \| null | Line at this sportsbook |
216
216
|`all_books[].last_seen_at`| string | When our pipeline last observed this book's row — pipeline freshness signal |
217
217
|`all_books[].odds_changed_at`| string | When this book's price, line, or `is_live` flag last actually changed. On Pinnacle, carries forward across unchanged refreshes — see [Understanding Pinnacle's `odds_changed_at`](/en/concepts/pinnacle-odds-changed-at/). |
218
-
|`last_seen_at`| string |ISO 8601 timestamp of the best odds determination|
218
+
|`last_seen_at`| string |**⚠️ Deprecated** — being internalized; will be removed in a future release. Liveness heartbeat for the row (advances every cycle even when the price is unchanged), not a price-freshness signal. Read `odds_changed_at` for freshness.|
219
219
|`player_name`| string\|undefined | Player name (player prop markets only) |
220
220
|`stat_category`| string\|undefined | Stat category, e.g. `points`, `rebounds` (player prop markets only) |
Copy file name to clipboardExpand all lines: content/en/api-reference/odds-comparison.mdx
+1-1Lines changed: 1 addition & 1 deletion
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -253,7 +253,7 @@ Each entry in the `books` object:
253
253
|-------|------|-------------|
254
254
|`odds_american`| number | American odds |
255
255
|`odds_decimal`| number | Decimal odds |
256
-
|`last_seen_at`| string |When our pipeline last observed this book's row — pipeline freshness signal |
256
+
|`last_seen_at`| string |**⚠️ Deprecated** — being internalized; will be removed in a future release. Liveness heartbeat (advances every cycle even when the price is unchanged), not a price-freshness signal. Read `odds_changed_at` for freshness.|
257
257
|`odds_changed_at`| string | When this book's price, line, or `is_live` flag last actually changed. On Pinnacle, carries forward across unchanged refreshes — see [Understanding Pinnacle's `odds_changed_at`](/en/concepts/pinnacle-odds-changed-at/). |
|`is_alternate_line`| boolean |`true` when this row's `line` differs from the canonical main line for its `(event, market_type, selection)` group. `false` for the consensus main line, for no-line markets (moneyline, outright), and for rows whose cohort hasn't been published yet (cold start, brand-new event). See `is_main_line` for the positive-polarity sibling that disambiguates "main" from "cohort-pending". |
326
326
|`is_main_line`| boolean |`true` when this row's `line` equals the canonical main line for its `(event, market_type, selection)` group, AND for no-line markets (moneyline, outright — always main by definition). `false` for alt-line rows and for rows whose cohort hasn't been published yet. **Mutually exclusive with `is_alternate_line`:** both true never coexists; both false is the cohort-pending state. Filter for `is_main_line=true` when you want a positive "main lines only" view that excludes cohort-pending rows. Also accepted as a query filter on [`/odds/best?is_main_line=true`](/en/api-reference/odds-best/). |
327
327
|`event_start_time`| string | ISO 8601 event start time |
328
-
|`last_seen_at`| string |ISO 8601 timestamp of our adapter's last observation of this row. Advances every ingest cycle even when the content is unchanged — use as a heartbeat that the row is still being maintained, not as an ingest-latency benchmark. |
329
-
|`wire_received_at`| string\|undefined |ISO 8601 timestamp of when SharpAPI first observed a content change for this row, carried forward across subsequent unchanged refreshes. **Use this field for ingest-latency benchmarking** — it isolates SharpAPI's pipeline contribution from the sportsbook's source-side publish cadence. Omitted on cold-start rows where no prior observation exists. See [Understanding Pinnacle's `odds_changed_at`](/en/concepts/pinnacle-odds-changed-at/#benchmarking-pipeline-latency). |
330
-
|`odds_changed_at`| string |ISO 8601 timestamp of the sportsbook's own sourceupdate for this line, when available. On Pinnacle, carries forward while the price/line/`is_live`flag are unchanged (see [Understanding Pinnacle's `odds_changed_at`](/en/concepts/pinnacle-odds-changed-at/)). Not suitable for SharpAPI pipeline-latency benchmarking — use `wire_received_at` for that. |
328
+
|`last_seen_at`| string |**⚠️ Deprecated** — being internalized; will be removed in a future release. Liveness heartbeat: advances every ingest cycle even when the price is unchanged, so it is *not* a price-freshness signal. Read `odds_changed_at` for freshness. |
329
+
|`wire_received_at`| string\|undefined |**⚠️ Deprecated** — being internalized; will be removed in a future release. SharpAPI pipeline-arrival stamp for ingest-latency benchmarking only — not a betting signal. Read `odds_changed_at` for price freshness. |
330
+
|`odds_changed_at`| string |**The freshness field to read.** Best-available last-price-change time: the sportsbook's own source-update timestamp when provided, otherwise when SharpAPI first detected a price/line/`is_live`change — carried forward across unchanged refreshes (see [Understanding Pinnacle's `odds_changed_at`](/en/concepts/pinnacle-odds-changed-at/)). |
331
331
|`is_live`| boolean | Whether the event is currently live |
332
332
|`is_active`| boolean |`true` (default) = market open and bettable; `false` = market suspended/closed with the price **frozen** at its last value (so you can grey it out rather than trust a stale price). Mirrors OpticOdds' `locked-odds`, but as a queryable field you can also filter on. Absent is treated as `true`. An active→suspended transition is pushed on the [odds stream](/en/api-reference/stream/) as an `odds:update` carrying `is_active: false`. |
333
333
|`event_uuid`| string\|undefined | Stable canonical event UUID from the SharpAPI atlas, when the event is mapped. Where `event_id` carries the adapter's primary event identifier (often the originating sportsbook's), `event_uuid` is a feed-stable hash you can use for cross-feed joins. Absent for unmapped events. |
Copy file name to clipboardExpand all lines: public/openapi.json
+16-3Lines changed: 16 additions & 3 deletions
Original file line number
Diff line number
Diff line change
@@ -2,7 +2,7 @@
2
2
"openapi": "3.1.0",
3
3
"info": {
4
4
"title": "SharpAPI",
5
-
"version": "2.2.0",
5
+
"version": "2.3.0",
6
6
"description": "Real-time sports betting odds API with +EV detection, arbitrage, middles, and low-hold opportunities.\n\n## Spec Versioning\n\n`info.version` is bumped on every schema or path change. Minor version (`2.x.0`) for additive changes or breaking shape fixes that align the spec to the live response; major version (`x.0.0`) for backward-incompatible redesigns. Removed paths and renamed fields always bump the minor at minimum. Check `x-generated-at` and `x-commit-sha` for the build provenance of a given snapshot.\n\n## Authentication\n\nAll authenticated endpoints accept an API key via one of three methods:\n\n| Method | Header / Param | Use case |\n|--------|---------------|----------|\n| `X-API-Key` | `X-API-Key: sk_live_...` | Recommended for server-side |\n| `Authorization` | `Authorization: Bearer sk_live_...` | Standard Bearer token |\n| `api_key` query | `?api_key=sk_live_...` | SSE/EventSource (cannot set headers) |\n\n## Subscription Tiers\n\n| Tier | Rate Limit | Data Delay | Max Books | EV | Arb | Middles | Game State |\n|------|-----------|------------|-----------|-----|-----|---------|------------|\n| Free | 12/min | 60s | 2 (DK, FD) | - | - | - | - |\n| Hobby | 120/min | Real-time | 5 | - | Yes | - | - |\n| Pro | 300/min | Real-time | 15 | Yes | Yes | Yes | - |\n| Sharp | 1000/min | Real-time | All | Yes | Yes | Yes | - |\n| Enterprise | Custom | Real-time | All | Yes | Yes | Yes | Yes |\n\n## Rate Limit Headers\n\nEvery authenticated response includes:\n\n- `X-RateLimit-Limit` - Requests allowed per minute\n- `X-RateLimit-Remaining` - Requests remaining in current window\n- `X-RateLimit-Reset` - Unix timestamp when the window resets\n- `X-Data-Delay` - Odds delay in seconds for your tier (0 = real-time)\n- `X-Request-Id` - Unique request identifier for support\n\n## WebSocket Streaming\n\nThe WebSocket endpoint at `wss://ws.sharpapi.io` is documented separately in [`asyncapi.yaml`](./asyncapi.yaml) (AsyncAPI 3.0). OpenAPI 3.x cannot express WebSocket subprotocols and message channels, so the SSE endpoint (`/stream`) is the only stream covered by this document.\n\n## MCP Server\n\nThe `POST /mcp` endpoint is a Model Context Protocol server (JSON-RPC 2.0 over Streamable HTTP). Tools are self-described at runtime via `tools/list`, so it's documented as a setup guide rather than an OpenAPI path — see [`/sdks/mcp`](https://docs.sharpapi.io/sdks/mcp).\n",
7
7
"contact": {
8
8
"name": "SharpAPI Support",
@@ -4462,15 +4462,28 @@
4462
4462
"type": "boolean",
4463
4463
"description": "true (default) = market open and bettable; false = market suspended/closed with the price frozen. Mirrors OpticOdds locked-odds but exposed as a queryable field. Absent is treated as true. An active->suspended transition is emitted on the odds stream (odds:update with is_active=false)."
4464
4464
},
4465
+
"odds_changed_at": {
4466
+
"type": "string",
4467
+
"format": "date-time",
4468
+
"description": "Best-available last-price-change time for this line: the sportsbook's own source-update timestamp when provided, otherwise when SharpAPI first detected a price/line/is_live change — carried forward across unchanged refreshes. This is the freshness field to read."
4469
+
},
4465
4470
"timestamp": {
4466
4471
"type": "string",
4467
4472
"format": "date-time",
4468
-
"description": "When these odds were last updated"
4473
+
"deprecated": true,
4474
+
"description": "DEPRECATED — not emitted on the odds response; read `odds_changed_at` for price freshness."
4475
+
},
4476
+
"last_seen_at": {
4477
+
"type": "string",
4478
+
"format": "date-time",
4479
+
"deprecated": true,
4480
+
"description": "DEPRECATED (being internalized, will be removed in a future release): liveness heartbeat — advances every ingest cycle even when the price is unchanged, so it is NOT a price-freshness signal. Read `odds_changed_at` instead."
4469
4481
},
4470
4482
"wire_received_at": {
4471
4483
"type": "string",
4472
4484
"format": "date-time",
4473
-
"description": "Pipeline-arrival stamp — when sharp-api-go last observed a content-hash change for this row. Distinct from `last_seen_at` (adapter observation) and `odds_changed_at` (sportsbook's own source update)."
4485
+
"deprecated": true,
4486
+
"description": "DEPRECATED (being internalized, will be removed in a future release): SharpAPI pipeline-arrival stamp for ingest-latency benchmarking only — not a betting/price-freshness signal. Read `odds_changed_at` for price freshness."
0 commit comments