From 4f723de8c2cdbff05801a220a391df287441a054 Mon Sep 17 00:00:00 2001 From: Samuel Tinnerholm Date: Tue, 2 Jun 2026 11:38:41 +0000 Subject: [PATCH 1/4] docs: clarify historical fetch order book usage --- docs/api-reference/fetch-order-book.mdx | 95 +++++++++++++++++-------- 1 file changed, 66 insertions(+), 29 deletions(-) diff --git a/docs/api-reference/fetch-order-book.mdx b/docs/api-reference/fetch-order-book.mdx index 1aea6e43..55b0cb4d 100644 --- a/docs/api-reference/fetch-order-book.mdx +++ b/docs/api-reference/fetch-order-book.mdx @@ -7,7 +7,11 @@ openapi: GET /api/{exchange}/fetchOrderBook ### Live order book -Fetch the current L2 order book for an outcome. If you already have an outcome token ID, pass it directly: +Fetch the current L2 order book for an outcome from the exchange's live order book endpoint. For Polymarket this is a live CLOB call: pass the outcome token ID directly and omit historical params. + + +`poly.fetch_order_book(token_id)` without `params.since` or `params.until` is live-only. It does not read PMXT Archive data and may fail for closed, resolved, or otherwise inactive Polymarket markets with an error such as `No orderbook exists`. + ```python Python @@ -28,15 +32,15 @@ console.log(`${book.bids.length} bids, ${book.asks.length} asks`); ```bash curl curl "https://api.pmxt.dev/api/polymarket/fetchOrderBook?outcomeId=104932610032177696635191871147557737718087870958469629338467406422339967452218" \ - -H "Authorization: Bearer $PMXT_API_KEY" + -H "Authorization: Bearer ***" ``` ### Historical snapshot -Get the order book at a specific point in time. Pass `since` as a Unix timestamp in milliseconds — returns the nearest snapshot at or before that time. +Get the order book at a specific point in time by passing `since` as a Unix timestamp in milliseconds. Historical Polymarket queries are served from the PMXT Archive and return the nearest reconstructed snapshot at or before that time. -For binary markets, you can pass the market ID and choose the side with `params.outcome`. Use `"yes"` or `"no"` instead of copying the long outcome token ID: +For binary markets, you can pass the Polymarket condition ID as the first argument and choose the side with `params.outcome`. Use `"yes"` or `"no"` instead of copying the long outcome token ID. ```python Python @@ -44,12 +48,9 @@ import pmxt poly = pmxt.Polymarket(pmxt_api_key="pmxt_...") -market = poly.fetch_market( - slug="will-spacex-starship-flight-test-12-launch-by-may-22-354-721" -) - +# Historical lookup against PMXT Archive, not the live CLOB. book = poly.fetch_order_book( - market.market_id, + "0xc704f74e2f9dfae70f770cb253ffadde10768eeab41233098bf5ac67995a94b5", params={"since": 1779487200000, "outcome": "yes"}, ) @@ -63,12 +64,9 @@ import { Polymarket } from "pmxtjs"; const poly = new Polymarket({ pmxtApiKey: "pmxt_..." }); -const market = await poly.fetchMarket({ - slug: "will-spacex-starship-flight-test-12-launch-by-may-22-354-721", -}); - +// Historical lookup against PMXT Archive, not the live CLOB. const book = await poly.fetchOrderBook( - market.marketId, + "0xc704f74e2f9dfae70f770cb253ffadde10768eeab41233098bf5ac67995a94b5", undefined, { since: 1779487200000, outcome: "yes" } ); @@ -79,9 +77,51 @@ console.log(` best ask: ${Math.min(...book.asks.map((a) => a.price)).toFixed(3) ```bash curl curl -X POST "https://api.pmxt.dev/api/polymarket/fetchOrderBook" \ - -H "Authorization: Bearer $PMXT_API_KEY" \ + -H "Authorization: Bearer ***" \ + -H "Content-Type: application/json" \ + -d '{"args":["0xc704f74e2f9dfae70f770cb253ffadde10768eeab41233098bf5ac67995a94b5", null, {"since": 1779487200000, "outcome": "yes"}]}' +``` + + +### Historical crypto 5m events + +Polymarket crypto 5-minute slugs such as `btc-updown-5m-1779481500` are event slugs. Use `fetch_event(slug=...)`, then select the nested market you want. Do not use `fetch_market(slug=...)` for these event-level slugs. + + +```python Python +import pmxt + +poly = pmxt.Polymarket(pmxt_api_key="pmxt_...") +event = poly.fetch_event(slug="btc-updown-5m-1779481500") +market = event.markets[0] + +book = poly.fetch_order_book( + market.market_id, + params={"since": 1779487200000, "outcome": "yes"}, +) +print(f"{market.title}: {book.dt}") +``` + +```javascript JavaScript +import { Polymarket } from "pmxtjs"; + +const poly = new Polymarket({ pmxtApiKey: "pmxt_..." }); +const event = await poly.fetchEvent({ slug: "btc-updown-5m-1779481500" }); +const market = event.markets[0]; + +const book = await poly.fetchOrderBook( + market.marketId, + undefined, + { since: 1779487200000, outcome: "yes" } +); +console.log(`${market.title}: ${book.datetime}`); +``` + +```bash curl +curl -X POST "https://api.pmxt.dev/api/polymarket/fetchEvent" \ + -H "Authorization: Bearer ***" \ -H "Content-Type: application/json" \ - -d '{"args":["61b0ed20-7f42-41fd-af15-7b86153f6bb7", null, {"since": 1779487200000, "outcome": "yes"}]}' + -d '{"kwargs":{"slug":"btc-updown-5m-1779481500"}}' ``` @@ -89,17 +129,15 @@ curl -X POST "https://api.pmxt.dev/api/polymarket/fetchOrderBook" \ Pass both `since` and `until` to get an array of fully reconstructed L2 order book snapshots. Each snapshot is a complete book at that moment in time — not deltas. -Default 100 snapshots per request, max 1000. +The API returns up to `limit` snapshots from the PMXT Archive. Defaults to 100 snapshots per request, max 1000. For raw bulk downloads, use the Parquet files in the [PMXT Archive](https://archive.pmxt.dev) instead of trying to stream bulk data through the live order book endpoint. ```python Python import pmxt poly = pmxt.Polymarket(pmxt_api_key="pmxt_...") - -market = poly.fetch_market( - slug="will-spacex-starship-flight-test-12-launch-by-may-22-354-721" -) +event = poly.fetch_event(slug="btc-updown-5m-1779481500") +market = event.markets[0] books = poly.fetch_order_book( market.market_id, @@ -107,6 +145,7 @@ books = poly.fetch_order_book( "since": 1779480000000, "until": 1779487200000, "outcome": "yes", + "limit": 100, } ) print(f"{len(books)} snapshots") @@ -118,15 +157,13 @@ for ob in books[:3]: import { Polymarket } from "pmxtjs"; const poly = new Polymarket({ pmxtApiKey: "pmxt_..." }); - -const market = await poly.fetchMarket({ - slug: "will-spacex-starship-flight-test-12-launch-by-may-22-354-721", -}); +const event = await poly.fetchEvent({ slug: "btc-updown-5m-1779481500" }); +const market = event.markets[0]; const books = await poly.fetchOrderBook( market.marketId, undefined, - { since: 1779480000000, until: 1779487200000, outcome: "yes" } + { since: 1779480000000, until: 1779487200000, outcome: "yes", limit: 100 } ); console.log(`${books.length} snapshots`); books.slice(0, 3).forEach((ob) => @@ -136,12 +173,12 @@ books.slice(0, 3).forEach((ob) => ```bash curl curl -X POST "https://api.pmxt.dev/api/polymarket/fetchOrderBook" \ - -H "Authorization: Bearer $PMXT_API_KEY" \ + -H "Authorization: Bearer ***" \ -H "Content-Type: application/json" \ - -d '{"args":["61b0ed20-7f42-41fd-af15-7b86153f6bb7", null, {"since": 1779480000000, "until": 1779487200000, "outcome": "yes"}]}' + -d '{"args":["0xc704f74e2f9dfae70f770cb253ffadde10768eeab41233098bf5ac67995a94b5", null, {"since": 1779480000000, "until": 1779487200000, "outcome": "yes", "limit": 100}]}' ``` -Historical order book data is backed by the [PMXT Archive](https://archive.pmxt.dev) — the same tick-level data available as Parquet files, but queryable directly from the API without downloading or parsing files. Supports Polymarket, Kalshi, Limitless, and Opinion. +Historical order book data is backed by the [PMXT Archive](https://archive.pmxt.dev) — the same tick-level data available as Parquet files, but queryable directly from the API without downloading or parsing files. Historical `fetchOrderBook` supports Polymarket, Kalshi, Limitless, and Opinion. From a7f618b2ae80760f649ca0bbd60ff6475f95be82 Mon Sep 17 00:00:00 2001 From: Samuel Tinnerholm Date: Tue, 2 Jun 2026 11:41:32 +0000 Subject: [PATCH 2/4] docs: sync generated API docs --- core/src/server/openapi.yaml | 296 +++-------------------------------- docs/llms-full.txt | 96 ++++++++---- 2 files changed, 85 insertions(+), 307 deletions(-) diff --git a/core/src/server/openapi.yaml b/core/src/server/openapi.yaml index 6c1789f8..c91cfce5 100644 --- a/core/src/server/openapi.yaml +++ b/core/src/server/openapi.yaml @@ -72,26 +72,7 @@ paths: summary: Fetch Markets operationId: fetchMarkets parameters: - - in: path - name: exchange - schema: - type: string - enum: - - polymarket - - kalshi - - kalshi-demo - - limitless - - probable - - baozi - - myriad - - opinion - - metaculus - - smarkets - - polymarket_us - - suibets - - router - required: true - description: The prediction market exchange to target. + - $ref: '#/components/parameters/ExchangeParam' - in: query name: limit required: false @@ -277,26 +258,7 @@ paths: summary: Fetch Events operationId: fetchEvents parameters: - - in: path - name: exchange - schema: - type: string - enum: - - polymarket - - kalshi - - kalshi-demo - - limitless - - probable - - baozi - - myriad - - opinion - - metaculus - - smarkets - - polymarket_us - - suibets - - router - required: true - description: The prediction market exchange to target. + - $ref: '#/components/parameters/ExchangeParam' - in: query name: query required: false @@ -421,19 +383,7 @@ paths: summary: Fetch Series operationId: fetchSeries parameters: - - in: path - name: exchange - schema: - type: string - enum: - - polymarket - - kalshi - - kalshi-demo - - opinion - - polymarket_us - - router - required: true - description: The prediction market exchange to target. + - $ref: '#/components/parameters/ExchangeParam' responses: '200': description: Fetch Series response @@ -687,21 +637,7 @@ paths: summary: Fetch OHLCV operationId: fetchOHLCV parameters: - - in: path - name: exchange - schema: - type: string - enum: - - polymarket - - kalshi - - kalshi-demo - - limitless - - probable - - baozi - - myriad - - opinion - required: true - description: The prediction market exchange to target. + - $ref: '#/components/parameters/ExchangeParam' - in: query name: outcomeId required: true @@ -753,25 +689,7 @@ paths: summary: Fetch Order Book operationId: fetchOrderBook parameters: - - in: path - name: exchange - schema: - type: string - enum: - - polymarket - - kalshi - - kalshi-demo - - limitless - - probable - - baozi - - myriad - - opinion - - smarkets - - polymarket_us - - suibets - - router - required: true - description: The prediction market exchange to target. + - $ref: '#/components/parameters/ExchangeParam' - in: query name: outcomeId required: true @@ -839,16 +757,7 @@ paths: summary: Fetch Order Books operationId: fetchOrderBooks parameters: - - in: path - name: exchange - schema: - type: string - enum: - - polymarket - - kalshi - - kalshi-demo - required: true - description: The prediction market exchange to target. + - $ref: '#/components/parameters/ExchangeParam' requestBody: content: application/json: @@ -890,21 +799,7 @@ paths: summary: Fetch Trades operationId: fetchTrades parameters: - - in: path - name: exchange - schema: - type: string - enum: - - polymarket - - kalshi - - kalshi-demo - - limitless - - probable - - baozi - - myriad - - smarkets - required: true - description: The prediction market exchange to target. + - $ref: '#/components/parameters/ExchangeParam' - in: query name: outcomeId required: true @@ -950,24 +845,7 @@ paths: summary: Create Order operationId: createOrder parameters: - - in: path - name: exchange - schema: - type: string - enum: - - polymarket - - kalshi - - kalshi-demo - - limitless - - probable - - baozi - - myriad - - opinion - - metaculus - - smarkets - - polymarket_us - required: true - description: The prediction market exchange to target. + - $ref: '#/components/parameters/ExchangeParam' requestBody: content: application/json: @@ -1003,19 +881,7 @@ paths: summary: Build Order operationId: buildOrder parameters: - - in: path - name: exchange - schema: - type: string - enum: - - polymarket - - kalshi - - kalshi-demo - - opinion - - smarkets - - polymarket_us - required: true - description: The prediction market exchange to target. + - $ref: '#/components/parameters/ExchangeParam' requestBody: content: application/json: @@ -1053,19 +919,7 @@ paths: summary: Submit Order operationId: submitOrder parameters: - - in: path - name: exchange - schema: - type: string - enum: - - polymarket - - kalshi - - kalshi-demo - - opinion - - smarkets - - polymarket_us - required: true - description: The prediction market exchange to target. + - $ref: '#/components/parameters/ExchangeParam' requestBody: content: application/json: @@ -1101,22 +955,7 @@ paths: summary: Cancel Order operationId: cancelOrder parameters: - - in: path - name: exchange - schema: - type: string - enum: - - polymarket - - kalshi - - kalshi-demo - - limitless - - probable - - opinion - - metaculus - - smarkets - - polymarket_us - required: true - description: The prediction market exchange to target. + - $ref: '#/components/parameters/ExchangeParam' requestBody: content: application/json: @@ -1152,21 +991,7 @@ paths: summary: Fetch Order operationId: fetchOrder parameters: - - in: path - name: exchange - schema: - type: string - enum: - - polymarket - - kalshi - - kalshi-demo - - probable - - baozi - - opinion - - smarkets - - polymarket_us - required: true - description: The prediction market exchange to target. + - $ref: '#/components/parameters/ExchangeParam' - in: query name: orderId required: true @@ -1190,23 +1015,7 @@ paths: summary: Fetch Open Orders operationId: fetchOpenOrders parameters: - - in: path - name: exchange - schema: - type: string - enum: - - polymarket - - kalshi - - kalshi-demo - - limitless - - probable - - baozi - - myriad - - opinion - - smarkets - - polymarket_us - required: true - description: The prediction market exchange to target. + - $ref: '#/components/parameters/ExchangeParam' - in: query name: marketId required: false @@ -1232,22 +1041,7 @@ paths: summary: Fetch My Trades operationId: fetchMyTrades parameters: - - in: path - name: exchange - schema: - type: string - enum: - - polymarket - - kalshi - - kalshi-demo - - limitless - - probable - - myriad - - opinion - - smarkets - - polymarket_us - required: true - description: The prediction market exchange to target. + - $ref: '#/components/parameters/ExchangeParam' - in: query name: outcomeId required: false @@ -1305,18 +1099,7 @@ paths: summary: Fetch Closed Orders operationId: fetchClosedOrders parameters: - - in: path - name: exchange - schema: - type: string - enum: - - kalshi - - kalshi-demo - - limitless - - opinion - - smarkets - required: true - description: The prediction market exchange to target. + - $ref: '#/components/parameters/ExchangeParam' - in: query name: marketId required: false @@ -1368,18 +1151,7 @@ paths: summary: Fetch All Orders operationId: fetchAllOrders parameters: - - in: path - name: exchange - schema: - type: string - enum: - - kalshi - - kalshi-demo - - limitless - - opinion - - smarkets - required: true - description: The prediction market exchange to target. + - $ref: '#/components/parameters/ExchangeParam' - in: query name: marketId required: false @@ -1431,24 +1203,7 @@ paths: summary: Fetch Positions operationId: fetchPositions parameters: - - in: path - name: exchange - schema: - type: string - enum: - - polymarket - - kalshi - - kalshi-demo - - limitless - - probable - - baozi - - myriad - - opinion - - smarkets - - polymarket_us - - suibets - required: true - description: The prediction market exchange to target. + - $ref: '#/components/parameters/ExchangeParam' - in: query name: address required: false @@ -1474,22 +1229,7 @@ paths: summary: Fetch Balance operationId: fetchBalance parameters: - - in: path - name: exchange - schema: - type: string - enum: - - polymarket - - kalshi - - kalshi-demo - - limitless - - probable - - baozi - - myriad - - smarkets - - polymarket_us - required: true - description: The prediction market exchange to target. + - $ref: '#/components/parameters/ExchangeParam' - in: query name: address required: false diff --git a/docs/llms-full.txt b/docs/llms-full.txt index 55c492dc..bbf82c1f 100644 --- a/docs/llms-full.txt +++ b/docs/llms-full.txt @@ -3274,7 +3274,12 @@ Source: https://pmxt.dev/docs/api-reference/fetch-order-book ##### Live order book -Fetch the current L2 order book for an outcome. If you already have an outcome token ID, pass it directly: +Fetch the current L2 order book for an outcome from the exchange's live order book endpoint. For Polymarket this is a live CLOB call: pass the outcome token ID directly and omit historical params. + + +> **Warning:** +`poly.fetch_order_book(token_id)` without `params.since` or `params.until` is live-only. It does not read PMXT Archive data and may fail for closed, resolved, or otherwise inactive Polymarket markets with an error such as `No orderbook exists`. + ```python Python @@ -3295,15 +3300,15 @@ console.log(`${book.bids.length} bids, ${book.asks.length} asks`); ```bash curl curl "https://api.pmxt.dev/api/polymarket/fetchOrderBook?outcomeId=104932610032177696635191871147557737718087870958469629338467406422339967452218" \ - -H "Authorization: Bearer $PMXT_API_KEY" + -H "Authorization: Bearer ***" ``` ##### Historical snapshot -Get the order book at a specific point in time. Pass `since` as a Unix timestamp in milliseconds — returns the nearest snapshot at or before that time. +Get the order book at a specific point in time by passing `since` as a Unix timestamp in milliseconds. Historical Polymarket queries are served from the PMXT Archive and return the nearest reconstructed snapshot at or before that time. -For binary markets, you can pass the market ID and choose the side with `params.outcome`. Use `"yes"` or `"no"` instead of copying the long outcome token ID: +For binary markets, you can pass the Polymarket condition ID as the first argument and choose the side with `params.outcome`. Use `"yes"` or `"no"` instead of copying the long outcome token ID. ```python Python @@ -3311,12 +3316,9 @@ import pmxt poly = pmxt.Polymarket(pmxt_api_key="pmxt_...") -market = poly.fetch_market( -slug="will-spacex-starship-flight-test-12-launch-by-may-22-354-721" -) - +# Historical lookup against PMXT Archive, not the live CLOB. book = poly.fetch_order_book( -market.market_id, +"0xc704f74e2f9dfae70f770cb253ffadde10768eeab41233098bf5ac67995a94b5", params={"since": 1779487200000, "outcome": "yes"}, ) @@ -3330,12 +3332,9 @@ import { Polymarket } from "pmxtjs"; const poly = new Polymarket({ pmxtApiKey: "pmxt_..." }); -const market = await poly.fetchMarket({ - slug: "will-spacex-starship-flight-test-12-launch-by-may-22-354-721", -}); - +// Historical lookup against PMXT Archive, not the live CLOB. const book = await poly.fetchOrderBook( - market.marketId, + "0xc704f74e2f9dfae70f770cb253ffadde10768eeab41233098bf5ac67995a94b5", undefined, { since: 1779487200000, outcome: "yes" } ); @@ -3346,9 +3345,51 @@ console.log(` best ask: ${Math.min(...book.asks.map((a) => a.price)).toFixed(3) ```bash curl curl -X POST "https://api.pmxt.dev/api/polymarket/fetchOrderBook" \ - -H "Authorization: Bearer $PMXT_API_KEY" \ + -H "Authorization: Bearer ***" \ + -H "Content-Type: application/json" \ + -d '{"args":["0xc704f74e2f9dfae70f770cb253ffadde10768eeab41233098bf5ac67995a94b5", null, {"since": 1779487200000, "outcome": "yes"}]}' +``` + + +##### Historical crypto 5m events + +Polymarket crypto 5-minute slugs such as `btc-updown-5m-1779481500` are event slugs. Use `fetch_event(slug=...)`, then select the nested market you want. Do not use `fetch_market(slug=...)` for these event-level slugs. + + +```python Python +import pmxt + +poly = pmxt.Polymarket(pmxt_api_key="pmxt_...") +event = poly.fetch_event(slug="btc-updown-5m-1779481500") +market = event.markets[0] + +book = poly.fetch_order_book( +market.market_id, +params={"since": 1779487200000, "outcome": "yes"}, +) +print(f"{market.title}: {book.dt}") +``` + +```javascript JavaScript +import { Polymarket } from "pmxtjs"; + +const poly = new Polymarket({ pmxtApiKey: "pmxt_..." }); +const event = await poly.fetchEvent({ slug: "btc-updown-5m-1779481500" }); +const market = event.markets[0]; + +const book = await poly.fetchOrderBook( + market.marketId, + undefined, + { since: 1779487200000, outcome: "yes" } +); +console.log(`${market.title}: ${book.datetime}`); +``` + +```bash curl +curl -X POST "https://api.pmxt.dev/api/polymarket/fetchEvent" \ + -H "Authorization: Bearer ***" \ -H "Content-Type: application/json" \ - -d '{"args":["61b0ed20-7f42-41fd-af15-7b86153f6bb7", null, {"since": 1779487200000, "outcome": "yes"}]}' + -d '{"kwargs":{"slug":"btc-updown-5m-1779481500"}}' ``` @@ -3356,17 +3397,15 @@ curl -X POST "https://api.pmxt.dev/api/polymarket/fetchOrderBook" \ Pass both `since` and `until` to get an array of fully reconstructed L2 order book snapshots. Each snapshot is a complete book at that moment in time — not deltas. -Default 100 snapshots per request, max 1000. +The API returns up to `limit` snapshots from the PMXT Archive. Defaults to 100 snapshots per request, max 1000. For raw bulk downloads, use the Parquet files in the [PMXT Archive](https://archive.pmxt.dev) instead of trying to stream bulk data through the live order book endpoint. ```python Python import pmxt poly = pmxt.Polymarket(pmxt_api_key="pmxt_...") - -market = poly.fetch_market( -slug="will-spacex-starship-flight-test-12-launch-by-may-22-354-721" -) +event = poly.fetch_event(slug="btc-updown-5m-1779481500") +market = event.markets[0] books = poly.fetch_order_book( market.market_id, @@ -3374,6 +3413,7 @@ params={ "since": 1779480000000, "until": 1779487200000, "outcome": "yes", + "limit": 100, } ) print(f"{len(books)} snapshots") @@ -3385,15 +3425,13 @@ print(f" {ob.dt} bid={ob.bids[0].price} ask={ob.asks[0].price}") import { Polymarket } from "pmxtjs"; const poly = new Polymarket({ pmxtApiKey: "pmxt_..." }); - -const market = await poly.fetchMarket({ - slug: "will-spacex-starship-flight-test-12-launch-by-may-22-354-721", -}); +const event = await poly.fetchEvent({ slug: "btc-updown-5m-1779481500" }); +const market = event.markets[0]; const books = await poly.fetchOrderBook( market.marketId, undefined, - { since: 1779480000000, until: 1779487200000, outcome: "yes" } + { since: 1779480000000, until: 1779487200000, outcome: "yes", limit: 100 } ); console.log(`${books.length} snapshots`); books.slice(0, 3).forEach((ob) => @@ -3403,15 +3441,15 @@ books.slice(0, 3).forEach((ob) => ```bash curl curl -X POST "https://api.pmxt.dev/api/polymarket/fetchOrderBook" \ - -H "Authorization: Bearer $PMXT_API_KEY" \ + -H "Authorization: Bearer ***" \ -H "Content-Type: application/json" \ - -d '{"args":["61b0ed20-7f42-41fd-af15-7b86153f6bb7", null, {"since": 1779480000000, "until": 1779487200000, "outcome": "yes"}]}' + -d '{"args":["0xc704f74e2f9dfae70f770cb253ffadde10768eeab41233098bf5ac67995a94b5", null, {"since": 1779480000000, "until": 1779487200000, "outcome": "yes", "limit": 100}]}' ``` > **Note:** -Historical order book data is backed by the [PMXT Archive](https://archive.pmxt.dev) — the same tick-level data available as Parquet files, but queryable directly from the API without downloading or parsing files. Supports Polymarket, Kalshi, Limitless, and Opinion. +Historical order book data is backed by the [PMXT Archive](https://archive.pmxt.dev) — the same tick-level data available as Parquet files, but queryable directly from the API without downloading or parsing files. Historical `fetchOrderBook` supports Polymarket, Kalshi, Limitless, and Opinion. ### Fetch Order Books From ae7bfe3f0251e4c9659af7d827931beb7e5c2d9e Mon Sep 17 00:00:00 2001 From: Samuel Tinnerholm Date: Fri, 5 Jun 2026 14:07:52 +0000 Subject: [PATCH 3/4] fix: surface websocket and feed hygiene issues --- core/src/exchanges/gemini-titan/websocket.ts | 22 +++++++--- core/src/exchanges/kalshi/websocket.ts | 30 +++++++++----- core/src/exchanges/opinion/websocket.ts | 40 +++++++++++-------- core/src/exchanges/polymarket_us/websocket.ts | 5 ++- core/src/feeds/binance/binance-feed.ts | 9 +++-- core/src/feeds/chainlink/chainlink-feed.ts | 7 +++- 6 files changed, 75 insertions(+), 38 deletions(-) diff --git a/core/src/exchanges/gemini-titan/websocket.ts b/core/src/exchanges/gemini-titan/websocket.ts index 227dab54..e7babb30 100644 --- a/core/src/exchanges/gemini-titan/websocket.ts +++ b/core/src/exchanges/gemini-titan/websocket.ts @@ -91,8 +91,10 @@ export class GeminiWebSocket { try { const message = JSON.parse(data.toString()); this.handleMessage(message); - } catch { - // Ignore unparseable messages + } catch (error) { + logger.warn('[gemini-titan] failed to parse or handle message', { + error: error instanceof Error ? error.message : String(error), + }); } }); @@ -212,7 +214,7 @@ export class GeminiWebSocket { size: number, sortOrder: 'asc' | 'desc', ): void { - const idx = levels.findIndex(l => Math.abs(l.price - price) < 0.0001); + const idx = levels.findIndex(l => l.price === price); if (size === 0) { if (idx !== -1) levels.splice(idx, 1); @@ -267,7 +269,12 @@ export class GeminiWebSocket { if (!this.isConnected) { this.connect().catch((err: unknown) => { - logger.warn(`[gemini-titan] connect failed during watchOrderBook('${symbol}'): ${err instanceof Error ? err.message : String(err)}`); + logger.warn(`[gemini-titan] connect failed during watchOrderBook('${symbol}')`, { + error: err instanceof Error ? err.message : String(err), + }); + if (!this.isTerminated) { + this.scheduleReconnect(); + } }); } else { this.sendSubscribe([`${symbol}@depth20`]); @@ -301,7 +308,12 @@ export class GeminiWebSocket { if (!this.isConnected) { this.connect().catch((err: unknown) => { - logger.warn(`[gemini-titan] connect failed during watchTrades('${symbol}'): ${err instanceof Error ? err.message : String(err)}`); + logger.warn(`[gemini-titan] connect failed during watchTrades('${symbol}')`, { + error: err instanceof Error ? err.message : String(err), + }); + if (!this.isTerminated) { + this.scheduleReconnect(); + } }); } else { this.sendSubscribe([`${symbol}@trade`]); diff --git a/core/src/exchanges/kalshi/websocket.ts b/core/src/exchanges/kalshi/websocket.ts index 9b4d5189..da7f8cfe 100644 --- a/core/src/exchanges/kalshi/websocket.ts +++ b/core/src/exchanges/kalshi/websocket.ts @@ -480,13 +480,15 @@ export class KalshiWebSocket { // The resolver will be fulfilled once the connection is (re)established // and data arrives. if (!this.isConnected) { - this.connect().catch(() => { - // Connection failed — scheduleReconnect is already queued via the - // close handler, so we intentionally swallow here. The pending - // resolver will be resolved when data arrives on the next connection. + this.connect().catch((err) => { + logger.warn("Kalshi WebSocket connect failed during subscribeToOrderbook", { + error: String(err), + }); + if (!this.isTerminated) { + this.scheduleReconnect(); + } }); } else { - // Already connected — ensure subscription message is sent this.subscribeToOrderbook([ticker]); } @@ -519,8 +521,13 @@ export class KalshiWebSocket { // Attempt connection — if it fails, scheduleReconnect handles recovery. if (!this.isConnected) { - this.connect().catch(() => { - // Swallow — scheduleReconnect will retry. Resolvers stay pending. + this.connect().catch((err) => { + logger.warn("Kalshi WebSocket connect failed during subscribeToOrderbooks", { + error: String(err), + }); + if (!this.isTerminated) { + this.scheduleReconnect(); + } }); } else if (newTickers.length > 0) { this.subscribeToOrderbook(newTickers); @@ -565,8 +572,13 @@ export class KalshiWebSocket { } if (!this.isConnected) { - this.connect().catch(() => { - // Swallow — scheduleReconnect will retry. Resolvers stay pending. + this.connect().catch((err) => { + logger.warn("Kalshi WebSocket connect failed during subscribeToTrades", { + error: String(err), + }); + if (!this.isTerminated) { + this.scheduleReconnect(); + } }); } else { this.subscribeToTrades([ticker]); diff --git a/core/src/exchanges/opinion/websocket.ts b/core/src/exchanges/opinion/websocket.ts index fd1380ba..cc587776 100644 --- a/core/src/exchanges/opinion/websocket.ts +++ b/core/src/exchanges/opinion/websocket.ts @@ -242,10 +242,7 @@ export class OpinionWebSocket { return; } - const existing = this.orderBooks.get(marketId); - const book: OrderBook = existing - ? { ...existing } - : { bids: [], asks: [], timestamp: Date.now() }; + const book: OrderBook = this.orderBooks.get(marketId) ?? { bids: [], asks: [], timestamp: Date.now() }; if (side === "bids") { book.bids = applyLevelUpdate(book.bids, price, size, "desc"); @@ -333,8 +330,11 @@ export class OpinionWebSocket { } if (!this.isConnected) { - this.connect().catch(() => { - // Swallow — scheduleReconnect will retry. Resolvers stay pending. + this.connect().catch((err) => { + logger.warn("Opinion WebSocket connect failed during watchOrderBook", { error: String(err) }); + if (!this.isTerminated) { + this.scheduleReconnect(); + } }); } else { this.sendSubscribe("market.depth.diff", marketId); @@ -370,8 +370,11 @@ export class OpinionWebSocket { } if (!this.isConnected) { - this.connect().catch(() => { - // Swallow — scheduleReconnect will retry. Resolvers stay pending. + this.connect().catch((err) => { + logger.warn("Opinion WebSocket connect failed during watchTrades", { error: String(err) }); + if (!this.isTerminated) { + this.scheduleReconnect(); + } }); } else { this.sendSubscribe("market.last.trade", marketId); @@ -457,29 +460,32 @@ export class OpinionWebSocket { /** * Apply an absolute-size level update to one side of the book. - * A size of 0 (or NaN) removes the level. Returns a new sorted array. + * A size of 0 (or NaN) removes the level. Mutates the provided array in place + * and keeps it sorted. */ function applyLevelUpdate( - levels: readonly OrderLevel[], + levels: OrderLevel[], price: number, size: number, sortOrder: "asc" | "desc", ): OrderLevel[] { - const updated = levels.filter( - (l) => Math.abs(l.price - price) >= 0.0001, - ); + const idx = levels.findIndex((l) => l.price === price); + + if (idx !== -1) { + levels.splice(idx, 1); + } if (!isNaN(size) && size > 0) { - updated.push({ price, size }); + levels.push({ price, size }); } if (sortOrder === "desc") { - updated.sort((a, b) => b.price - a.price); + levels.sort((a, b) => b.price - a.price); } else { - updated.sort((a, b) => a.price - b.price); + levels.sort((a, b) => a.price - b.price); } - return updated; + return levels; } /** diff --git a/core/src/exchanges/polymarket_us/websocket.ts b/core/src/exchanges/polymarket_us/websocket.ts index 04079110..c5e77276 100644 --- a/core/src/exchanges/polymarket_us/websocket.ts +++ b/core/src/exchanges/polymarket_us/websocket.ts @@ -22,6 +22,7 @@ import type { MarketBook, } from 'polymarket-us'; import { OrderBook, QueuedPromise, Trade } from '../../types'; +import { logger } from '../../utils/logger'; import { DEFAULT_WATCH_TIMEOUT_MS, withWatchTimeout } from '../../utils/watch-timeout'; import { fromAmount } from './price'; import { PolymarketUSNormalizer } from './normalizer'; @@ -124,8 +125,8 @@ export class PolymarketUSWebSocket { if (this.socket) { try { this.socket.close(); - } catch { - // Ignore close errors. + } catch (error) { + logger.debug('[polymarket_us] error during socket close', { error: String(error) }); } this.socket = null; } diff --git a/core/src/feeds/binance/binance-feed.ts b/core/src/feeds/binance/binance-feed.ts index e60b2184..a9020cb6 100644 --- a/core/src/feeds/binance/binance-feed.ts +++ b/core/src/feeds/binance/binance-feed.ts @@ -131,7 +131,7 @@ export class BinanceFeed extends BaseDataFeed { protected watchTickerImpl(symbol: string, callback: (ticker: Ticker) => void): () => void { const sub: Subscription = { symbol, callback }; - this.subscriptions = [...this.subscriptions, sub]; + this.subscriptions.push(sub); this.ensureConnected().catch((err: unknown) => { logger.error('[BinanceFeed] initial connect failed in watchTickerImpl', { error: err instanceof Error ? err.message : String(err), @@ -213,7 +213,10 @@ export class BinanceFeed extends BaseDataFeed { let msg: BinanceRelayMessage; try { msg = JSON.parse(text) as BinanceRelayMessage; - } catch { + } catch (error) { + logger.debug('[BinanceFeed] failed to parse relay message', { + error: error instanceof Error ? error.message : String(error), + }); return; } @@ -222,7 +225,7 @@ export class BinanceFeed extends BaseDataFeed { const event = msg as BinanceRelayTradeEvent; const ticker = normalizeTradeToTicker(event); - this.latestTickers = new Map(this.latestTickers).set(ticker.symbol, ticker); + this.latestTickers.set(ticker.symbol, ticker); for (const sub of this.subscriptions) { if (sub.symbol === ticker.symbol) { diff --git a/core/src/feeds/chainlink/chainlink-feed.ts b/core/src/feeds/chainlink/chainlink-feed.ts index c29ebe30..f336079a 100644 --- a/core/src/feeds/chainlink/chainlink-feed.ts +++ b/core/src/feeds/chainlink/chainlink-feed.ts @@ -179,7 +179,7 @@ export class ChainlinkFeed extends BaseDataFeed { protected watchTickerImpl(symbol: string, callback: (ticker: Ticker) => void): () => void { const sub: Subscription = { symbol: symbol.toUpperCase(), callback }; - this.subscriptions = [...this.subscriptions, sub]; + this.subscriptions.push(sub); this.ensureConnected().catch((err: unknown) => { logger.error('[ChainlinkFeed] initial connect failed in watchTickerImpl', { error: err instanceof Error ? err.message : String(err) }); }); @@ -384,7 +384,10 @@ export class ChainlinkFeed extends BaseDataFeed { let msg: ChainlinkWsMessage; try { msg = JSON.parse(text) as ChainlinkWsMessage; - } catch { + } catch (error) { + logger.debug('[ChainlinkFeed] failed to parse relay message', { + error: error instanceof Error ? error.message : String(error), + }); return; } From 83df2203eda3909f6472c122002190b28205105a Mon Sep 17 00:00:00 2001 From: Samuel Tinnerholm Date: Fri, 5 Jun 2026 14:23:05 +0000 Subject: [PATCH 4/4] chore: sync generated outputs for websocket hygiene PR --- core/api-doc-config.generated.json | 36 +-- core/src/server/openapi.yaml | 296 ++++++++++++++++++++++-- docs/api-reference/fetch-order-book.mdx | 95 +++----- docs/llms-full.txt | 96 +++----- sdks/python/API_REFERENCE.md | 2 +- sdks/typescript/API_REFERENCE.md | 2 +- 6 files changed, 356 insertions(+), 171 deletions(-) diff --git a/core/api-doc-config.generated.json b/core/api-doc-config.generated.json index 3e176c67..9e70f2b8 100644 --- a/core/api-doc-config.generated.json +++ b/core/api-doc-config.generated.json @@ -1,5 +1,5 @@ { - "_generated": "Auto-generated by extract-jsdoc.js on 2026-06-02T00:34:44.916Z. Do not edit manually.", + "_generated": "Auto-generated by extract-jsdoc.js on 2026-06-05T14:22:59.198Z. Do not edit manually.", "methods": { "has": { "summary": "HTTP verb for the endpoint (e.g. GET, POST). */", @@ -566,7 +566,7 @@ "type": "UnifiedEvent[]", "description": "Filtered array of events" }, - "source": "BaseExchange.ts:1317" + "source": "BaseExchange.ts:1318" }, "watchOrderBook": { "summary": "Watch order book updates in real-time via WebSocket.", @@ -595,7 +595,7 @@ "type": "OrderBook", "description": "Promise that resolves with the current orderbook state" }, - "source": "BaseExchange.ts:1413" + "source": "BaseExchange.ts:1414" }, "watchOrderBooks": { "summary": "Watch multiple order books simultaneously via WebSocket.", @@ -624,7 +624,7 @@ "type": "Record", "description": "Promise that resolves with order books keyed by ID" }, - "source": "BaseExchange.ts:1426" + "source": "BaseExchange.ts:1427" }, "unwatchOrderBook": { "summary": "Unsubscribe from a previously watched order book stream.", @@ -641,7 +641,7 @@ "type": "void", "description": "Result" }, - "source": "BaseExchange.ts:1454" + "source": "BaseExchange.ts:1455" }, "watchTrades": { "summary": "Watch trade executions in real-time via WebSocket.", @@ -676,7 +676,7 @@ "type": "Trade[]", "description": "Promise that resolves with recent trades" }, - "source": "BaseExchange.ts:1467" + "source": "BaseExchange.ts:1468" }, "watchAddress": { "summary": "Stream activity for a public wallet address", @@ -699,7 +699,7 @@ "type": "SubscribedAddressSnapshot", "description": "Promise that resolves with the latest SubscribedAddressSnapshot snapshot" }, - "source": "BaseExchange.ts:1481" + "source": "BaseExchange.ts:1482" }, "unwatchAddress": { "summary": "Stop watching a previously registered wallet address and release its resource updates.", @@ -716,7 +716,7 @@ "type": "void", "description": "Result" }, - "source": "BaseExchange.ts:1494" + "source": "BaseExchange.ts:1495" }, "close": { "summary": "Close all WebSocket connections and clean up resources.", @@ -726,7 +726,7 @@ "type": "void", "description": "Result" }, - "source": "BaseExchange.ts:1503" + "source": "BaseExchange.ts:1504" }, "fetchMarketMatches": { "summary": "Find the same or related market on other venues. Two modes:", @@ -743,7 +743,7 @@ "type": "MatchResult[]", "description": "Array of matched markets with relation and confidence" }, - "source": "BaseExchange.ts:1517" + "source": "BaseExchange.ts:1518" }, "fetchMatches": { "summary": "fetchMatches", @@ -760,7 +760,7 @@ "type": "MatchResult[]", "description": "Result" }, - "source": "BaseExchange.ts:1533" + "source": "BaseExchange.ts:1534" }, "fetchEventMatches": { "summary": "Find the same or related event on other venues. Two modes:", @@ -777,7 +777,7 @@ "type": "EventMatchResult[]", "description": "Array of matched events with market-level match details" }, - "source": "BaseExchange.ts:1541" + "source": "BaseExchange.ts:1542" }, "compareMarketPrices": { "summary": "Compare live prices for the same market across venues. Finds identity matches and returns side-by-side best bid/ask prices so you can spot price differences at a glance.", @@ -794,7 +794,7 @@ "type": "PriceComparison[]", "description": "Array of price comparisons across venues" }, - "source": "BaseExchange.ts:1557" + "source": "BaseExchange.ts:1558" }, "fetchRelatedMarkets": { "summary": "Find related markets across venues. Discovers subset/superset market relationships", @@ -811,7 +811,7 @@ "type": "PriceComparison[]", "description": "Array of subset/superset matches with live prices" }, - "source": "BaseExchange.ts:1567" + "source": "BaseExchange.ts:1568" }, "fetchMatchedMarkets": { "summary": "fetchMatchedMarkets", @@ -828,7 +828,7 @@ "type": "MatchedMarketPair[]", "description": "Result" }, - "source": "BaseExchange.ts:1578" + "source": "BaseExchange.ts:1579" }, "fetchMatchedPrices": { "summary": "fetchMatchedPrices", @@ -845,7 +845,7 @@ "type": "MatchedPricePair[]", "description": "Array of matched market pairs with prices from each venue" }, - "source": "BaseExchange.ts:1586" + "source": "BaseExchange.ts:1587" }, "fetchHedges": { "summary": "fetchHedges", @@ -862,7 +862,7 @@ "type": "PriceComparison[]", "description": "Array of subset/superset matches with live prices" }, - "source": "BaseExchange.ts:1597" + "source": "BaseExchange.ts:1598" }, "fetchArbitrage": { "summary": "fetchArbitrage", @@ -879,7 +879,7 @@ "type": "ArbitrageOpportunity[]", "description": "Array of arbitrage opportunities sorted by spread" }, - "source": "BaseExchange.ts:1607" + "source": "BaseExchange.ts:1608" }, "watchPrices": { "summary": "Watch AMM price updates for a market address (Limitless only).", diff --git a/core/src/server/openapi.yaml b/core/src/server/openapi.yaml index c91cfce5..6c1789f8 100644 --- a/core/src/server/openapi.yaml +++ b/core/src/server/openapi.yaml @@ -72,7 +72,26 @@ paths: summary: Fetch Markets operationId: fetchMarkets parameters: - - $ref: '#/components/parameters/ExchangeParam' + - in: path + name: exchange + schema: + type: string + enum: + - polymarket + - kalshi + - kalshi-demo + - limitless + - probable + - baozi + - myriad + - opinion + - metaculus + - smarkets + - polymarket_us + - suibets + - router + required: true + description: The prediction market exchange to target. - in: query name: limit required: false @@ -258,7 +277,26 @@ paths: summary: Fetch Events operationId: fetchEvents parameters: - - $ref: '#/components/parameters/ExchangeParam' + - in: path + name: exchange + schema: + type: string + enum: + - polymarket + - kalshi + - kalshi-demo + - limitless + - probable + - baozi + - myriad + - opinion + - metaculus + - smarkets + - polymarket_us + - suibets + - router + required: true + description: The prediction market exchange to target. - in: query name: query required: false @@ -383,7 +421,19 @@ paths: summary: Fetch Series operationId: fetchSeries parameters: - - $ref: '#/components/parameters/ExchangeParam' + - in: path + name: exchange + schema: + type: string + enum: + - polymarket + - kalshi + - kalshi-demo + - opinion + - polymarket_us + - router + required: true + description: The prediction market exchange to target. responses: '200': description: Fetch Series response @@ -637,7 +687,21 @@ paths: summary: Fetch OHLCV operationId: fetchOHLCV parameters: - - $ref: '#/components/parameters/ExchangeParam' + - in: path + name: exchange + schema: + type: string + enum: + - polymarket + - kalshi + - kalshi-demo + - limitless + - probable + - baozi + - myriad + - opinion + required: true + description: The prediction market exchange to target. - in: query name: outcomeId required: true @@ -689,7 +753,25 @@ paths: summary: Fetch Order Book operationId: fetchOrderBook parameters: - - $ref: '#/components/parameters/ExchangeParam' + - in: path + name: exchange + schema: + type: string + enum: + - polymarket + - kalshi + - kalshi-demo + - limitless + - probable + - baozi + - myriad + - opinion + - smarkets + - polymarket_us + - suibets + - router + required: true + description: The prediction market exchange to target. - in: query name: outcomeId required: true @@ -757,7 +839,16 @@ paths: summary: Fetch Order Books operationId: fetchOrderBooks parameters: - - $ref: '#/components/parameters/ExchangeParam' + - in: path + name: exchange + schema: + type: string + enum: + - polymarket + - kalshi + - kalshi-demo + required: true + description: The prediction market exchange to target. requestBody: content: application/json: @@ -799,7 +890,21 @@ paths: summary: Fetch Trades operationId: fetchTrades parameters: - - $ref: '#/components/parameters/ExchangeParam' + - in: path + name: exchange + schema: + type: string + enum: + - polymarket + - kalshi + - kalshi-demo + - limitless + - probable + - baozi + - myriad + - smarkets + required: true + description: The prediction market exchange to target. - in: query name: outcomeId required: true @@ -845,7 +950,24 @@ paths: summary: Create Order operationId: createOrder parameters: - - $ref: '#/components/parameters/ExchangeParam' + - in: path + name: exchange + schema: + type: string + enum: + - polymarket + - kalshi + - kalshi-demo + - limitless + - probable + - baozi + - myriad + - opinion + - metaculus + - smarkets + - polymarket_us + required: true + description: The prediction market exchange to target. requestBody: content: application/json: @@ -881,7 +1003,19 @@ paths: summary: Build Order operationId: buildOrder parameters: - - $ref: '#/components/parameters/ExchangeParam' + - in: path + name: exchange + schema: + type: string + enum: + - polymarket + - kalshi + - kalshi-demo + - opinion + - smarkets + - polymarket_us + required: true + description: The prediction market exchange to target. requestBody: content: application/json: @@ -919,7 +1053,19 @@ paths: summary: Submit Order operationId: submitOrder parameters: - - $ref: '#/components/parameters/ExchangeParam' + - in: path + name: exchange + schema: + type: string + enum: + - polymarket + - kalshi + - kalshi-demo + - opinion + - smarkets + - polymarket_us + required: true + description: The prediction market exchange to target. requestBody: content: application/json: @@ -955,7 +1101,22 @@ paths: summary: Cancel Order operationId: cancelOrder parameters: - - $ref: '#/components/parameters/ExchangeParam' + - in: path + name: exchange + schema: + type: string + enum: + - polymarket + - kalshi + - kalshi-demo + - limitless + - probable + - opinion + - metaculus + - smarkets + - polymarket_us + required: true + description: The prediction market exchange to target. requestBody: content: application/json: @@ -991,7 +1152,21 @@ paths: summary: Fetch Order operationId: fetchOrder parameters: - - $ref: '#/components/parameters/ExchangeParam' + - in: path + name: exchange + schema: + type: string + enum: + - polymarket + - kalshi + - kalshi-demo + - probable + - baozi + - opinion + - smarkets + - polymarket_us + required: true + description: The prediction market exchange to target. - in: query name: orderId required: true @@ -1015,7 +1190,23 @@ paths: summary: Fetch Open Orders operationId: fetchOpenOrders parameters: - - $ref: '#/components/parameters/ExchangeParam' + - in: path + name: exchange + schema: + type: string + enum: + - polymarket + - kalshi + - kalshi-demo + - limitless + - probable + - baozi + - myriad + - opinion + - smarkets + - polymarket_us + required: true + description: The prediction market exchange to target. - in: query name: marketId required: false @@ -1041,7 +1232,22 @@ paths: summary: Fetch My Trades operationId: fetchMyTrades parameters: - - $ref: '#/components/parameters/ExchangeParam' + - in: path + name: exchange + schema: + type: string + enum: + - polymarket + - kalshi + - kalshi-demo + - limitless + - probable + - myriad + - opinion + - smarkets + - polymarket_us + required: true + description: The prediction market exchange to target. - in: query name: outcomeId required: false @@ -1099,7 +1305,18 @@ paths: summary: Fetch Closed Orders operationId: fetchClosedOrders parameters: - - $ref: '#/components/parameters/ExchangeParam' + - in: path + name: exchange + schema: + type: string + enum: + - kalshi + - kalshi-demo + - limitless + - opinion + - smarkets + required: true + description: The prediction market exchange to target. - in: query name: marketId required: false @@ -1151,7 +1368,18 @@ paths: summary: Fetch All Orders operationId: fetchAllOrders parameters: - - $ref: '#/components/parameters/ExchangeParam' + - in: path + name: exchange + schema: + type: string + enum: + - kalshi + - kalshi-demo + - limitless + - opinion + - smarkets + required: true + description: The prediction market exchange to target. - in: query name: marketId required: false @@ -1203,7 +1431,24 @@ paths: summary: Fetch Positions operationId: fetchPositions parameters: - - $ref: '#/components/parameters/ExchangeParam' + - in: path + name: exchange + schema: + type: string + enum: + - polymarket + - kalshi + - kalshi-demo + - limitless + - probable + - baozi + - myriad + - opinion + - smarkets + - polymarket_us + - suibets + required: true + description: The prediction market exchange to target. - in: query name: address required: false @@ -1229,7 +1474,22 @@ paths: summary: Fetch Balance operationId: fetchBalance parameters: - - $ref: '#/components/parameters/ExchangeParam' + - in: path + name: exchange + schema: + type: string + enum: + - polymarket + - kalshi + - kalshi-demo + - limitless + - probable + - baozi + - myriad + - smarkets + - polymarket_us + required: true + description: The prediction market exchange to target. - in: query name: address required: false diff --git a/docs/api-reference/fetch-order-book.mdx b/docs/api-reference/fetch-order-book.mdx index 55b0cb4d..1aea6e43 100644 --- a/docs/api-reference/fetch-order-book.mdx +++ b/docs/api-reference/fetch-order-book.mdx @@ -7,11 +7,7 @@ openapi: GET /api/{exchange}/fetchOrderBook ### Live order book -Fetch the current L2 order book for an outcome from the exchange's live order book endpoint. For Polymarket this is a live CLOB call: pass the outcome token ID directly and omit historical params. - - -`poly.fetch_order_book(token_id)` without `params.since` or `params.until` is live-only. It does not read PMXT Archive data and may fail for closed, resolved, or otherwise inactive Polymarket markets with an error such as `No orderbook exists`. - +Fetch the current L2 order book for an outcome. If you already have an outcome token ID, pass it directly: ```python Python @@ -32,15 +28,15 @@ console.log(`${book.bids.length} bids, ${book.asks.length} asks`); ```bash curl curl "https://api.pmxt.dev/api/polymarket/fetchOrderBook?outcomeId=104932610032177696635191871147557737718087870958469629338467406422339967452218" \ - -H "Authorization: Bearer ***" + -H "Authorization: Bearer $PMXT_API_KEY" ``` ### Historical snapshot -Get the order book at a specific point in time by passing `since` as a Unix timestamp in milliseconds. Historical Polymarket queries are served from the PMXT Archive and return the nearest reconstructed snapshot at or before that time. +Get the order book at a specific point in time. Pass `since` as a Unix timestamp in milliseconds — returns the nearest snapshot at or before that time. -For binary markets, you can pass the Polymarket condition ID as the first argument and choose the side with `params.outcome`. Use `"yes"` or `"no"` instead of copying the long outcome token ID. +For binary markets, you can pass the market ID and choose the side with `params.outcome`. Use `"yes"` or `"no"` instead of copying the long outcome token ID: ```python Python @@ -48,9 +44,12 @@ import pmxt poly = pmxt.Polymarket(pmxt_api_key="pmxt_...") -# Historical lookup against PMXT Archive, not the live CLOB. +market = poly.fetch_market( + slug="will-spacex-starship-flight-test-12-launch-by-may-22-354-721" +) + book = poly.fetch_order_book( - "0xc704f74e2f9dfae70f770cb253ffadde10768eeab41233098bf5ac67995a94b5", + market.market_id, params={"since": 1779487200000, "outcome": "yes"}, ) @@ -64,9 +63,12 @@ import { Polymarket } from "pmxtjs"; const poly = new Polymarket({ pmxtApiKey: "pmxt_..." }); -// Historical lookup against PMXT Archive, not the live CLOB. +const market = await poly.fetchMarket({ + slug: "will-spacex-starship-flight-test-12-launch-by-may-22-354-721", +}); + const book = await poly.fetchOrderBook( - "0xc704f74e2f9dfae70f770cb253ffadde10768eeab41233098bf5ac67995a94b5", + market.marketId, undefined, { since: 1779487200000, outcome: "yes" } ); @@ -77,51 +79,9 @@ console.log(` best ask: ${Math.min(...book.asks.map((a) => a.price)).toFixed(3) ```bash curl curl -X POST "https://api.pmxt.dev/api/polymarket/fetchOrderBook" \ - -H "Authorization: Bearer ***" \ - -H "Content-Type: application/json" \ - -d '{"args":["0xc704f74e2f9dfae70f770cb253ffadde10768eeab41233098bf5ac67995a94b5", null, {"since": 1779487200000, "outcome": "yes"}]}' -``` - - -### Historical crypto 5m events - -Polymarket crypto 5-minute slugs such as `btc-updown-5m-1779481500` are event slugs. Use `fetch_event(slug=...)`, then select the nested market you want. Do not use `fetch_market(slug=...)` for these event-level slugs. - - -```python Python -import pmxt - -poly = pmxt.Polymarket(pmxt_api_key="pmxt_...") -event = poly.fetch_event(slug="btc-updown-5m-1779481500") -market = event.markets[0] - -book = poly.fetch_order_book( - market.market_id, - params={"since": 1779487200000, "outcome": "yes"}, -) -print(f"{market.title}: {book.dt}") -``` - -```javascript JavaScript -import { Polymarket } from "pmxtjs"; - -const poly = new Polymarket({ pmxtApiKey: "pmxt_..." }); -const event = await poly.fetchEvent({ slug: "btc-updown-5m-1779481500" }); -const market = event.markets[0]; - -const book = await poly.fetchOrderBook( - market.marketId, - undefined, - { since: 1779487200000, outcome: "yes" } -); -console.log(`${market.title}: ${book.datetime}`); -``` - -```bash curl -curl -X POST "https://api.pmxt.dev/api/polymarket/fetchEvent" \ - -H "Authorization: Bearer ***" \ + -H "Authorization: Bearer $PMXT_API_KEY" \ -H "Content-Type: application/json" \ - -d '{"kwargs":{"slug":"btc-updown-5m-1779481500"}}' + -d '{"args":["61b0ed20-7f42-41fd-af15-7b86153f6bb7", null, {"since": 1779487200000, "outcome": "yes"}]}' ``` @@ -129,15 +89,17 @@ curl -X POST "https://api.pmxt.dev/api/polymarket/fetchEvent" \ Pass both `since` and `until` to get an array of fully reconstructed L2 order book snapshots. Each snapshot is a complete book at that moment in time — not deltas. -The API returns up to `limit` snapshots from the PMXT Archive. Defaults to 100 snapshots per request, max 1000. For raw bulk downloads, use the Parquet files in the [PMXT Archive](https://archive.pmxt.dev) instead of trying to stream bulk data through the live order book endpoint. +Default 100 snapshots per request, max 1000. ```python Python import pmxt poly = pmxt.Polymarket(pmxt_api_key="pmxt_...") -event = poly.fetch_event(slug="btc-updown-5m-1779481500") -market = event.markets[0] + +market = poly.fetch_market( + slug="will-spacex-starship-flight-test-12-launch-by-may-22-354-721" +) books = poly.fetch_order_book( market.market_id, @@ -145,7 +107,6 @@ books = poly.fetch_order_book( "since": 1779480000000, "until": 1779487200000, "outcome": "yes", - "limit": 100, } ) print(f"{len(books)} snapshots") @@ -157,13 +118,15 @@ for ob in books[:3]: import { Polymarket } from "pmxtjs"; const poly = new Polymarket({ pmxtApiKey: "pmxt_..." }); -const event = await poly.fetchEvent({ slug: "btc-updown-5m-1779481500" }); -const market = event.markets[0]; + +const market = await poly.fetchMarket({ + slug: "will-spacex-starship-flight-test-12-launch-by-may-22-354-721", +}); const books = await poly.fetchOrderBook( market.marketId, undefined, - { since: 1779480000000, until: 1779487200000, outcome: "yes", limit: 100 } + { since: 1779480000000, until: 1779487200000, outcome: "yes" } ); console.log(`${books.length} snapshots`); books.slice(0, 3).forEach((ob) => @@ -173,12 +136,12 @@ books.slice(0, 3).forEach((ob) => ```bash curl curl -X POST "https://api.pmxt.dev/api/polymarket/fetchOrderBook" \ - -H "Authorization: Bearer ***" \ + -H "Authorization: Bearer $PMXT_API_KEY" \ -H "Content-Type: application/json" \ - -d '{"args":["0xc704f74e2f9dfae70f770cb253ffadde10768eeab41233098bf5ac67995a94b5", null, {"since": 1779480000000, "until": 1779487200000, "outcome": "yes", "limit": 100}]}' + -d '{"args":["61b0ed20-7f42-41fd-af15-7b86153f6bb7", null, {"since": 1779480000000, "until": 1779487200000, "outcome": "yes"}]}' ``` -Historical order book data is backed by the [PMXT Archive](https://archive.pmxt.dev) — the same tick-level data available as Parquet files, but queryable directly from the API without downloading or parsing files. Historical `fetchOrderBook` supports Polymarket, Kalshi, Limitless, and Opinion. +Historical order book data is backed by the [PMXT Archive](https://archive.pmxt.dev) — the same tick-level data available as Parquet files, but queryable directly from the API without downloading or parsing files. Supports Polymarket, Kalshi, Limitless, and Opinion. diff --git a/docs/llms-full.txt b/docs/llms-full.txt index bbf82c1f..55c492dc 100644 --- a/docs/llms-full.txt +++ b/docs/llms-full.txt @@ -3274,12 +3274,7 @@ Source: https://pmxt.dev/docs/api-reference/fetch-order-book ##### Live order book -Fetch the current L2 order book for an outcome from the exchange's live order book endpoint. For Polymarket this is a live CLOB call: pass the outcome token ID directly and omit historical params. - - -> **Warning:** -`poly.fetch_order_book(token_id)` without `params.since` or `params.until` is live-only. It does not read PMXT Archive data and may fail for closed, resolved, or otherwise inactive Polymarket markets with an error such as `No orderbook exists`. - +Fetch the current L2 order book for an outcome. If you already have an outcome token ID, pass it directly: ```python Python @@ -3300,15 +3295,15 @@ console.log(`${book.bids.length} bids, ${book.asks.length} asks`); ```bash curl curl "https://api.pmxt.dev/api/polymarket/fetchOrderBook?outcomeId=104932610032177696635191871147557737718087870958469629338467406422339967452218" \ - -H "Authorization: Bearer ***" + -H "Authorization: Bearer $PMXT_API_KEY" ``` ##### Historical snapshot -Get the order book at a specific point in time by passing `since` as a Unix timestamp in milliseconds. Historical Polymarket queries are served from the PMXT Archive and return the nearest reconstructed snapshot at or before that time. +Get the order book at a specific point in time. Pass `since` as a Unix timestamp in milliseconds — returns the nearest snapshot at or before that time. -For binary markets, you can pass the Polymarket condition ID as the first argument and choose the side with `params.outcome`. Use `"yes"` or `"no"` instead of copying the long outcome token ID. +For binary markets, you can pass the market ID and choose the side with `params.outcome`. Use `"yes"` or `"no"` instead of copying the long outcome token ID: ```python Python @@ -3316,9 +3311,12 @@ import pmxt poly = pmxt.Polymarket(pmxt_api_key="pmxt_...") -# Historical lookup against PMXT Archive, not the live CLOB. +market = poly.fetch_market( +slug="will-spacex-starship-flight-test-12-launch-by-may-22-354-721" +) + book = poly.fetch_order_book( -"0xc704f74e2f9dfae70f770cb253ffadde10768eeab41233098bf5ac67995a94b5", +market.market_id, params={"since": 1779487200000, "outcome": "yes"}, ) @@ -3332,9 +3330,12 @@ import { Polymarket } from "pmxtjs"; const poly = new Polymarket({ pmxtApiKey: "pmxt_..." }); -// Historical lookup against PMXT Archive, not the live CLOB. +const market = await poly.fetchMarket({ + slug: "will-spacex-starship-flight-test-12-launch-by-may-22-354-721", +}); + const book = await poly.fetchOrderBook( - "0xc704f74e2f9dfae70f770cb253ffadde10768eeab41233098bf5ac67995a94b5", + market.marketId, undefined, { since: 1779487200000, outcome: "yes" } ); @@ -3345,51 +3346,9 @@ console.log(` best ask: ${Math.min(...book.asks.map((a) => a.price)).toFixed(3) ```bash curl curl -X POST "https://api.pmxt.dev/api/polymarket/fetchOrderBook" \ - -H "Authorization: Bearer ***" \ - -H "Content-Type: application/json" \ - -d '{"args":["0xc704f74e2f9dfae70f770cb253ffadde10768eeab41233098bf5ac67995a94b5", null, {"since": 1779487200000, "outcome": "yes"}]}' -``` - - -##### Historical crypto 5m events - -Polymarket crypto 5-minute slugs such as `btc-updown-5m-1779481500` are event slugs. Use `fetch_event(slug=...)`, then select the nested market you want. Do not use `fetch_market(slug=...)` for these event-level slugs. - - -```python Python -import pmxt - -poly = pmxt.Polymarket(pmxt_api_key="pmxt_...") -event = poly.fetch_event(slug="btc-updown-5m-1779481500") -market = event.markets[0] - -book = poly.fetch_order_book( -market.market_id, -params={"since": 1779487200000, "outcome": "yes"}, -) -print(f"{market.title}: {book.dt}") -``` - -```javascript JavaScript -import { Polymarket } from "pmxtjs"; - -const poly = new Polymarket({ pmxtApiKey: "pmxt_..." }); -const event = await poly.fetchEvent({ slug: "btc-updown-5m-1779481500" }); -const market = event.markets[0]; - -const book = await poly.fetchOrderBook( - market.marketId, - undefined, - { since: 1779487200000, outcome: "yes" } -); -console.log(`${market.title}: ${book.datetime}`); -``` - -```bash curl -curl -X POST "https://api.pmxt.dev/api/polymarket/fetchEvent" \ - -H "Authorization: Bearer ***" \ + -H "Authorization: Bearer $PMXT_API_KEY" \ -H "Content-Type: application/json" \ - -d '{"kwargs":{"slug":"btc-updown-5m-1779481500"}}' + -d '{"args":["61b0ed20-7f42-41fd-af15-7b86153f6bb7", null, {"since": 1779487200000, "outcome": "yes"}]}' ``` @@ -3397,15 +3356,17 @@ curl -X POST "https://api.pmxt.dev/api/polymarket/fetchEvent" \ Pass both `since` and `until` to get an array of fully reconstructed L2 order book snapshots. Each snapshot is a complete book at that moment in time — not deltas. -The API returns up to `limit` snapshots from the PMXT Archive. Defaults to 100 snapshots per request, max 1000. For raw bulk downloads, use the Parquet files in the [PMXT Archive](https://archive.pmxt.dev) instead of trying to stream bulk data through the live order book endpoint. +Default 100 snapshots per request, max 1000. ```python Python import pmxt poly = pmxt.Polymarket(pmxt_api_key="pmxt_...") -event = poly.fetch_event(slug="btc-updown-5m-1779481500") -market = event.markets[0] + +market = poly.fetch_market( +slug="will-spacex-starship-flight-test-12-launch-by-may-22-354-721" +) books = poly.fetch_order_book( market.market_id, @@ -3413,7 +3374,6 @@ params={ "since": 1779480000000, "until": 1779487200000, "outcome": "yes", - "limit": 100, } ) print(f"{len(books)} snapshots") @@ -3425,13 +3385,15 @@ print(f" {ob.dt} bid={ob.bids[0].price} ask={ob.asks[0].price}") import { Polymarket } from "pmxtjs"; const poly = new Polymarket({ pmxtApiKey: "pmxt_..." }); -const event = await poly.fetchEvent({ slug: "btc-updown-5m-1779481500" }); -const market = event.markets[0]; + +const market = await poly.fetchMarket({ + slug: "will-spacex-starship-flight-test-12-launch-by-may-22-354-721", +}); const books = await poly.fetchOrderBook( market.marketId, undefined, - { since: 1779480000000, until: 1779487200000, outcome: "yes", limit: 100 } + { since: 1779480000000, until: 1779487200000, outcome: "yes" } ); console.log(`${books.length} snapshots`); books.slice(0, 3).forEach((ob) => @@ -3441,15 +3403,15 @@ books.slice(0, 3).forEach((ob) => ```bash curl curl -X POST "https://api.pmxt.dev/api/polymarket/fetchOrderBook" \ - -H "Authorization: Bearer ***" \ + -H "Authorization: Bearer $PMXT_API_KEY" \ -H "Content-Type: application/json" \ - -d '{"args":["0xc704f74e2f9dfae70f770cb253ffadde10768eeab41233098bf5ac67995a94b5", null, {"since": 1779480000000, "until": 1779487200000, "outcome": "yes", "limit": 100}]}' + -d '{"args":["61b0ed20-7f42-41fd-af15-7b86153f6bb7", null, {"since": 1779480000000, "until": 1779487200000, "outcome": "yes"}]}' ``` > **Note:** -Historical order book data is backed by the [PMXT Archive](https://archive.pmxt.dev) — the same tick-level data available as Parquet files, but queryable directly from the API without downloading or parsing files. Historical `fetchOrderBook` supports Polymarket, Kalshi, Limitless, and Opinion. +Historical order book data is backed by the [PMXT Archive](https://archive.pmxt.dev) — the same tick-level data available as Parquet files, but queryable directly from the API without downloading or parsing files. Supports Polymarket, Kalshi, Limitless, and Opinion. ### Fetch Order Books diff --git a/sdks/python/API_REFERENCE.md b/sdks/python/API_REFERENCE.md index dac681ce..b20f3aa2 100644 --- a/sdks/python/API_REFERENCE.md +++ b/sdks/python/API_REFERENCE.md @@ -1456,7 +1456,7 @@ title: str # The market title (e.g., "Will BTC close above $100k on Dec 31?"). description: str # Long-form market description or resolution criteria. slug: str # URL-friendly slug for the market. outcomes: List[MarketOutcome] # The possible outcomes for this market. -resolution_date: str # When the market is scheduled to resolve. +resolution_date: str # When the market is scheduled to resolve. Optional because some venues do not publish a cutoff for every market (e.g. Opinion categorical children) — emit `undefined` rather than coercing to epoch. volume24h: float # Trading volume over the past 24 hours (USD). volume: float # Total / Lifetime volume liquidity: float # Current market liquidity (USD). diff --git a/sdks/typescript/API_REFERENCE.md b/sdks/typescript/API_REFERENCE.md index f8504e0a..ec2f3280 100644 --- a/sdks/typescript/API_REFERENCE.md +++ b/sdks/typescript/API_REFERENCE.md @@ -1456,7 +1456,7 @@ title: string; // The market title (e.g., "Will BTC close above $100k on Dec 31? description: string; // Long-form market description or resolution criteria. slug: string; // URL-friendly slug for the market. outcomes: MarketOutcome[]; // The possible outcomes for this market. -resolutionDate: string; // When the market is scheduled to resolve. +resolutionDate: string; // When the market is scheduled to resolve. Optional because some venues do not publish a cutoff for every market (e.g. Opinion categorical children) — emit `undefined` rather than coercing to epoch. volume24h: number; // Trading volume over the past 24 hours (USD). volume: number; // Total / Lifetime volume liquidity: number; // Current market liquidity (USD).