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: document is_active field on Odds (SHA-3803) (#255)
* docs: document is_active market-suspension field on Odds (SHA-3803)
Add `is_active` to the OpenAPI Odds schema + the /odds field reference
(false = market suspended/closed, price frozen; mirrors OpticOdds locked-odds;
absent treated as true). Bumps openapi info.version 2.1.0 -> 2.2.0 with the
matching CHANGELOG entry required by the openapi-version-check gate.
Type: docs
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* docs: document odds:locked stream event (SHA-3803)
Add the odds:locked SSE + WS event (sharp-api-go #725) to stream.mdx and
websocket.mdx, plus an is_active row in the OddsDelta table. Supplementary
OpticOdds-locked-odds analogue; suspended rows also ride odds:update with
is_active=false. Content/MDX only — no openapi.json schema change, so no
further info.version bump.
Type: docs
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* docs: accurate OddsDelta field set + static-vs-dynamic guidance (SHA-3803)
The OddsDelta table omitted dynamic fields that deltas carry (is_main_line,
is_alternate_line, is_stale_pregame_price, exchange volumes) and didn't spell
out the static-vs-dynamic split. Document the real compact delta shape and the
"merge by id, never read static fields off a delta" contract. Pairs with the
server-side delta compaction (sharp-api-go #726). Content/MDX only.
Type: docs
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>
|`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
330
|`odds_changed_at`| string | ISO 8601 timestamp of the sportsbook's own source update 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. |
331
331
|`is_live`| boolean | Whether the event is currently live |
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`. |
332
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. |
333
334
|`external_event_id`| string\|undefined | The sportsbook's own native event ID, when distinct from `event_id`. Useful for round-tripping rows back to the sportsbook's UI or API. |
334
335
|`deep_link`| string\|undefined | Resolver URL pointing to the sportsbook's event or bet-slip page. Pass `state=` (e.g. `state=nj`) on the request to route through state-specific subdomains for books that need them (BetMGM, Caesars, BetRivers). |
|`opportunities`|`snapshot`, `ev:detected/expired`, `arb:detected/expired`, `middles:detected/expired`, `low_hold:detected/expired`, `heartbeat`| Alert on opportunities |
49
49
|`gamestate`|`gamestate:snapshot`, `gamestate:update`, `gamestate:removed`, `heartbeat`| Live scores, periods, clocks, and situational data per event. **Enterprise tier only.** See [Live Game State](/api-reference/gamestate/) for the full field catalog. |
50
50
|`all`| All event types | Full real-time picture |
|`odds_probability`| number | Updated implied probability (e.g. `0.6`) |
133
133
|`line`| number \| null | Updated line/spread (e.g. `-3.5`), or `null` for moneyline |
134
134
|`is_live`| boolean | Whether the event is currently live |
135
+
|`is_active`| boolean |`true` = market open/bettable; `false` = market suspended/closed with the price frozen. A market suspending (e.g. after a goal) emits an `odds:update` with `is_active: false` — grey out the line rather than trusting the frozen price. See also the [`odds:locked`](#odds-locked) event. |
136
+
|`is_main_line`| boolean |`true` when this line is the consensus main line for its market; `false` for alternate lines. Can flip as the main line moves. |
137
+
|`is_alternate_line`| boolean | Positive-polarity sibling of `is_main_line` (mutually exclusive). |
138
+
|`is_stale_pregame_price`| boolean |`true` when a live row still carries a pre-game price that hasn't moved since kickoff. |
135
139
|`odds_changed_at`| string | ISO 8601 timestamp of the sportsbook's own source update for this line, when available. On Pinnacle, carries forward while the underlying price/line/`is_live` flag are unchanged — see [Understanding Pinnacle's `odds_changed_at`](/en/concepts/pinnacle-odds-changed-at/). |
136
140
141
+
Exchange books additionally carry the dynamic `volume`, `volume_24h`, `open_interest`, and `max_bet` fields when present. Everything else — `sportsbook`, `sport`, `league`, `home_team`, `away_team`, `market_type`, `selection`, `deep_link`, `event_start_time`, and the nested entity refs — is **static** and comes from the initial `snapshot`; merge each delta into your local map by `id` and never read a static field off a delta.
142
+
137
143
**Envelope fields:**
138
144
139
145
| Field | Type | Description |
@@ -223,6 +229,20 @@ id: evt_00050
223
229
data: {"expired":["lowhold_abc123"]}
224
230
```
225
231
232
+
### `odds:locked`
233
+
234
+
Fired when a market is **suspended/closed** (e.g. after a goal, during a line move, or a late-game lockout) — the price is **frozen** but the selection is no longer bettable. Carries the suspended subset of the current delta, same payload shape as `odds:update`, with `is_active: false`. Only sent on `odds` or `all` channels.
235
+
236
+
This is a 1:1 analogue of OpticOdds' `locked-odds` for easy migration. It is **supplementary** — the same rows also arrive in `odds:update` with `is_active: false`, so clients that already read `is_active` need not subscribe to `odds:locked` separately. Use it when you want a dedicated lock signal without parsing every `odds:update`.
A market re-opening emits a normal `odds:update` with `is_active: true` (and a fresh price). Markets a book **removes entirely** come through [`odds:removed`](#odds-removed) instead.
245
+
226
246
### `odds:removed`
227
247
228
248
Odds removed by a sportsbook (e.g. market taken down, event settled). Only sent on `odds` or `all` channels.
Copy file name to clipboardExpand all lines: content/en/api-reference/websocket.mdx
+17Lines changed: 17 additions & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -283,6 +283,23 @@ Incremental odds update from a single sportsbook.
283
283
}
284
284
```
285
285
286
+
#### `odds:locked`
287
+
288
+
A market was **suspended/closed** (e.g. after a goal, a line move, or a late-game lockout) — the price is **frozen** and no longer bettable. Carries the suspended subset of the delta (same payload as `odds:update`, with `is_active: false`). A 1:1 analogue of OpticOdds' `locked-odds`.
289
+
290
+
Supplementary: the same rows also arrive in `odds:update` with `is_active: false`, so clients reading `is_active` need not subscribe separately. A re-open emits a normal `odds:update` with `is_active: true`; a full removal comes through `odds:removed`.
291
+
292
+
```json
293
+
{
294
+
"type": "odds:locked",
295
+
"seq": 48,
296
+
"source": "pinnacle",
297
+
"data": [ /* NormalizedOdds[] with is_active: false */ ],
298
+
"count": 1,
299
+
"timestamp": "2026-02-08T18:47:19.250Z"
300
+
}
301
+
```
302
+
286
303
#### `odds:removed`
287
304
288
305
Odds removed by a sportsbook (e.g. market taken down, event settled).
Copy file name to clipboardExpand all lines: public/openapi.json
+5-1Lines changed: 5 additions & 1 deletion
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.1.0",
5
+
"version": "2.2.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",
@@ -4458,6 +4458,10 @@
4458
4458
"is_live": {
4459
4459
"type": "boolean"
4460
4460
},
4461
+
"is_active": {
4462
+
"type": "boolean",
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)."
0 commit comments