Skip to main content

Client

Struct Client 

Source
pub struct Client { /* private fields */ }
Expand description

A typed client for the 0xinsider Developer API.

Cheap to clone: clones share one connection pool. Build one with Client::from_env, Client::new, Client::sandbox or Client::builder.

Implementations§

Source§

impl Client

Source

pub fn new(api_key: impl Into<String>) -> Result<Self>

A client for the production API with this API key (oxi_sk_live_...) or OAuth 2.1 access token (oxi_at_...).

Source

pub fn from_env() -> Result<Self>

A client for the production API with the key in OXINSIDER_API_KEY.

When the variable is unset or empty the client has no credential: the public operations (discovery, health, platforms) still work, and every other operation answers 401 invalid_api_key.

Source

pub fn sandbox() -> Result<Self>

A client for the sandbox: no credential, example data only.

Source

pub fn builder() -> ClientBuilder

A builder for any other configuration.

Source

pub fn base_url(&self) -> &str

The server URL every operation path is appended to.

Source

pub fn has_credential(&self) -> bool

Whether requests carry a credential.

Source

pub async fn request_json<T: DeserializeOwned>( &self, method: Method, path: &str, query: &[(&str, &str)], body: Option<&Value>, ) -> Result<T>

Send any request and decode the JSON answer, for an operation this release does not know yet. path is appended to the base URL (/api/v1/leaderboard). GETs are retried like a generated read.

Source§

impl Client

Source

pub async fn get_api_discovery(&self) -> Result<GetApiDiscoveryResponse>

API discovery

GET /api/v1

Unauthenticated API-origin discovery document pointing agents to the canonical API base URL, full docs, web-origin OpenAPI spec, health check, and the COMPLETE index of authenticated data routes. data.authenticated_routes is the whole authenticated route surface, not a sample: it carries every authenticated route this spec documents, so an agent that starts here never has to guess a path. The index is a hand-maintained const in backend/src/api_v1/discovery.rs kept in step with this spec by review; no automated check compares the two, so treat this spec as authoritative if they ever disagree.

Source

pub async fn redirect_api_openapi_spec(&self) -> Result<Download>

Redirect to the canonical OpenAPI spec

GET /api/v1/openapi.json

Unauthenticated API-origin compatibility redirect to the canonical web-origin OpenAPI JSON document at https://0xinsider.com/api/v1/openapi.json.

The API answers with a redirect to the file. It is followed once, by a fresh request that carries no credential, and the file comes back as a streaming Download.

Source

pub async fn register_agent(&self) -> Result<RegisterAgentResponse>

Register an agent for a sandbox key

POST /api/v1/agents/register

Self-serve agent onboarding: no account, no request body, no human step. Returns a sandbox API key (oxi_sk_test_…) and the path to live access. The key works only on the sandbox server (https://0xinsider.com/sandbox/api/v1), where it is optional: send it as Authorization: Bearer to exercise the credential path, and the sandbox answers a malformed key with the production 401. Nothing is stored, so the key cannot be listed or revoked and does not expire; register again for a new one. The live API answers a sandbox key with 401 invalid_api_key and error.reason sandbox_api_key. Live data needs an account with an active Pro subscription, and either an oxi_sk_live_ key from https://0xinsider.com/developers or an OAuth access token (https://0xinsider.com/auth.md). The request body is not read.

Source

pub async fn get_trader( &self, address: &str, params: &GetTraderParams, ) -> Result<GetTraderResponse>

Get trader

GET /api/v1/trader/{address}

Returns a trader’s grade (S through F), P&L, win rate, and optional strategy/category data. The path accepts either an Ethereum wallet address, a known trader username, or a trd_-prefixed trader ID emitted by this API. A wallet address this API does not track yet returns 200 with sync_status “unknown” instead of 404, so it can be polled. A value that is not a wallet address and matches no username or trader ID this API knows returns 404 not_found with error.param address; it is never echoed back as a trader address. The additive stats.exact.total_volume field carries a decimal string with unit, scale and basis metadata; parse it with decimal-safe arithmetic and keep the numeric twin for display. P&L and stats cover sports and esports markets only (#29830): pnl.in_scope (#27311) is the wallet’s sports and esports record, the P&L the 0xinsider profile headlines, and stats counts the same markets. The wallet-wide pnl.total, pnl.net_total, pnl.realized, pnl.exact, pnl.correction and stats.daily_win_rate are deprecated and never sent since 2026-10-27; pnl.unrealized stays wallet-wide.

Source

pub async fn get_trader_with_etag( &self, address: &str, params: &GetTraderParams, ) -> Result<Tagged<GetTraderResponse>>

Get trader, with the response’s ETag.

GET /api/v1/trader/{address}

Self::get_trader, returning the ETag validator the response carried beside the decoded body. Send it back as if_none_match on the next read: an unchanged resource answers Error::NotModified { etag }.

Source

pub async fn get_trader_context_markdown(&self, address: &str) -> Result<String>

Get trader context (Markdown)

GET /api/v1/trader/{address}/context.md

Returns a single human- and LLM-readable Markdown briefing for one trader: identity, grade, P&L, position coverage, and freshness. The path accepts an Ethereum wallet address (0x…), a known trader username, or a trd_-prefixed trader ID emitted by this API. A wallet address this API does not track yet still returns 200 with a degraded ‘not yet synced’ document (no 404). A value that is not a wallet address and matches no username or trader ID this API knows returns 404 not_found with error.param address, as the JSON error envelope. The Markdown variant does not emit an ETag and does not support conditional requests; use the JSON variant (drop the .md suffix) for ETag/If-None-Match handling.

Source

pub async fn get_trader_grade_at( &self, address: &str, as_of: &str, params: &GetTraderGradeAtParams, ) -> Result<GetTraderGradeAtResponse>

Get a trader grade proven visible at a past instant

GET /api/v1/trader/{address}/grade-at

Reads one trader’s recorded grade at as_of from prospective visibility evidence. Before the first recorded observation, after deletion, or during a grade-transition gap, status is unknown and grade is null; a recorded ungraded trader instead has status ungraded. available_from is the first proven observation for this trader, not a claim that earlier grades can be reconstructed. observed_at is when the grade writer began the transition and published_by is the later confirmation that the row was committed, not an exact commit time. Older observations have null model_version, model_build_sha and source_observed_by; do not substitute a current grade or a dated ranking row. The requested event time, publication bounds and model provenance are separate. Use a grade only when status is graded. This is additive; the current-grade trader and leaderboard operations do not change.

Source

pub async fn get_trader_grade_at_with_etag( &self, address: &str, as_of: &str, params: &GetTraderGradeAtParams, ) -> Result<Tagged<GetTraderGradeAtResponse>>

Get a trader grade proven visible at a past instant, with the response’s ETag.

GET /api/v1/trader/{address}/grade-at

Self::get_trader_grade_at, returning the ETag validator the response carried beside the decoded body. Send it back as if_none_match on the next read: an unchanged resource answers Error::NotModified { etag }.

Source

pub async fn get_trader_context( &self, address: &str, params: &GetTraderContextParams, ) -> Result<GetTraderContextResponse>

Get trader context (JSON)

GET /api/v1/trader/{address}/context

Returns a single structured context object for one trader: the full trader profile (same shape as GET /api/v1/trader/{address}) plus a position_summary (sync coverage and realized/unrealized P&L rollups, with an as_of open-position freshness clock), the data_as_of freshness timestamp (the open-position data’s latest /positions snapshot, else last completed sync; the snapshot advances only open positions, so resolved counts and win rate still date to the last full sync; native realized P&L follows its accounting snapshot), and a freshness_note describing the point-in-time snapshot semantics. The path accepts an Ethereum wallet address (0x…), a known trader username, or a trd_-prefixed trader ID emitted by this API. position_summary is omitted when the trader is not in the local database or native net economics is unavailable; realized fields remain numeric when present; a wallet address this API does not track yet returns 200 with sync_status ‘unknown’ on the nested trader (no 404), while a value that is not a wallet address and matches no username or trader ID this API knows returns 404 not_found with error.param address. Append .md to the path for the Markdown rendering.

Source

pub async fn get_trader_context_with_etag( &self, address: &str, params: &GetTraderContextParams, ) -> Result<Tagged<GetTraderContextResponse>>

Get trader context (JSON), with the response’s ETag.

GET /api/v1/trader/{address}/context

Self::get_trader_context, returning the ETag validator the response carried beside the decoded body. Send it back as if_none_match on the next read: an unchanged resource answers Error::NotModified { etag }.

Source

pub async fn batch_get_traders( &self, body: &BatchGetTradersBody, ) -> Result<BatchGetTradersResponse>

Batch traders

POST /api/v1/traders/batch

Returns traders for 1-25 wallet addresses or known usernames. Results preserve request order, duplicate inputs return duplicate rows, and each item is charged one batch item unit before execution. A wallet address this API does not track yet returns data with sync_status “unknown”, matching the single trader endpoint. A username, trd_ id or numeric trader id that resolves to no trader is a per-item not_found error with error.param “traders”, never an ok row keyed on the input.

Source

pub async fn get_position_timeline( &self, address: &str, condition_id: &str, params: &GetPositionTimelineParams, ) -> Result<GetPositionTimelineResponse>

Get a trader’s position timeline for one market

GET /api/v1/trader/{address}/position-timeline

Returns stored Polymarket fills for one tracked trader in one market, newest first, with outcome-specific running_amount and running_avg_price including earlier stored fills before pagination. Stored fills start 2026-07-17; the timeline returns no fill before that date. The {address} segment accepts the same identity shapes as the plural alias: wallet, username, trd_-prefixed trader id, bare integer traders.id, or @username, resolved with precedence @username -> wallet -> trd_ -> integer -> username. A leading @ resolves the rest as a username only, which is how to reach an all-digit username that the integer rule reads as an id. Only warm-tier traders, the wallets we sync most often, are served; others return 404. The cumulative buy-weighted fill average is unchanged by sells. Split, merge, redemption and neg-risk activity are excluded; totals are not complete holdings or provider current-position avgPrice. Cursor-paginated; values stay stable across page sizes/cursors for unchanged source history.

Source

pub async fn get_position_timeline_with_etag( &self, address: &str, condition_id: &str, params: &GetPositionTimelineParams, ) -> Result<Tagged<GetPositionTimelineResponse>>

Get a trader’s position timeline for one market, with the response’s ETag.

GET /api/v1/trader/{address}/position-timeline

Self::get_position_timeline, returning the ETag validator the response carried beside the decoded body. Send it back as if_none_match on the next read: an unchanged resource answers Error::NotModified { etag }.

Source

pub async fn get_trader_category_records( &self, address: &str, params: &GetTraderCategoryRecordsParams, ) -> Result<GetTraderCategoryRecordsResponse>

Get trader category win records

GET /api/v1/trader/{address}/categories

Returns one wallet’s win record in every canonical category it has a settled market in: wins, decided (wins plus losses) and the rate, per category, busiest category first. These are the same counts the Pick of the Day holder chips carry. The Esports record also carries games: the wallet’s record in each esports title it has a settled market in (LoL, CS2, Dota 2, Valorant, …), the same per-game record the holder chips and GET /api/v1/market/{condition_id}/holders name in category_win_rate_game, with the same counts, floor and status rule; the Esports row itself stays the whole bucket, which is what a chip falls back to when a game record is thin. The basis is every settled market at any position size, which is NOT the basis of category_strengths on GET /api/v1/trader/{address}?expand=categories: that breakdown keeps the 20 USD notional floor and the entry-price rule its ranks depend on, so the two legitimately differ, in both directions. Measured on production 2026-09-22 over the 122,497 wallet-category pairs with at least 20 decided markets on both bases: the floored rate was higher in 56.5% of pairs, lower in 34.5% and equal in 9.0%, median +0.6 points, p10 -4.6, p90 +9.8, and 14.0% of pairs differ by 10 points or more. Neither rate corrects the other. The difference is not only small positions: on a 1-in-250 wallet sample the same day, admitted markets won 56.6% while markets dropped by the notional floor alone won 45.2% and markets dropped by the entry-price rule alone won 48.7%. The two tables also run on different clocks, this one rebuilt daily and that breakdown updated incrementally, so a same-day read can differ on timing alone. A category or game with fewer than min_decided_for_win_rate decided markets keeps its counts and returns win_rate null with status not_enough_data, so a caller can apply its own sample rule. A category the wallet has no settled market in is omitted entirely, and so is a game: an absent entry means no record, never a 0% record. A resolved trader with no settled market returns an empty records array (HTTP 200); an unknown address returns 404. Sports and esports only (#27311): records cover sports and esports categories only, so a wallet whose settled markets are all elsewhere returns an empty records array.

Source

pub async fn get_trader_category_records_with_etag( &self, address: &str, params: &GetTraderCategoryRecordsParams, ) -> Result<Tagged<GetTraderCategoryRecordsResponse>>

Get trader category win records, with the response’s ETag.

GET /api/v1/trader/{address}/categories

Self::get_trader_category_records, returning the ETag validator the response carried beside the decoded body. Send it back as if_none_match on the next read: an unchanged resource answers Error::NotModified { etag }.

Source

pub async fn get_trader_pnl( &self, address: &str, params: &GetTraderPnlParams, ) -> Result<GetTraderPnlResponse>

Get trader P&L time series

GET /api/v1/trader/{address}/pnl

Returns a trader’s sports and esports daily P&L time series and pre-derived stats: entries (daily realized P&L and its running sum), period stats (all/90d/30d/7d), monthly aggregation, per-year totals, the drawdown series, series_status, and realized, which reconciles the days with the wallet’s Sports P&L (pnl.in_scope.realized_pnl on GET /api/v1/trader/{address}): the days plus realized.undated_pnl equal it. A day’s daily_change is the realized P&L of the wallet’s sports and esports markets dated to that UTC day, the resolution day for a settled market, else the last trade day. series_status pending means the series is not computed yet; its empty sections never mean zero. A resolved trader with no sports or esports day returns an empty structured object (HTTP 200), as does a wallet that traded no sports or esports market (series_status ready, realized null, freshness_at its last completed sync; since 2026-10-29, #30589, #30657) or that trades only combos and holds no single sports or esports market (the same; since 2026-10-29, #30637); an unknown address returns 404. Optional from/to and sections bound what comes back: from/to clip the two daily series (entries, drawdown) to an inclusive UTC date window, and sections names which of the five sections the body carries. A request that sends none of the three receives the whole object and no data.view. A body built with any of them carries data.view, which names the bounds, the sections applied, the size of the full series and the last daily point before the window; a section the request excluded is absent from data, not null or empty. stats, monthly and year_totals keep their full-period definitions whatever the window says, so an economic value never changes meaning because a chart asked for seven days.

Source

pub async fn get_trader_pnl_with_etag( &self, address: &str, params: &GetTraderPnlParams, ) -> Result<Tagged<GetTraderPnlResponse>>

Get trader P&L time series, with the response’s ETag.

GET /api/v1/trader/{address}/pnl

Self::get_trader_pnl, returning the ETag validator the response carried beside the decoded body. Send it back as if_none_match on the next read: an unchanged resource answers Error::NotModified { etag }.

Source

pub async fn get_position_timeline_by_id( &self, trader: &str, condition_id: &str, params: &GetPositionTimelineByIdParams, ) -> Result<GetPositionTimelineByIdResponse>

Get a trader’s position timeline (unified identity resolver)

GET /api/v1/traders/{trader}/position-timeline

Unified trader-timeline route (#4975). {trader} accepts all five identity shapes - 0x… wallet, username, trd_-prefixed trader id, bare integer id, and @username (a username lookup only, for all-digit usernames) - resolved by the single shared trader-identity resolver. Returns stored Polymarket fills for one tracked trader in one market, newest first, with server-computed running_amount and running_avg_price. Stored fills start 2026-07-17; the timeline returns no fill before that date. Only warm-tier traders, the wallets we sync most often, are tracked; other traders return 404. Response payload matches the singular GET /api/v1/trader/{address}/position-timeline route for the same (trader, market), except for the per-request meta.request_id. The prior integer-id-only variant is preserved: the same path now accepts every identity shape.

Source

pub async fn get_position_timeline_by_id_with_etag( &self, trader: &str, condition_id: &str, params: &GetPositionTimelineByIdParams, ) -> Result<Tagged<GetPositionTimelineByIdResponse>>

Get a trader’s position timeline (unified identity resolver), with the response’s ETag.

GET /api/v1/traders/{trader}/position-timeline

Self::get_position_timeline_by_id, returning the ETag validator the response carried beside the decoded body. Send it back as if_none_match on the next read: an unchanged resource answers Error::NotModified { etag }.

Source

pub async fn list_positions( &self, params: &ListPositionsParams, ) -> Result<ListPositionsResponse>

List current positions (positions-board feed)

GET /api/v1/positions

Returns the current positions-board feed backed by the wallet_positions mirror. Ordered by current_value_usd DESC with deterministic (wallet, condition_id, outcome_index) tiebreakers. Pre-reconcile rows (current_value_usd IS NULL) are excluded. Positions on resolved markets are excluded: a position leaves the feed when its market resolves, so a condition_id read of a resolved market returns no rows. Positions on markets outside sports and esports are excluded on every read, the board and wallet reads included. Cursor-paginated. Every filter pushes into SQL. Deep cursor pages cost the same as the first page: the value bounds and the cursor are index conditions, so a page never rescans the feed from the top. With wallet, the same feed is read for one wallet or a book of up to 25 wallets from each wallet’s own ordered index range, so the pages are that wallet’s complete reconciled binary open positions on sports and esports markets and the cost is the page, never the board. min_size defaults to 100 on the board read, so a request with no wallet and no min_size drops positions valued under 100 USD; with wallet it defaults to 0 instead. Send min_size=0 to include every reconciled position. What a wallet read does not return: positions with shares at 0 (closed), positions on resolved markets (redeemable or already redeemed), positions on markets outside sports and esports, rows the reconciler has not valued yet (current_value_usd IS NULL), and non-binary outcomes; per-row last_reconciled_at and freshness say how old each valuation is. Each row also carries additive exact source atoms as decimal strings with unit, scale and basis metadata; parse those values with decimal-safe arithmetic and do not reconstruct them from the display-safe numeric fields. The response adds a top-level data_quality object beside data, with positions, trader, and market groups owned by wallet_positions.last_reconciled_at|updated_at, traders.last_synced, and market_canonical.last_refreshed_at. Its stored clocks are part of the ETag; meta continues to hold transport cache facts.

Source

pub async fn list_positions_with_etag( &self, params: &ListPositionsParams, ) -> Result<Tagged<ListPositionsResponse>>

List current positions (positions-board feed), with the response’s ETag.

GET /api/v1/positions

Self::list_positions, returning the ETag validator the response carried beside the decoded body. Send it back as if_none_match on the next read: an unchanged resource answers Error::NotModified { etag }.

Source

pub async fn list_large_positions( &self, params: &ListLargePositionsParams, ) -> Result<ListLargePositionsResponse>

List large positions

GET /api/v1/large-positions

Returns the largest current open positions from graded traders, value-descending, with opaque cursor pagination. This is a change-detection feed, not a holder list: a row needs a current value of at least 50,000 USD and a live detection in the last 24 hours, so a wallet that holds a market without moving is not listed. For every graded holder of one market use GET /api/v1/market/{condition_id}/holders. Polymarket-only by design: the large-positions scanner filters platform = ‘polymarket’ (backend/crates/large-positions/src/scanner.rs), so only Polymarket rows are scanned and an unknown condition_id filter returns an empty list (never fabricated rows). Sports and esports markets only (#27305): a position on a market outside product scope is not listed; a condition_id naming one returns 404 not_found with error.reason=market_out_of_scope, and a category outside sports and esports returns 400 bad_request with param=category. The teaser cap that the internal product UI applies to anonymous viewers does not apply here: the API key already proves an active Pro subscription (wire tier insider), so authed API callers get full access.

Source

pub async fn list_large_positions_with_etag( &self, params: &ListLargePositionsParams, ) -> Result<Tagged<ListLargePositionsResponse>>

List large positions, with the response’s ETag.

GET /api/v1/large-positions

Self::list_large_positions, returning the ETag validator the response carried beside the decoded body. Send it back as if_none_match on the next read: an unchanged resource answers Error::NotModified { etag }.

Source

pub async fn list_large_trades( &self, params: &ListLargeTradesParams, ) -> Result<ListLargeTradesResponse>

List large trades

GET /api/v1/large-trades

Returns recent large trades with signal scoring and persisted suspicion facts. Filter by size, category, trader grade, or persisted suspicion. Filters are applied before pagination, and every request uses SQL-backed limit + 1 pagination so has_more and next_cursor reflect the filtered result set. Cursor-paginated, newest first. Market categories come from provider-backed market_canonical identity. Returns trades on sports and esports markets only (#27430): a trade on any other market is not listed, not counted toward has_more, and not returned by since. The response adds a top-level data_quality object beside data, grouping alert, trade, trader, ranking, market, and volume fields by their database writer. whale_alerts.inserted_xid is reported as unknown because it is a transaction identifier rather than a timestamp. Its stored clocks are part of the ETag; meta continues to hold transport cache facts. Pass since to poll for new trades: the answer holds only the trades recorded after the one you name, and with If-None-Match a poll that finds none is 304 with an empty body.

Source

pub async fn list_large_trades_with_etag( &self, params: &ListLargeTradesParams, ) -> Result<Tagged<ListLargeTradesResponse>>

List large trades, with the response’s ETag.

GET /api/v1/large-trades

Self::list_large_trades, returning the ETag validator the response carried beside the decoded body. Send it back as if_none_match on the next read: an unchanged resource answers Error::NotModified { etag }.

Source

pub async fn list_whale_trades( &self, params: &ListWhaleTradesParams, ) -> Result<ListWhaleTradesResponse>

List whale trades

GET /api/v1/whale-trades

Returns recent large trades with signal scoring and persisted suspicion facts. Filter by size, category, trader grade, or persisted suspicion. Filters are applied before pagination, and every request uses SQL-backed limit + 1 pagination so has_more and next_cursor reflect the filtered result set. Cursor-paginated, newest first. Market categories come from provider-backed market_canonical identity. Returns trades on sports and esports markets only (#27430): a trade on any other market is not listed, not counted toward has_more, and not returned by since. Deprecated alias of GET /api/v1/large-trades, kept live until its sunset on 2027-02-01 (#16304, #29351); every response carries Deprecation: @1790132400 (RFC 9745, 2026-09-23T03:00:00Z) and a Link to the successor (rel="successor-version") and to the versioning policy (rel="deprecation"). Sunset: Mon, 01 Feb 2027 00:00:00 GMT (RFC 8594) names that date, after which the path answers 410 Gone with error.code endpoint_retired. The response adds a top-level data_quality object beside data, grouping alert, trade, trader, ranking, market, and volume fields by their database writer. whale_alerts.inserted_xid is reported as unknown because it is a transaction identifier rather than a timestamp. Its stored clocks are part of the ETag; meta continues to hold transport cache facts. Pass since to poll for new trades: the answer holds only the trades recorded after the one you name, and with If-None-Match a poll that finds none is 304 with an empty body.

Source

pub async fn list_whale_trades_with_etag( &self, params: &ListWhaleTradesParams, ) -> Result<Tagged<ListWhaleTradesResponse>>

List whale trades, with the response’s ETag.

GET /api/v1/whale-trades

Self::list_whale_trades, returning the ETag validator the response carried beside the decoded body. Send it back as if_none_match on the next read: an unchanged resource answers Error::NotModified { etag }.

Source

pub async fn list_large_trade_history( &self, params: &ListLargeTradeHistoryParams, ) -> Result<ListLargeTradeHistoryResponse>

Replay historical large trades

GET /api/v1/large-trades/history

Returns historical large trades from local whale_alerts rows, not request-time provider fetches. Filter by condition_id, trader, category, minimum grade, persisted suspicion, platform, and RFC3339 from/to windows. All filters are pushed into SQL before LIMIT, every request uses SQL-backed limit + 1 pagination, and results are ordered newest first by traded_at desc, id desc. Metadata exposes local_replay source and best_effort completeness. Returns trades on sports and esports markets only (#27430), across the whole archive: a stored trade on any other market is left out before LIMIT. POINT IN TIME: signal_score, trader.grade and the min_grade filter carry today’s values on every row however old, so a backtest that selects by them selects wallets on what they did after the trade. The point-in-time fields are recorded_signal_score (from 2026-08-03T11:59Z; null before, and never backfilled, because the trader statistics it reads at insert were not kept for older rows) and trader.grade_at_trade with trader.grade_at_trade_status (from 2026-09-19T23:00Z; unknown before). CAPTURE RULES changed over the archive’s life: rows before 2026-02-02 are sparse (at most a few hundred a month); from 2026-02-02 the floor was 3,000 USD (1.4% of rows through 2026-07-05 are smaller) and trades at any price were kept; from 2026-07-06 a trade is kept at 10,000 USD or more (1,000 USD in earnings markets) and only when priced below 0.97 (0.99 in earnings markets). Pass min_size=10000 for one size rule across the whole range; monthly row counts still follow the sports calendar. Until 2026-07-17 one match could be stored twice, once per wallet: from 2026-05-01 to 2026-07-17, 27.7% of rows at 10,000 USD or more share a transaction and market with another stored wallet, almost always a Yes buyer and a No buyer filled against each other. From 2026-07-18 a row is the taker’s side only. Before 2026-05 the transaction hash is mostly absent, so the share cannot be measured there. From 2026-09-23 a fill must ALSO be at least 0.1% of its market’s recorded traded volume, Polymarket’s own share count, so a $10,000 print that lands in a market which has already traded tens of millions of shares is no longer kept; rows written before that date were not re-filtered. The response adds a top-level data_quality object beside data, grouping alert, trade, trader, ranking, market, and volume fields by their database writer. whale_alerts.inserted_xid is reported as unknown because it is a transaction identifier rather than a timestamp. Its stored clocks are part of the ETag; meta continues to hold transport cache facts.

Source

pub async fn list_large_trade_history_with_etag( &self, params: &ListLargeTradeHistoryParams, ) -> Result<Tagged<ListLargeTradeHistoryResponse>>

Replay historical large trades, with the response’s ETag.

GET /api/v1/large-trades/history

Self::list_large_trade_history, returning the ETag validator the response carried beside the decoded body. Send it back as if_none_match on the next read: an unchanged resource answers Error::NotModified { etag }.

Source

pub async fn list_whale_trade_history( &self, params: &ListWhaleTradeHistoryParams, ) -> Result<ListWhaleTradeHistoryResponse>

Replay historical whale trades

GET /api/v1/whale-trades/history

Returns historical whale trades from local whale_alerts rows, not request-time provider fetches. Filter by condition_id, trader, category, minimum grade, persisted suspicion, platform, and RFC3339 from/to windows. All filters are pushed into SQL before LIMIT, every request uses SQL-backed limit + 1 pagination, and results are ordered newest first by traded_at desc, id desc. Metadata exposes local_replay source and best_effort completeness. Returns trades on sports and esports markets only (#27430), across the whole archive: a stored trade on any other market is left out before LIMIT. Deprecated alias of GET /api/v1/large-trades/history, kept live until its sunset on 2027-02-01 (#16304, #29351); every response carries Deprecation: @1790132400 (RFC 9745, 2026-09-23T03:00:00Z) and a Link to the successor (rel="successor-version") and to the versioning policy (rel="deprecation"). Sunset: Mon, 01 Feb 2027 00:00:00 GMT (RFC 8594) names that date, after which the path answers 410 Gone with error.code endpoint_retired. The response adds a top-level data_quality object beside data, grouping alert, trade, trader, ranking, market, and volume fields by their database writer. whale_alerts.inserted_xid is reported as unknown because it is a transaction identifier rather than a timestamp. Its stored clocks are part of the ETag; meta continues to hold transport cache facts.

Source

pub async fn list_whale_trade_history_with_etag( &self, params: &ListWhaleTradeHistoryParams, ) -> Result<Tagged<ListWhaleTradeHistoryResponse>>

Replay historical whale trades, with the response’s ETag.

GET /api/v1/whale-trades/history

Self::list_whale_trade_history, returning the ETag validator the response carried beside the decoded body. Send it back as if_none_match on the next read: an unchanged resource answers Error::NotModified { etag }.

Source

pub async fn get_large_trade( &self, id: &str, params: &GetLargeTradeParams, ) -> Result<GetLargeTradeResponse>

Get large trade by ID

GET /api/v1/large-trades/{id}

Returns one large trade by raw whale_alerts.id or the wt_-prefixed id emitted by list and history responses.

Source

pub async fn get_large_trade_with_etag( &self, id: &str, params: &GetLargeTradeParams, ) -> Result<Tagged<GetLargeTradeResponse>>

Get large trade by ID, with the response’s ETag.

GET /api/v1/large-trades/{id}

Self::get_large_trade, returning the ETag validator the response carried beside the decoded body. Send it back as if_none_match on the next read: an unchanged resource answers Error::NotModified { etag }.

Source

pub async fn get_whale_trade( &self, id: &str, params: &GetWhaleTradeParams, ) -> Result<GetWhaleTradeResponse>

Get whale trade by ID

GET /api/v1/whale-trades/{id}

Returns one whale trade by raw whale_alerts.id or the wt_-prefixed id emitted by list and history responses. Deprecated alias of GET /api/v1/large-trades/{id}, kept live until its sunset on 2027-02-01 (#16304, #29351); every response carries Deprecation: @1790132400 (RFC 9745, 2026-09-23T03:00:00Z) and a Link to the successor (rel="successor-version") and to the versioning policy (rel="deprecation"). Sunset: Mon, 01 Feb 2027 00:00:00 GMT (RFC 8594) names that date, after which the path answers 410 Gone with error.code endpoint_retired.

Source

pub async fn get_whale_trade_with_etag( &self, id: &str, params: &GetWhaleTradeParams, ) -> Result<Tagged<GetWhaleTradeResponse>>

Get whale trade by ID, with the response’s ETag.

GET /api/v1/whale-trades/{id}

Self::get_whale_trade, returning the ETag validator the response carried beside the decoded body. Send it back as if_none_match on the next read: an unchanged resource answers Error::NotModified { etag }.

Source

pub async fn list_large_trade_counterparty_executions( &self, id: &str, snapshot_id: &str, params: &ListLargeTradeCounterpartyExecutionsParams, ) -> Result<ListLargeTradeCounterpartyExecutionsResponse>

Page counterparty executions

GET /api/v1/large-trades/{id}/counterparties/executions

Returns a bounded execution page from the immutable snapshot emitted by large-trade detail. A stale or changed snapshot returns a cursor-expired error so clients restart from detail.

Source

pub async fn list_large_trade_counterparty_executions_with_etag( &self, id: &str, snapshot_id: &str, params: &ListLargeTradeCounterpartyExecutionsParams, ) -> Result<Tagged<ListLargeTradeCounterpartyExecutionsResponse>>

Page counterparty executions, with the response’s ETag.

GET /api/v1/large-trades/{id}/counterparties/executions

Self::list_large_trade_counterparty_executions, returning the ETag validator the response carried beside the decoded body. Send it back as if_none_match on the next read: an unchanged resource answers Error::NotModified { etag }.

Source

pub async fn list_whale_trade_counterparty_executions( &self, id: &str, snapshot_id: &str, params: &ListWhaleTradeCounterpartyExecutionsParams, ) -> Result<ListWhaleTradeCounterpartyExecutionsResponse>

Page counterparty executions

GET /api/v1/whale-trades/{id}/counterparties/executions

Returns a bounded execution page from the immutable snapshot emitted by whale-trade detail. A stale or changed snapshot returns a cursor-expired error so clients restart from detail. Deprecated alias of GET /api/v1/large-trades/{id}/counterparties/executions, kept live until its sunset on 2027-02-01 (#16304, #29351); every response carries Deprecation: @1790132400 (RFC 9745, 2026-09-23T03:00:00Z) and a Link to the successor (rel="successor-version") and to the versioning policy (rel="deprecation"). Sunset: Mon, 01 Feb 2027 00:00:00 GMT (RFC 8594) names that date, after which the path answers 410 Gone with error.code endpoint_retired.

Source

pub async fn list_whale_trade_counterparty_executions_with_etag( &self, id: &str, snapshot_id: &str, params: &ListWhaleTradeCounterpartyExecutionsParams, ) -> Result<Tagged<ListWhaleTradeCounterpartyExecutionsResponse>>

Page counterparty executions, with the response’s ETag.

GET /api/v1/whale-trades/{id}/counterparties/executions

Self::list_whale_trade_counterparty_executions, returning the ETag validator the response carried beside the decoded body. Send it back as if_none_match on the next read: an unchanged resource answers Error::NotModified { etag }.

Source

pub async fn list_large_trade_counterparty_makers( &self, id: &str, execution_id: &str, snapshot_id: &str, params: &ListLargeTradeCounterpartyMakersParams, ) -> Result<ListLargeTradeCounterpartyMakersResponse>

Page maker counterparties

GET /api/v1/large-trades/{id}/counterparties/executions/{execution_id}/makers

Returns a bounded maker-wallet page for one exact execution. Percentages keep the complete execution denominator across pages.

Source

pub async fn list_large_trade_counterparty_makers_with_etag( &self, id: &str, execution_id: &str, snapshot_id: &str, params: &ListLargeTradeCounterpartyMakersParams, ) -> Result<Tagged<ListLargeTradeCounterpartyMakersResponse>>

Page maker counterparties, with the response’s ETag.

GET /api/v1/large-trades/{id}/counterparties/executions/{execution_id}/makers

Self::list_large_trade_counterparty_makers, returning the ETag validator the response carried beside the decoded body. Send it back as if_none_match on the next read: an unchanged resource answers Error::NotModified { etag }.

Source

pub async fn list_whale_trade_counterparty_makers( &self, id: &str, execution_id: &str, snapshot_id: &str, params: &ListWhaleTradeCounterpartyMakersParams, ) -> Result<ListWhaleTradeCounterpartyMakersResponse>

Page maker counterparties

GET /api/v1/whale-trades/{id}/counterparties/executions/{execution_id}/makers

Returns a bounded maker-wallet page for one exact execution. Percentages keep the complete execution denominator across pages. Deprecated alias of GET /api/v1/large-trades/{id}/counterparties/executions/{execution_id}/makers, kept live until its sunset on 2027-02-01 (#16304, #29351); every response carries Deprecation: @1790132400 (RFC 9745, 2026-09-23T03:00:00Z) and a Link to the successor (rel="successor-version") and to the versioning policy (rel="deprecation"). Sunset: Mon, 01 Feb 2027 00:00:00 GMT (RFC 8594) names that date, after which the path answers 410 Gone with error.code endpoint_retired.

Source

pub async fn list_whale_trade_counterparty_makers_with_etag( &self, id: &str, execution_id: &str, snapshot_id: &str, params: &ListWhaleTradeCounterpartyMakersParams, ) -> Result<Tagged<ListWhaleTradeCounterpartyMakersResponse>>

Page maker counterparties, with the response’s ETag.

GET /api/v1/whale-trades/{id}/counterparties/executions/{execution_id}/makers

Self::list_whale_trade_counterparty_makers, returning the ETag validator the response carried beside the decoded body. Send it back as if_none_match on the next read: an unchanged resource answers Error::NotModified { etag }.

Source

pub async fn list_leaderboard( &self, params: &ListLeaderboardParams, ) -> Result<ListLeaderboardResponse>

Get trader leaderboard

GET /api/v1/leaderboard

Returns ranked traders (grades S/A/B), by default in the board’s own order: score descending, with a fixed tiebreak so the order never changes between reads of one ranking generation. sort, dir and activity reorder and scope the same wallets the way the site’s leaderboard does (since 2026-11-02, #32237). Supports cursor pagination and optional category/strategy filters. A sorted or activity-scoped page reads the serving projection only; while that projection is older than its 300 s freshness bound it answers 503 rate_limit_unavailable with error.reason read_model_warming and Retry-After, and the default order keeps serving.

Source

pub async fn list_leaderboard_with_etag( &self, params: &ListLeaderboardParams, ) -> Result<Tagged<ListLeaderboardResponse>>

Get trader leaderboard, with the response’s ETag.

GET /api/v1/leaderboard

Self::list_leaderboard, returning the ETag validator the response carried beside the decoded body. Send it back as if_none_match on the next read: an unchanged resource answers Error::NotModified { etag }.

Source

pub async fn get_pick_of_the_day( &self, params: &GetPickOfTheDayParams, ) -> Result<GetPickOfTheDayResponse>

Get today’s or a past day’s Pick of the Day

GET /api/v1/pick-of-the-day

Returns entitled published picks for the current product day, or for the past product day named by date. Pro includes five selections in total: the designated free selection and the first four non-free selections in publication order. Max opens every available selection, up to twenty-five. A day can include multiple selections from the same game. Same-game picks share exposure and need not be independent; retain each pick_id instead of deduplicating by game. Both read resolved picks. Publication prices and backing are frozen. Without date, a prior day never appears here.

With date (YYYY-MM-DD, America/New_York, before today), the response is that day’s slate as the dated web page shows it, under the same tier rule: resolved picks are open to both tiers, and unresolved picks outside the tier appear as locked_picks. A past day carries no scheduled_picks. A past day with no published pick returns a final 404 not_found with error.param=date and no retry guidance. Today’s date is the same read as no date; a date after today returns 400 bad_request with error.param=date. The archive lists every published pick with its pick_date.

Stable pick_id values identify selections. publication_order describes presentation and is_free_selection describes access; neither is a quality rating. Standing selections retain their identity, slot, and release schedule. Later additions fill available slots. Historical IDs, order, and proof bytes remain unchanged.

Unauthorized unresolved selections appear only as identity-free locked_picks containing pick_rank and required_tier. When only locked selections are published, HTTP 200 carries state=none, picks=[], pick_count=0, and an Upgrade to Max message. Proof warming applies only to entitled picks.

scheduled_picks contains only entitled slots and retains required release_at and kickoff. Unauthorized scheduled selections expose no game identity or release time.

For today, when no active pick is available, including after cancelled recommendations are withdrawn, HTTP 404 carries error.code="not_found" and error.reason="pick_not_released". Branch on the reason and schedule one request using Retry-After or error.retry_at instead of polling. This advisory retry does not reveal an unauthorized game’s release time.

Final sporting scores are retained for resolved picks after the event is confirmed ended and both sides have verified scores. A result without a verified final stays unavailable; a win/loss outcome alone does not establish a sporting score. Existing sports_context fields and return calculations are unchanged.

Withdrawn picks are excluded from current and dated recommendations, the archive, and its statistics, even after market settlement. The public ledger retains every published commitment under its original identity and proof bytes. Provider settlement still determines the recorded result. Request and response fields are unchanged.

Source

pub async fn get_pick_of_the_day_with_etag( &self, params: &GetPickOfTheDayParams, ) -> Result<Tagged<GetPickOfTheDayResponse>>

Get today’s or a past day’s Pick of the Day, with the response’s ETag.

GET /api/v1/pick-of-the-day

Self::get_pick_of_the_day, returning the ETag validator the response carried beside the decoded body. Send it back as if_none_match on the next read: an unchanged resource answers Error::NotModified { etag }.

Source

pub async fn get_pick_of_the_day_archive( &self, params: &GetPickOfTheDayArchiveParams, ) -> Result<GetPickOfTheDayArchiveResponse>

Get the Pick of the Day track record

GET /api/v1/pick-of-the-day/archive

Returns published picks that have not been withdrawn, with their outcome, modeled return, unit score, closing-line value, and cumulative record. Resolved game details and results are public. For unauthorized unresolved selections, game identity, category, image, publication time, and backed facts are omitted; required_tier and backed_side_locked provide an upgrade action. Pro includes the designated free selection and the first four non-free selections in publication order, five in total. Max opens every available selection, up to twenty-five.

Stable pick_id values identify selections. Historical picks retain their original IDs, order, and proof bytes. publication_order describes presentation and is_free_selection records access designation; neither is a quality rating. New selections use a neutral presentation order, while standing selections retain their identity, slot, and release schedule.

Each row carries measured CLV or the reason it was not measured. Coverage is the share of resolved picks published before kickoff that have measured CLV. A post-kickoff publication is not_applicable.

Withdrawn recommendations are excluded from archive rows, daily counts, and all track-record statistics, including CLV, financial aggregates, and the cumulative series. Later market settlement does not restore them to the archive. The public ledger retains every published commitment under its original pick_id and proof bytes. Request and response fields are unchanged.

Source

pub async fn get_pick_of_the_day_archive_with_etag( &self, params: &GetPickOfTheDayArchiveParams, ) -> Result<Tagged<GetPickOfTheDayArchiveResponse>>

Get the Pick of the Day track record, with the response’s ETag.

GET /api/v1/pick-of-the-day/archive

Self::get_pick_of_the_day_archive, returning the ETag validator the response carried beside the decoded body. Send it back as if_none_match on the next read: an unchanged resource answers Error::NotModified { etag }.

Source

pub async fn get_pick_of_the_day_stats( &self, params: &GetPickOfTheDayStatsParams, ) -> Result<GetPickOfTheDayStatsResponse>

Get the Pick of the Day track-record breakdown

GET /api/v1/pick-of-the-day/stats

The figures behind the stats page, computed from the same rows the archive publishes: the hit rate, the win rate the market expected at publication, flat-stake outcome stats (drawdown, rolling form, profit factor, concentration), and buckets by day, sport, category, access group, month, entry-price band, market type and totals side, plus streaks, records and every void pick. Withdrawn picks are outside every figure, as in the archive. Pro reads the Pro view; Max counts every selection whose backed side Max sees.

Source

pub async fn get_pick_of_the_day_stats_with_etag( &self, params: &GetPickOfTheDayStatsParams, ) -> Result<Tagged<GetPickOfTheDayStatsResponse>>

Get the Pick of the Day track-record breakdown, with the response’s ETag.

GET /api/v1/pick-of-the-day/stats

Self::get_pick_of_the_day_stats, returning the ETag validator the response carried beside the decoded body. Send it back as if_none_match on the next read: an unchanged resource answers Error::NotModified { etag }.

Source

pub async fn get_pick_of_the_day_ledger( &self, params: &GetPickOfTheDayLedgerParams, ) -> Result<GetPickOfTheDayLedgerResponse>

Get the Pick of the Day commitment ledger

GET /api/v1/pick-of-the-day/ledger

Returns the pre-game commitment for every published pick, so the public track record can be checked by someone who was not watching when the pick dropped.

One entry per (pick_date, pick_rank), ascending by pick_date then pick_rank, in one of three states. sealed is an unresolved pick: the hash, the algorithm, the seal instant, and nothing that states a side or a price. opened is a settled pick: the nonce and the exact canonical payload the hash was taken over. uncommitted is a pick with no commitment – published before the scheme existed, or one that reached kickoff unsealed – named rather than omitted.

To verify an opened entry: serialize nothing. Take the bytes of the payload object exactly as received, append the commitment_nonce decoded from hex, and sha256 the result; it equals commitment_hash. The payload is canonical JSON – keys sorted by UTF-8 byte value, no insignificant whitespace, decimals as strings at full stored precision, timestamps whole-second UTC with a literal Z – and it is served byte for byte as it was hashed.

This is a proof contract, not the archive’s display contract: nothing here is formatted for rendering, so an entry changes only when the pick does. Unresolved ledger entries omit game identity and kickoff; sealed_at remains public commitment provenance. A commitment is never written after kickoff and never rewritten by an outcome correction; resolved_at moving under an unchanged commitment_hash is a corrected market re-mapping an already-settled pick.

Withdrawal from current recommendations or the product archive does not remove a published ledger entry. The ledger retains every published commitment under its original identity and proof bytes; canonical market settlement still determines the recorded result.

Source

pub async fn get_pick_of_the_day_ledger_with_etag( &self, params: &GetPickOfTheDayLedgerParams, ) -> Result<Tagged<GetPickOfTheDayLedgerResponse>>

Get the Pick of the Day commitment ledger, with the response’s ETag.

GET /api/v1/pick-of-the-day/ledger

Self::get_pick_of_the_day_ledger, returning the ETag validator the response carried beside the decoded body. Send it back as if_none_match on the next read: an unchanged resource answers Error::NotModified { etag }.

List trending wallets

GET /api/v1/leaderboard/trending

Returns wallets ranked by Polymarket weekly/monthly P&L (Polymarket-only discovery), with opaque page-cursor pagination. trending_pnl_usd and the by-PNL row order come from Polymarket’s canonical leaderboard (data-api.polymarket.com/v2/leaderboard?time_period=week|month&sort_by=PNL), not a locally summed realized-leaf total. Each row also carries window volume, distinct markets, grade, hot-streak tier, and daily_pnl_series: the wallet’s sports and esports realized P&L on each UTC day of the window, with daily_pnl_series_status ready or pending (pending: the series is empty and never means zero). It leaves out non-sports markets, so it does not sum to trending_pnl_usd. The response comes from a retained Redis board: meta.cached is true, and meta.cache_age_s reports the original board age when known (omitted when unknown). A backend read of a board at least 120 seconds old attempts a detached refresh; after a quiet spell a product board may be up to a day old. HTTP freshness ends at 120 seconds; max-age plus stale-while-revalidate cannot exceed the remaining original retention. Unknown age grants no cache window. A true cold read returns 503 (warming, retry-after), never a fabricated ranking. Polymarket-only: the read model filters platform = ‘polymarket’. Cursors are bound to the effective limit, window and ranked-board generation; a changed board or request scope returns 400 with error.reason=cursor_expired, and legacy page-only cursors must restart from page one.

List trending wallets, with the response’s ETag.

GET /api/v1/leaderboard/trending

Self::list_trending_wallets, returning the ETag validator the response carried beside the decoded body. Send it back as if_none_match on the next read: an unchanged resource answers Error::NotModified { etag }.

Source

pub async fn list_leaderboard_leagues( &self, params: &ListLeaderboardLeaguesParams, ) -> Result<ListLeaderboardLeaguesResponse>

List the leagues that have a leaderboard

GET /api/v1/leaderboard/leagues

Every league with its own skill leaderboard, grouped by sport, most ranked wallets first. Each row’s sport and slug are the {sport} path segment and league value of GET /api/v1/leaderboard/sports/{sport}. A league is listed once its edge sort ranks at least min_ranked wallets. 503 read_model_warming while the league ranking is missing or past its refresh window.

Source

pub async fn list_leaderboard_leagues_with_etag( &self, params: &ListLeaderboardLeaguesParams, ) -> Result<Tagged<ListLeaderboardLeaguesResponse>>

List the leagues that have a leaderboard, with the response’s ETag.

GET /api/v1/leaderboard/leagues

Self::list_leaderboard_leagues, returning the ETag validator the response carried beside the decoded body. Send it back as if_none_match on the next read: an unchanged resource answers Error::NotModified { etag }.

Source

pub async fn get_sport_leaderboard( &self, sport: &str, params: &GetSportLeaderboardParams, ) -> Result<GetSportLeaderboardResponse>

Get a sport or league leaderboard

GET /api/v1/leaderboard/sports/{sport}

One sport’s skill leaderboard, or with league one league’s, the board the site’s sport pages show. Wallets need min_resolved resolved markets in the sport or league. sort=edge (the default) ranks edge_skill, the edge over the price paid pulled toward the board’s edge_average by how much of the record is noise; a wallet without a ranked edge sorts after the ranked ones. sort=pnl ranks realized P&L, and sort=win_rate the Wilson lower bound of the win rate. Boards are ranked to 100 wallets; limit returns the head of that list. A sport slug outside sports and esports is 404 with error.param=sport; a league slug the sport has no board for is 404 with error.param=league (GET /api/v1/leaderboard/leagues lists them). 503 read_model_warming while the ranking is being rebuilt.

Source

pub async fn get_sport_leaderboard_with_etag( &self, sport: &str, params: &GetSportLeaderboardParams, ) -> Result<Tagged<GetSportLeaderboardResponse>>

Get a sport or league leaderboard, with the response’s ETag.

GET /api/v1/leaderboard/sports/{sport}

Self::get_sport_leaderboard, returning the ETag validator the response carried beside the decoded body. Send it back as if_none_match on the next read: an unchanged resource answers Error::NotModified { etag }.

Source

pub async fn list_category_leaders( &self, params: &ListCategoryLeadersParams, ) -> Result<ListCategoryLeadersResponse>

List every category’s top edge leaders

GET /api/v1/leaderboard/categories

Every sports and esports category’s top three edge leaders in one read, the board the site’s categories index shows. In each category, wallets are ranked as GET /api/v1/leaderboard/categories/{slug} ranks them, by the 95% lower bound of their edge over the price they paid; only wallets whose lower bound is above zero are listed, so a category can carry fewer than three, or none. Rows come by category, then rank, in the same row shape as the per-category board. Not paginated. 503 read_model_warming, with Retry-After, while the category-skill ranking is being rebuilt or its model is degraded: never an empty 200.

Source

pub async fn list_category_leaders_with_etag( &self, params: &ListCategoryLeadersParams, ) -> Result<Tagged<ListCategoryLeadersResponse>>

List every category’s top edge leaders, with the response’s ETag.

GET /api/v1/leaderboard/categories

Self::list_category_leaders, returning the ETag validator the response carried beside the decoded body. Send it back as if_none_match on the next read: an unchanged resource answers Error::NotModified { etag }.

Source

pub async fn get_category_leaderboard( &self, slug: &str, params: &GetCategoryLeaderboardParams, ) -> Result<GetCategoryLeaderboardResponse>

Get a category’s edge leaderboard

GET /api/v1/leaderboard/categories/{slug}

One sports or esports category’s edge leaderboard, the board the site’s category pages show. Wallets are ranked by edge_lower_bound, the 95% lower bound of their edge over the price they paid in the category, counted over independent events (independent_events breaks ties, then the wallet). Only wallets whose lower bound is above zero are listed; total counts them across every page. active_within keeps wallets that traded in the category within 7d, 30d or 90d, and min_events sets the independent events a wallet needs. A sibling of GET /api/v1/leaderboard and GET /api/v1/leaderboard/sports/{sport}, which rank the grade score and the settled-record edge; this ranking is the category-skill model’s lower bound, with its own cursor. A slug outside sports and esports is 404 with error.param=slug. 503 read_model_warming, with Retry-After, while the category-skill ranking is being rebuilt or its model is degraded: never an empty 200.

Source

pub async fn get_category_leaderboard_with_etag( &self, slug: &str, params: &GetCategoryLeaderboardParams, ) -> Result<Tagged<GetCategoryLeaderboardResponse>>

Get a category’s edge leaderboard, with the response’s ETag.

GET /api/v1/leaderboard/categories/{slug}

Self::get_category_leaderboard, returning the ETag validator the response carried beside the decoded body. Send it back as if_none_match on the next read: an unchanged resource answers Error::NotModified { etag }.

Source

pub async fn search_markets( &self, q: &str, params: &SearchMarketsParams, ) -> Result<SearchMarketsResponse>

Search markets

GET /api/v1/markets/search

Search prediction markets by keyword. Returns one representative market per matching event, preferring the market whose title is q, then a winner market over a spread, total or prop, with status, category, platform metadata and the event_slug. Cursor pagination advances over grouped market results rather than raw sub-market rows. To list every market of a game, pass the event_slug to GET /api/v1/games/{event_slug}/markets. Returns sports and esports markets only (#27411). Searches every unresolved game market (a game’s winner market, or an outright) whether or not it has traded, and every market that has had a single trade of $10,000 or more, resolved or not; a resolved market that never had such a trade is not returned (#32215). Events rank by title similarity to q, a nearer game date first among similar matches; an event holding a market whose title is q (case-insensitive, with or without its closing question mark) ranks first whatever its date, so pasting a market’s title returns that market first (#32280).

Source

pub async fn search_content( &self, q: &str, params: &SearchContentParams, ) -> Result<SearchContentResponse>

Search editorial content

GET /api/v1/content/search

Search 0xinsider editorial content by keyword. Returns backend-owned learn, glossary, comparison, research, and trading-strategy items with canonical URLs. Opaque ranking scores are not exposed.

Source

pub async fn explore_markets( &self, params: &ExploreMarketsParams, ) -> Result<ExploreMarketsResponse>

Explore markets

GET /api/v1/markets/explore

Browse whale-active titled markets with category, platform, status, and keyword filters. Explore is Polymarket-only: the platform parameter is accepted for backward-compatibility but every request returns Polymarket markets. Paginates visible discovery entries rather than raw market rows, returns live category/platform facets alongside grouped event clusters or standalone markets, and includes total on the first page only. Each grouped event contains at most 12 markets, retaining the selected representative within that cap. Categories come straight from provider metadata (Polymarket Gamma) and facets are flat value/label/count rows. Returns sports and esports markets only (#27411).

Source

pub async fn explore_markets_with_etag( &self, params: &ExploreMarketsParams, ) -> Result<Tagged<ExploreMarketsResponse>>

Explore markets, with the response’s ETag.

GET /api/v1/markets/explore

Self::explore_markets, returning the ETag validator the response carried beside the decoded body. Send it back as if_none_match on the next read: an unchanged resource answers Error::NotModified { etag }.

Source

pub async fn list_smart_money_flows( &self, params: &ListSmartMoneyFlowsParams, ) -> Result<ListSmartMoneyFlowsResponse>

List ranked sharp-money flows (deprecated alias)

GET /api/v1/markets/smart-money-flows

Ranks sports and esports markets (#27306) by absolute net S/A/B-grade whale flow over a requested timeframe. Use this discovery endpoint to answer where sharp money is flowing before drilling into a specific market with /api/v1/market/{condition_id}/flow. Pagination is anchored by an opaque cursor carrying the first-page as_of timestamp, normalized effective filters, ranking revision and aggregate collection revision; a changed filter or collection returns cursor_expired so callers request the first page again. Deprecated alias of GET /api/v1/markets/sharp-money-flows, kept live until its sunset on 2027-02-01 (#29351); every response carries Deprecation: @1783276950 (RFC 9745, 2026-07-05T18:42:30Z) and a Link to the successor (rel="successor-version") and to the versioning policy (rel="deprecation"). Sunset: Mon, 01 Feb 2027 00:00:00 GMT (RFC 8594) names that date, after which the path answers 410 Gone with error.code endpoint_retired.

Source

pub async fn list_smart_money_flows_with_etag( &self, params: &ListSmartMoneyFlowsParams, ) -> Result<Tagged<ListSmartMoneyFlowsResponse>>

List ranked sharp-money flows (deprecated alias), with the response’s ETag.

GET /api/v1/markets/smart-money-flows

Self::list_smart_money_flows, returning the ETag validator the response carried beside the decoded body. Send it back as if_none_match on the next read: an unchanged resource answers Error::NotModified { etag }.

Source

pub async fn list_sharp_money_flows( &self, params: &ListSharpMoneyFlowsParams, ) -> Result<ListSharpMoneyFlowsResponse>

List ranked sharp-money flows

GET /api/v1/markets/sharp-money-flows

Canonical path for ranked sharp-money flows; /api/v1/markets/smart-money-flows remains live as a deprecated byte-identical alias of it. Ranks sports and esports markets (#27306) by absolute net S/A/B-grade whale flow over a requested timeframe. Use this discovery endpoint to answer where sharp money is flowing before drilling into a specific market with /api/v1/market/{condition_id}/flow. Pagination is anchored by an opaque cursor carrying the first-page as_of timestamp, normalized effective filters, ranking revision and aggregate collection revision; a changed filter or collection returns cursor_expired so callers request the first page again.

Source

pub async fn list_sharp_money_flows_with_etag( &self, params: &ListSharpMoneyFlowsParams, ) -> Result<Tagged<ListSharpMoneyFlowsResponse>>

List ranked sharp-money flows, with the response’s ETag.

GET /api/v1/markets/sharp-money-flows

Self::list_sharp_money_flows, returning the ETag validator the response carried beside the decoded body. Send it back as if_none_match on the next read: an unchanged resource answers Error::NotModified { etag }.

Source

pub async fn get_coverage(&self) -> Result<GetCoverageResponse>

Which reads the API serves for Polymarket

GET /api/v1/coverage

Unauthenticated discovery endpoint that declares which V1 data surfaces the API serves for Polymarket, the one venue it covers. Every capability reports supported. Coverage is sports and esports markets only (#27299): a market outside them answers 404 not_found with error.reason market_out_of_scope, and a category filter outside them answers 400 bad_request with param category on the reads that filter by it: large trades and their history, large positions, the leaderboard, market search and explore, and sharp-money flows. GET /api/v1/positions and GET /api/v1/trader/{address}/categories do not refuse a category value (#27581). Canonical since #16315; GET /api/v1/platforms is its deprecated alias with the same body.

Source

pub async fn get_platforms(&self) -> Result<GetPlatformsResponse>

Get platform capability matrix

GET /api/v1/platforms

Deprecated since #16315: use GET /api/v1/coverage, which serves the same body. This path stays live until its sunset on 2027-02-01 (#29351) and answers with Deprecation, Sunset and successor Link headers; from that date it answers 410 Gone with error.code endpoint_retired. Unauthenticated discovery endpoint that declares which V1 data surfaces the API serves for Polymarket, the one venue it covers. Every capability reports supported. Coverage is sports and esports markets only (#27299): a market outside them answers 404 not_found with error.reason market_out_of_scope, and a category filter outside them answers 400 bad_request with param category on the reads that filter by it: large trades and their history, large positions, the leaderboard, market search and explore, and sharp-money flows. GET /api/v1/positions and GET /api/v1/trader/{address}/categories do not refuse a category value (#27581).

Source

pub async fn get_market_holders( &self, condition_id: &str, params: &GetMarketHoldersParams, ) -> Result<GetMarketHoldersResponse>

List a market’s graded holders

GET /api/v1/market/{condition_id}/holders

The graded holder roster of one market, the list a Pick of the Day shows for its market, for any Polymarket market: every S/A/B wallet with open shares on either outcome, from a complete provider holder scan, each with its shares, Polymarket’s own currentValue for the leg, its grade, its win record in the market’s category, and its badges. Both outcomes are listed; a wallet holding both is listed once on its net side. Ordered by current_value_usd DESC, then shares DESC, then address. The roster is computed at most once a minute per market and shared by every caller; a page is cut from that roster, so paging is cheap and total is exact. An answer whose scan saw no wallet at all (scan.wallet_count 0), on an open market before its end date, is kept for up to an hour while no trade in that market is seen after the scan; meta.cache_age_s gives its age. The route never widens past the graded cohort: scan.wallet_count reports every wallet the walk saw, graded or not. An incomplete, unstable or failed scan is a 503 with Retry-After, never a shorter list. Cold reads are shared per account (#28752): a request for a market with no cached roster and no cached complete scan walks Polymarket, and an account may start 6 such walks a minute, in bursts of up to 400. Over that share the answer is 429 rate_limited with error.reason cold_read_limited and Retry-After, and nothing is walked; a market whose roster or scan is cached, including one another caller just read, still answers and spends no share. A complete scan in which no S, A or B wallet holds either outcome is a 200 with an empty holders list and zero totals; retrying cannot change it. The cohort is S, A and B by the wallet’s current grade (traders.latest_grade); a C, D, F or ungraded wallet is never listed here and appears only inside scan.wallet_count. A count here will not match GET /api/v1/positions for the same market, and the cohort is only one of three reasons: /positions reads the wallet_positions mirror rather than this live provider scan, and without a wallet its min_size defaults to 100 USD, so it drops the smaller legs this route keeps. Read a difference as all three, never as a grade count. This is current state and takes no as-of parameter: the roster is at most 60 s old over a provider walk at most 180 s old (an answer with no wallet at all, at most an hour), and scan.fetched_at is the only timestamp on it. For a point-in-time roster, snapshot this route yourself on your own schedule; the one frozen roster the API serves is display_holders on GET /api/v1/pick-of-the-day, for that pick’s market and backed side at publication.

Source

pub async fn get_market_holders_with_etag( &self, condition_id: &str, params: &GetMarketHoldersParams, ) -> Result<Tagged<GetMarketHoldersResponse>>

List a market’s graded holders, with the response’s ETag.

GET /api/v1/market/{condition_id}/holders

Self::get_market_holders, returning the ETag validator the response carried beside the decoded body. Send it back as if_none_match on the next read: an unchanged resource answers Error::NotModified { etag }.

Source

pub async fn get_market_top_holders( &self, condition_id: &str, params: &GetMarketTopHoldersParams, ) -> Result<GetMarketTopHoldersResponse>

List a market’s top holders, every grade

GET /api/v1/market/{condition_id}/top-holders

The largest holders of one market on each outcome, every grade from S to F and ungraded wallets included: the table the market’s event page shows under Top holders. Each outcome lists up to 100 wallets, largest shares first, from Polymarket’s holder board, with the provider’s current value, average entry price and position P&L for the leg, the wallet’s grade, its sports and esports realized P&L and volume, its win record in the market’s category, and its badges. A wallet holding both outcomes appears on each side it holds, with other_outcome_shares set. This is a bounded sample, not a census: totals.yes_count equal to totals.sample_cap means that many or more. For the complete S, A and B roster from a full holder scan, with exact counts and USD per grade, use GET /api/v1/market/{condition_id}/holders. The sample is the same one the event page reads and is shared by every caller. On an open market it is rebuilt at most every 30 s from Polymarket’s holder board, which is itself re-read at most every 120 s, so positions are usually up to about 150 s old: data.positions_as_of says when that board was fetched, data.computed_at when the sample was built and meta.cache_age_s the response’s age. Past the 30 s window a read gets the last sample with data.source_stale true while a new one builds, as long as its board is at most 300 s old; read again a few seconds later for the new sample. A resolved market’s sample is at most 300 s old and has no positions_as_of. Busy or unavailable holder data is a 503 with Retry-After, never an empty list. Current state only; there is no as-of parameter.

Source

pub async fn get_market_top_holders_with_etag( &self, condition_id: &str, params: &GetMarketTopHoldersParams, ) -> Result<Tagged<GetMarketTopHoldersResponse>>

List a market’s top holders, every grade, with the response’s ETag.

GET /api/v1/market/{condition_id}/top-holders

Self::get_market_top_holders, returning the ETag validator the response carried beside the decoded body. Send it back as if_none_match on the next read: an unchanged resource answers Error::NotModified { etag }.

Source

pub async fn get_market_flow( &self, condition_id: &str, params: &GetMarketFlowParams, ) -> Result<GetMarketFlowResponse>

Get a market’s flow and top positions

GET /api/v1/market/{condition_id}/flow

Canonical since #16312; GET /api/v1/market/{condition_id}/intel is its deprecated alias and serves the same body under object market_intel. One market’s flow and top positions: the signed flow of every tracked large trade in the window (net_flow_usd and its YES or NO direction), the large-trade count, gross buy and sell volumes, and the top five graded trader positions.

Source

pub async fn get_market_flow_with_etag( &self, condition_id: &str, params: &GetMarketFlowParams, ) -> Result<Tagged<GetMarketFlowResponse>>

Get a market’s flow and top positions, with the response’s ETag.

GET /api/v1/market/{condition_id}/flow

Self::get_market_flow, returning the ETag validator the response carried beside the decoded body. Send it back as if_none_match on the next read: an unchanged resource answers Error::NotModified { etag }.

Source

pub async fn get_market_intel( &self, condition_id: &str, params: &GetMarketIntelParams, ) -> Result<GetMarketIntelResponse>

Get market intelligence

GET /api/v1/market/{condition_id}/intel

Deprecated since #16312: use GET /api/v1/market/{condition_id}/flow, which serves the same body under object market_flow. This path stays live until its sunset on 2027-02-01 (#29351), keeps object market_intel, and answers with Deprecation, Sunset and successor Link headers; from that date it answers 410 Gone with error.code endpoint_retired. One market’s flow and top positions: the signed flow of every tracked large trade in the window (net_flow_usd and its YES or NO direction), the large-trade count, gross buy and sell volumes, and the top five graded trader positions.

Source

pub async fn get_market_intel_with_etag( &self, condition_id: &str, params: &GetMarketIntelParams, ) -> Result<Tagged<GetMarketIntelResponse>>

Get market intelligence, with the response’s ETag.

GET /api/v1/market/{condition_id}/intel

Self::get_market_intel, returning the ETag validator the response carried beside the decoded body. Send it back as if_none_match on the next read: an unchanged resource answers Error::NotModified { etag }.

Source

pub async fn batch_get_market_flow( &self, body: &BatchGetMarketFlowBody, ) -> Result<BatchGetMarketFlowResponse>

Batch market flow

POST /api/v1/markets/flow/batch

Returns each market’s flow and top positions for 1-25 raw provider condition_id values. Results preserve request order, duplicate inputs return duplicate rows, and each item is charged one batch item unit before execution. Do not pass prefixed mkt_ IDs; use market.condition_id from search or explore.

Source

pub async fn batch_get_market_intel( &self, body: &BatchGetMarketIntelBody, ) -> Result<BatchGetMarketIntelResponse>

Batch market intelligence

POST /api/v1/markets/intel/batch

Deprecated since #16312: use POST /api/v1/markets/flow/batch, which returns the same items under object market_flow_batch. This path stays live until its sunset on 2027-02-01 (#29351), keeps object market_intel_batch, and answers with Deprecation, Sunset and successor Link headers; from that date it answers 410 Gone with error.code endpoint_retired. Returns each market’s flow and top positions for 1-25 raw provider condition_id values. Results preserve request order, duplicate inputs return duplicate rows, and each item is charged one batch item unit before execution. Do not pass prefixed mkt_ IDs; use market.condition_id from search or explore.

Source

pub async fn get_market_snapshot( &self, condition_id: &str, params: &GetMarketSnapshotParams, ) -> Result<GetMarketSnapshotResponse>

Get market live snapshot

GET /api/v1/market/{condition_id}/snapshot

Provider-first market card snapshot with canonical identity, outcome labels, cached top-of-book when available, liquidity, live sports context, and explicit freshness/unavailable states.

Source

pub async fn get_market_snapshot_with_etag( &self, condition_id: &str, params: &GetMarketSnapshotParams, ) -> Result<Tagged<GetMarketSnapshotResponse>>

Get market live snapshot, with the response’s ETag.

GET /api/v1/market/{condition_id}/snapshot

Self::get_market_snapshot, returning the ETag validator the response carried beside the decoded body. Send it back as if_none_match on the next read: an unchanged resource answers Error::NotModified { etag }.

Source

pub async fn get_market_candles( &self, condition_id: &str, params: &GetMarketCandlesParams, ) -> Result<GetMarketCandlesResponse>

Get market OHLC price candles

GET /api/v1/market/{condition_id}/candles

Provider-first bucketed OHLC price candles for a market’s outcome tokens, derived from our stored copy of the Polymarket CLOB prices-history series (the same series the market-detail chart renders; covers open and resolved markets). Because the stored data is daily, a 1d bucket typically carries one point so its open/high/low/close all equal that day’s close; 1w aggregates real OHLC across the observed daily closes. No intraday fidelity is fabricated. Query field names and recognized values are URL-decoded; duplicate recognized fields use the last value and unknown fields are ignored. When both bounds are present, from must be less than or equal to to; malformed or inverted bounds answer 400.

Source

pub async fn get_market_candles_with_etag( &self, condition_id: &str, params: &GetMarketCandlesParams, ) -> Result<Tagged<GetMarketCandlesResponse>>

Get market OHLC price candles, with the response’s ETag.

GET /api/v1/market/{condition_id}/candles

Self::get_market_candles, returning the ETag validator the response carried beside the decoded body. Send it back as if_none_match on the next read: an unchanged resource answers Error::NotModified { etag }.

Source

pub async fn get_market_wallets_in_out( &self, condition_id: &str, params: &GetMarketWalletsInOutParams, ) -> Result<GetMarketWalletsInOutResponse>

Get a market’s net buyers and sellers

GET /api/v1/market/{condition_id}/wallets-in-out

One market’s net buyers and net sellers over the last 24 hours or 7 days, live: the body the 0xinsider market page’s wallets in/out panel reads for a Pro viewer, from the same aggregate and cache (reads are cached for up to 60 seconds). A wallet’s net is its buys minus its sells in the window across both outcomes, in USD of matched notional, fees excluded. summary counts every wallet that traded the market in the window; net_buyers and net_sellers list the top 25 on each side. A wallet that bought and sold the same amount nets zero and is on neither list.

Source

pub async fn get_market_wallets_in_out_with_etag( &self, condition_id: &str, params: &GetMarketWalletsInOutParams, ) -> Result<Tagged<GetMarketWalletsInOutResponse>>

Get a market’s net buyers and sellers, with the response’s ETag.

GET /api/v1/market/{condition_id}/wallets-in-out

Self::get_market_wallets_in_out, returning the ETag validator the response carried beside the decoded body. Send it back as if_none_match on the next read: an unchanged resource answers Error::NotModified { etag }.

Source

pub async fn list_suspicious_trades( &self, params: &ListSuspiciousTradesParams, ) -> Result<ListSuspiciousTradesResponse>

Get suspicious trades

GET /api/v1/suspicious-trades

Stored trades whose recorded suspicion score meets the live flag threshold. Evidence contains the scorer’s stored signals. Cursor-paginated by suspicion score. mode=live (default) uses fresh cached pages; mode=stable pins pagination to one published scoring generation and returns cursor_expired when scores or filters change. Sports and esports markets only (#27305): a flag on a market outside product scope is not listed, and the detail route answers it 404 not_found with error.reason=market_out_of_scope. Canonical since 2026-09-23; GET /api/v1/insider-radar is its deprecated alias.

Source

pub async fn list_suspicious_trades_with_etag( &self, params: &ListSuspiciousTradesParams, ) -> Result<Tagged<ListSuspiciousTradesResponse>>

Get suspicious trades, with the response’s ETag.

GET /api/v1/suspicious-trades

Self::list_suspicious_trades, returning the ETag validator the response carried beside the decoded body. Send it back as if_none_match on the next read: an unchanged resource answers Error::NotModified { etag }.

Source

pub async fn get_suspicious_trade( &self, id: &str, params: &GetSuspiciousTradeParams, ) -> Result<GetSuspiciousTradeResponse>

Get suspicious trade by ID

GET /api/v1/suspicious-trades/{id}

Returns one suspicious trade by raw whale_alerts.id or the rf_-prefixed id emitted by list responses. A flag on a market outside sports and esports returns 404 not_found with error.reason=market_out_of_scope (#27305); stop requesting it. Canonical since 2026-09-23; GET /api/v1/insider-radar/{id} is its deprecated alias and keeps answering object: radar_flag.

Source

pub async fn get_suspicious_trade_with_etag( &self, id: &str, params: &GetSuspiciousTradeParams, ) -> Result<Tagged<GetSuspiciousTradeResponse>>

Get suspicious trade by ID, with the response’s ETag.

GET /api/v1/suspicious-trades/{id}

Self::get_suspicious_trade, returning the ETag validator the response carried beside the decoded body. Send it back as if_none_match on the next read: an unchanged resource answers Error::NotModified { etag }.

Source

pub async fn list_games( &self, params: &ListGamesParams, ) -> Result<ListGamesResponse>

List covered games

GET /api/v1/games

One coherent game view per row: both sides with their provider ids and live scores, the UTC kickoff, the provider’s own status, the esports series format, and the game’s winner markets (the moneyline, or a soccer game’s three 1X2 legs) with their condition ids and outcome token ids. Spreads, totals, team totals, period lines and player props are on GET /api/v1/games/{event_slug}/markets. Built from the same provider-first live and upcoming projections the site’s sports boards use, so a request pays no provider fan-out of its own. A finished status (ended, cancelled, postponed) also lists the games the board has dropped, from their stored records (freshness.source stored, as GET /api/v1/games/{event_slug} serves them), for kickoffs from starts_after (default 7 days before starts_before) to starts_before (default and cap: now); such a page can hold fewer than limit games, or none, while has_more is true, so follow next_cursor until has_more is false. Ordered by kickoff, then by event_slug; games whose kickoff the provider has not published sort last. For what is on now, pass window=now: without it the list starts at the oldest game still open, which can be weeks old. coverage names the sports and leagues this deployment serves and any scope whose source was unavailable for the read, so an empty page is never ambiguous. A sport or status outside the published vocabulary returns an empty page rather than a 400. Carries the board’s existing competitor-bound provider moneyline price state by default, with an observation clock and explicit incomplete or invalid state. Each game also carries its traded volume (volume: value, unit, basis, source, observed_at) and each market its provider liquidity, so games can be ranked by activity without a Gamma call; a figure the board does not hold is omitted, never 0. A tennis game also carries its tournament (tier, city and country, venue time zone, official dates) from the reviewed registry its page reads, omitted when the registry holds no single edition for it. It does not fetch another provider endpoint. Soccer 1X2 moneylines retain independent YES legs classified as home, draw and away; draw_offered is true when a classified draw leg is included. The event-level sharp-money split is opt-in through expand=sharp_money (Pro and Max); holder identities remain on their own gated routes.

Source

pub async fn list_games_with_etag( &self, params: &ListGamesParams, ) -> Result<Tagged<ListGamesResponse>>

List covered games, with the response’s ETag.

GET /api/v1/games

Self::list_games, returning the ETag validator the response carried beside the decoded body. Send it back as if_none_match on the next read: an unchanged resource answers Error::NotModified { etag }.

Source

pub async fn get_game( &self, event_slug: &str, params: &GetGameParams, ) -> Result<GetGameResponse>

Get one game

GET /api/v1/games/{event_slug}

The same game object GET /api/v1/games returns, for one event_slug, including soccer 1X2 home, draw and away market sides and draw_offered. The slug is the identity the live_sports_updated webhook pulse carries, so a receiver can read the full game straight from a pulse. A slug outside the published coverage returns 404, including a real Polymarket event in a sport this deployment does not serve; GET /api/v1/games names what is covered. A finished game leaves GET /api/v1/games with the live board and stays readable here by its slug: once the provider reports it final, awarded, forfeited, cancelled or postponed, or every moneyline leg has resolved, it is served from its stored record with freshness.source stored, the final score when a live-score frame carried one, no prices, title, volume, liquidity or tournament. A game the board dropped while it was still in play returns 404. markets holds the winner markets only; GET /api/v1/games/{event_slug}/markets lists every other market of the event.

Source

pub async fn get_game_with_etag( &self, event_slug: &str, params: &GetGameParams, ) -> Result<Tagged<GetGameResponse>>

Get one game, with the response’s ETag.

GET /api/v1/games/{event_slug}

Self::get_game, returning the ETag validator the response carried beside the decoded body. Send it back as if_none_match on the next read: an unchanged resource answers Error::NotModified { etag }.

Source

pub async fn list_game_markets( &self, event_slug: &str, params: &ListGameMarketsParams, ) -> Result<ListGameMarketsResponse>

List a game’s markets

GET /api/v1/games/{event_slug}/markets

Every stored Polymarket market of one event_slug: the moneyline, spreads, totals, team totals, half and quarter lines, player props and the rest, plus the markets of the event’s child events (a soccer game’s exact score, halftime result and more-markets events), each with its own event_slug. The game object on GET /api/v1/games carries only the winner markets the sports board binds to the two sides; this read lists the rest. Each row carries the condition_id, the provider’s sports_market_type, groupItemTitle and typed line, the outcome index the line belongs to when the provider states it, both outcome labels with their CLOB token ids, and active or closed status. families lists every market type the event holds with its counts, whatever the filters. Ordered by sports_market_type, then line, then condition_id. Live, upcoming and settled events all answer. expand=sharp_money adds each row’s sharp-money split (side USD and S, A and B wallet counts per outcome) from the game page’s per-line owner: computed for a line in the game’s prop frame, not_computed for a line outside it (a player prop, a line beyond the seven around its family’s main line, a closed or settled line), unavailable when the read failed or held no observation of the line within 120 ms. While the frame is being built, a line the game’s stored split holds is computed and dated by money_as_of. Each read starts the game’s split refilling: read again after a minute or two for the lines it could not answer. No price and no holder identity: GET /api/v1/games/{event_slug}/holders reads the holders of up to 25 of these markets in one call, and GET /api/v1/market/{condition_id}/holders or /snapshot reads one.

Source

pub async fn list_game_markets_with_etag( &self, event_slug: &str, params: &ListGameMarketsParams, ) -> Result<Tagged<ListGameMarketsResponse>>

List a game’s markets, with the response’s ETag.

GET /api/v1/games/{event_slug}/markets

Self::list_game_markets, returning the ETag validator the response carried beside the decoded body. Send it back as if_none_match on the next read: an unchanged resource answers Error::NotModified { etag }.

Source

pub async fn get_game_sharp_money( &self, event_slug: &str, params: &GetGameSharpMoneyParams, ) -> Result<GetGameSharpMoneyResponse>

Get one game’s sharp money

GET /api/v1/games/{event_slug}/sharp-money

Where the graded money sits on a whole game, in one read: the S, A and B grade wallets’ USD on each outcome (home, away and, for a 1X2 game, draw) summed across the game’s markets, each outcome’s integer share, the leader and wallet counts by grade, with the backend’s verdict on the split. It is the same aggregate the 0xinsider game page renders, so the two cannot disagree. A game the sports board did not evaluate is computed on demand; while that compute runs the route answers 503 read_model_warming with Retry-After, and the next read serves it. A settled game returns its recap with lifecycle settled. Every aggregate field is null unless status is computed: null means unknown, never zero. Pro and Max.

Source

pub async fn get_game_sharp_money_with_etag( &self, event_slug: &str, params: &GetGameSharpMoneyParams, ) -> Result<Tagged<GetGameSharpMoneyResponse>>

Get one game’s sharp money, with the response’s ETag.

GET /api/v1/games/{event_slug}/sharp-money

Self::get_game_sharp_money, returning the ETag validator the response carried beside the decoded body. Send it back as if_none_match on the next read: an unchanged resource answers Error::NotModified { etag }.

Source

pub async fn list_game_trades( &self, event_slug: &str, params: &ListGameTradesParams, ) -> Result<ListGameTradesResponse>

List a game’s trades

GET /api/v1/games/{event_slug}/trades

A game’s newest fills across every market in it, newest first and live: the moneyline, spreads, totals and every prop, player props included. The same rows the 0xinsider game page’s activity list shows a Pro viewer, read from the same live stream. Every fill, not only large ones: GET /api/v1/large-trades?event_slug= lists the game’s large trades with their review scores, and GET /api/v1/markets/sharp-money-flows?event_slug= its sharp-money flow. The list is the newest limit fills and does not page (has_more is always false); to follow a game, poll it with If-None-Match and deduplicate on id. source_stale true means the rows are the game’s last complete read, served while a refresh runs; read again in a few seconds for the live rows. While a game’s first read is still running the answer is 503 with error.reason read_model_warming and a Retry-After of 2 seconds.

Source

pub async fn list_game_trades_with_etag( &self, event_slug: &str, params: &ListGameTradesParams, ) -> Result<Tagged<ListGameTradesResponse>>

List a game’s trades, with the response’s ETag.

GET /api/v1/games/{event_slug}/trades

Self::list_game_trades, returning the ETag validator the response carried beside the decoded body. Send it back as if_none_match on the next read: an unchanged resource answers Error::NotModified { etag }.

Source

pub async fn get_game_holders( &self, event_slug: &str, condition_ids: &[&str], params: &GetGameHoldersParams, ) -> Result<GetGameHoldersResponse>

Get a game’s holders

GET /api/v1/games/{event_slug}/holders

The S, A and B graded holders of up to 25 markets of one game in one call. Each item is GET /api/v1/market/{condition_id}/holders for one market, unfiltered and cut to limit, with the same market, scan and totals beside it. Pass condition_ids from GET /api/v1/games/{event_slug}/markets; a market that is not one of the game’s is that item’s not_found. A batch route: results preserve request order, duplicate inputs return duplicate rows, and each requested condition_id costs one batch item unit, reserved before any item is read. Each market loads its roster as the single-market route does (a 60-second cache over a complete provider holder scan). The request waits at most 150 ms for all of them together: a market whose roster is not ready by then is an item error with code rate_limit_unavailable, reason read_model_warming and retry_at, and its scan keeps running, so a retry after that time reads the cached roster. Page a market past limit on its own holders route. Pro and Max.

Source

pub async fn list_insider_radar( &self, params: &ListInsiderRadarParams, ) -> Result<ListInsiderRadarResponse>

Get insider radar flags

GET /api/v1/insider-radar

Deprecated alias of GET /api/v1/suspicious-trades (2026-09-23), kept live until its sunset on 2027-02-01 (#29351); responses carry Deprecation, Sunset and a Link rel=“successor-version” to it, and from that date the path answers 410 Gone with error.code endpoint_retired. Stored trades whose recorded suspicion score meets the live flag threshold. Evidence contains the scorer’s stored signals. Cursor-paginated by suspicion score. mode=live (default) uses fresh cached pages; mode=stable pins pagination to one published scoring generation and returns cursor_expired when scores or filters change. Sports and esports markets only (#27305): a flag on a market outside product scope is not listed, and the detail route answers it 404 not_found with error.reason=market_out_of_scope.

Source

pub async fn list_insider_radar_with_etag( &self, params: &ListInsiderRadarParams, ) -> Result<Tagged<ListInsiderRadarResponse>>

Get insider radar flags, with the response’s ETag.

GET /api/v1/insider-radar

Self::list_insider_radar, returning the ETag validator the response carried beside the decoded body. Send it back as if_none_match on the next read: an unchanged resource answers Error::NotModified { etag }.

Source

pub async fn get_insider_radar_flag( &self, id: &str, params: &GetInsiderRadarFlagParams, ) -> Result<GetInsiderRadarFlagResponse>

Get insider radar flag by ID

GET /api/v1/insider-radar/{id}

Deprecated alias of GET /api/v1/suspicious-trades/{id} (2026-09-23), kept live until its sunset on 2027-02-01 (#29351); responses carry Deprecation, Sunset and a Link rel=“successor-version” to it, and from that date the path answers 410 Gone with error.code endpoint_retired. The envelope keeps object: radar_flag, so an integration that branches on it keeps working here. Returns one suspicious-trading radar flag by raw whale_alerts.id or the rf_-prefixed id emitted by list responses. A flag on a market outside sports and esports returns 404 not_found with error.reason=market_out_of_scope (#27305); stop requesting it.

Source

pub async fn get_insider_radar_flag_with_etag( &self, id: &str, params: &GetInsiderRadarFlagParams, ) -> Result<Tagged<GetInsiderRadarFlagResponse>>

Get insider radar flag by ID, with the response’s ETag.

GET /api/v1/insider-radar/{id}

Self::get_insider_radar_flag, returning the ETag validator the response carried beside the decoded body. Send it back as if_none_match on the next read: an unchanged resource answers Error::NotModified { etag }.

Source

pub async fn get_event_replay_since( &self, params: &GetEventReplaySinceParams, ) -> Result<GetEventReplaySinceResponse>

Replay public large-trade events

GET /api/v1/events/feed/since

Returns durable public large-trade events strictly after an opaque cursor, in commit order: events are ordered by the position at which their write became visible to every reader (whale_alerts.inserted_xid), then by whale_alerts.id, and a page never reaches past the oldest write transaction still open when it was read. A trade whose id is lower than one already delivered but whose write finished later is therefore delivered on a later request instead of being skipped (#16180). This is a separate API-key contract from the browser/session /api/events/feed stream: browser-only and private alert, following, radar, and position patch events are excluded until they have a durable public outbox. Carries large trades on sports and esports markets only (#27430).

Source

pub async fn list_webhooks(&self) -> Result<ListWebhooksResponse>

List builder webhook destinations

GET /api/v1/webhooks

Returns webhook destinations owned by the authenticated API key user. Deleted endpoints are omitted; an endpoint paused with PATCH or disabled after consecutive failures is listed with status disabled. Subscribable event_types and their payload shapes are described by GET /api/v1/webhooks/events. Export lifecycle event types (export_job_ready, export_job_failed, export_job_expired, export_job_cancelled) are delivered only to the API-key account that created the export and contain no download URL; use the authorized export status and download routes. Four subscribable event types are Pro-only and only deliver to API keys on an active Pro subscription. whale_trades_inserted is one of them, gated by the same SubscriberScope::InsiderOnly mechanism as the other three (each type carries its own LiveEventContract entry; they share the scope value). The other three: wallet_grade_changed (data: wallet, trader_id, old_grade, new_grade, direction (upgrade|downgrade), skill_index, final_score, date) fires on a Pass-2 grade transition; insider_radar_flag_raised (data: trade_id, wallet, trader_id, condition_id, suspicion_score, track, side (yes|no), size, price) fires the first time a trade’s suspicion score crosses the radar flag threshold; sharp_money_flow_detected (data: condition_id, net_flow_usd, abs_net_flow_usd, dominant_side (yes|no), grade_floor (S|A|B|C|D|F), whale_trade_count, window) fires when a scheduled scanner detects ranked-trader net flow crossing a threshold (up or down) on a market, and smart_money_flow_detected is its deprecated spelling of the same event. live_sports_updated (data: event_slug, game_id, league, version, changed, observed_at, published_at, status, period, clock, live, ended, scores, series_format, snapshot_url) is ungated and fires as a bounded per-game pulse when a live game’s scores, status, period or live/ended state moves: at most one delivery per game per 20 seconds, with a live or ended transition exempt from that interval, and the game clock alone never firing one. version is a durable per-game ordinal, changed lists the material fields that moved since that game’s previous delivered pulse, and snapshot_url is where to re-read the whole game after a gap.

Source

pub async fn create_webhook( &self, body: &CreateWebhookRequest, params: &CreateWebhookParams, ) -> Result<CreateWebhookResponse>

Create a builder webhook destination

POST /api/v1/webhooks

Creates a pending HTTPS webhook destination. The response includes one-time signing_secret and verification.token values. Deliveries are not sent until the endpoint is verified, and verification requires the destination to answer 2xx to a signed webhook.verification challenge (see POST /api/v1/webhooks/{id}/verify). The subscribable event_types and their data payload shapes are described by GET /api/v1/webhooks/events; the per-endpoint delivery log is GET /api/v1/webhooks/{id}/deliveries. Export lifecycle event types (export_job_ready, export_job_failed, export_job_expired, export_job_cancelled) are delivered only to the API-key account that created the export and contain no download URL; use the authorized export status and download routes. Four subscribable event types are Pro-only and only deliver to API keys on an active Pro subscription. whale_trades_inserted is one of them, gated by the same SubscriberScope::InsiderOnly mechanism as the other three (each type carries its own LiveEventContract entry; they share the scope value). The other three: wallet_grade_changed (data: wallet, trader_id, old_grade, new_grade, direction (upgrade|downgrade), skill_index, final_score, date) fires on a Pass-2 grade transition; insider_radar_flag_raised (data: trade_id, wallet, trader_id, condition_id, suspicion_score, track, side (yes|no), size, price) fires the first time a trade’s suspicion score crosses the radar flag threshold; sharp_money_flow_detected (data: condition_id, net_flow_usd, abs_net_flow_usd, dominant_side (yes|no), grade_floor (S|A|B|C|D|F), whale_trade_count, window) fires when a scheduled scanner detects ranked-trader net flow crossing a threshold (up or down) on a market, and smart_money_flow_detected is its deprecated spelling of the same event. live_sports_updated (data: event_slug, game_id, league, version, changed, observed_at, published_at, status, period, clock, live, ended, scores, series_format, snapshot_url) is ungated and fires as a bounded per-game pulse when a live game’s scores, status, period or live/ended state moves: at most one delivery per game per 20 seconds, with a live or ended transition exempt from that interval, and the game clock alone never firing one. version is a durable per-game ordinal, changed lists the material fields that moved since that game’s previous delivered pulse, and snapshot_url is where to re-read the whole game after a gap. Delivery signing: each delivery request carries one or more HMAC-SHA256 signatures in the x-0xinsider-signature header as comma-separated v1= candidates. During staged rotation, the current and previous signing secrets are both signed for one hour; while 0xinsider rotates the key every signing secret is derived from, each also signs under the previous key, so the secret you hold keeps verifying. Up to four candidates; accept any valid candidate. Each candidate is HMAC-SHA256(signing_secret, “.<raw_request_body>”). The signed is sent separately as x-0xinsider-timestamp (unix seconds). To verify a delivery: read x-0xinsider-timestamp, reject it if it differs from the current time by more than 300 seconds, recompute each candidate over “.<raw_body>” with the active secrets, and compare using a constant-time comparison. Each delivery also carries x-0xinsider-event-id, x-0xinsider-event-type, x-0xinsider-delivery-id, and x-0xinsider-delivery-attempt headers. Retries and disabling: ordinary transient delivery failures use stable jitter between half and the existing 60, 120, 240, 480, 960, 1920 and 3600-second upper bounds (retry_policy.retry_horizon_seconds = 7380); on 408, 429, or 5xx, a valid Retry-After delta-seconds or HTTP-date replaces that one wait and is clamped to 60–3600 seconds, while missing, malformed, past, or non-retryable-response hints use the ordinary schedule. The delivery still has eight attempts (retry_policy.max_attempts), and an eighth failure becomes dead_letter. The delivery log exposes retry_schedule_reason and next_attempt_at so a receiver can see the active schedule. Separately, an endpoint is disabled after 8 consecutive failed attempts across all of its deliveries (retry_policy.disable_after_consecutive_failures); any successful attempt resets that count, so a busy endpoint that goes down can be disabled in minutes, well before any single delivery exhausts its retries. Disabling dead-letters every delivery still queued for the endpoint and emails the account owner, within about an hour, with each disabled endpoint and its last failed response. Re-enable it with PATCH /api/v1/webhooks/{id} {“enabled”: true}; re-enabling does not resend dead-lettered deliveries. Resend each one with POST /api/v1/webhooks/{id}/deliveries/{delivery_id}/redeliver, or catch up with GET /api/v1/events/feed/since from the last event you processed. GET /api/v1/webhooks/{id}/deliveries shows next_attempt_at and retry_schedule_reason for a delivery still waiting to retry.

Source

pub async fn list_webhook_events(&self) -> Result<ListWebhookEventsResponse>

List webhook event catalog

GET /api/v1/webhooks/events

Self-describing catalog of every webhook event type: its description, data payload shape, and whether it is active (has a firing producer) or dormant (subscribable but not yet delivered; no event type is dormant today). The catalog is identical for every authenticated key and exposes no owner-scoped data. Pro-only event types (large_trades_inserted, whale_trades_inserted, wallet_grade_changed, suspicious_trade_flagged, insider_radar_flag_raised, sharp_money_flow_detected, smart_money_flow_detected) appear in the catalog but only deliver to API keys on an active Pro subscription. Export lifecycle event types (export_job_ready, export_job_failed, export_job_expired, export_job_cancelled) are owner-scoped to the API-key account that created the export and contain no download URL; use the authorized export status and download routes.

Source

pub async fn list_webhook_deliveries( &self, id: i64, params: &ListWebhookDeliveriesParams, ) -> Result<ListWebhookDeliveriesResponse>

List webhook delivery log

GET /api/v1/webhooks/{id}/deliveries

Recent delivery attempts for one webhook destination owned by the authenticated API key user, newest first, with opaque cursor pagination. Returns 404 (identical to an unknown id) when the endpoint is not owned by the caller, so a non-owner cannot tell an owned-but-empty log apart from someone else’s endpoint. Delivery rows omit the request body and signing secret. next_attempt_at is when a pending or retry delivery is next attempted; retry_schedule_reason says whether the queue is following receiver_retry_after, transient_failure, permanent_or_auth_failure, manual_redelivery, or configuration_changed; both are null while an attempt is in flight and once the delivery is delivered or dead_letter. A delivery keeps the existing eight-attempt budget, endpoint failure budget, fairness limits, and ordinary 7380-second upper-bound schedule; see POST /api/v1/webhooks for the bounded Retry-After policy. Resend a dead_letter delivery with POST /api/v1/webhooks/{id}/deliveries/{delivery_id}/redeliver.

Source

pub async fn redeliver_webhook_delivery( &self, id: i64, delivery_id: i64, params: &RedeliverWebhookDeliveryParams, ) -> Result<RedeliverWebhookDeliveryResponse>

Redeliver a dead-lettered webhook delivery

POST /api/v1/webhooks/{id}/deliveries/{delivery_id}/redeliver

Returns one dead_letter delivery to the delivery queue with a fresh attempt budget: status becomes pending, attempt_count resets to 0, and next_attempt_at is now, so the delivery worker claims it like any queued delivery, under the same per-owner and per-origin concurrency limits. Use it after a failed delivery exhausts its retries, or after re-enabling an endpoint whose queued deliveries were dead-lettered when it was disabled: PATCH /api/v1/webhooks/{id} {“enabled”: true} resends nothing by itself. The resend is signed with the endpoint’s current signing secret and sent to its current URL, and keeps the delivery’s event_id and idempotency-key header, so a receiver that already processed the event can deduplicate it; x-0xinsider-delivery-attempt starts again at 1. last_response_status and last_error keep the previous failure until the next attempt records its own. Returns 409 when the delivery is already delivered or still queued (pending, retry, or processing), or when the endpoint would not receive it (disabled, not verified, or no longer subscribed to the delivery’s event_type); error.message names the step that lets the same request succeed. Returns 404 for an endpoint or delivery the caller does not own, including a delivery removed by the seven-day retention of delivered and dead_letter history.

Source

pub async fn get_webhook(&self, id: i64) -> Result<GetWebhookResponse>

Get one builder webhook destination

GET /api/v1/webhooks/{id}

Returns one webhook destination owned by the authenticated API key user.

Source

pub async fn update_webhook( &self, id: i64, body: &UpdateWebhookRequest, params: &UpdateWebhookParams, ) -> Result<UpdateWebhookResponse>

Update a builder webhook destination

PATCH /api/v1/webhooks/{id}

Updates name, HTTPS URL, event types, or enabled state. URL changes force pending_verification and return a new verification token; the new destination must answer 2xx to the signed webhook.verification challenge before deliveries resume. A URL change returns 409 with error.reason=webhook_delivery_in_progress while this destination has an active delivery; retry the same mutation after that request completes.

Source

pub async fn delete_webhook( &self, id: i64, params: &DeleteWebhookParams, ) -> Result<DeleteWebhookResponse>

Disable a builder webhook destination

DELETE /api/v1/webhooks/{id}

Soft-deletes a webhook destination owned by the authenticated API key user. Existing delivery audit rows remain retained.

Source

pub async fn verify_webhook( &self, id: i64, body: &VerifyWebhookRequest, ) -> Result<VerifyWebhookResponse>

Verify a builder webhook destination

POST /api/v1/webhooks/{id}/verify

Activates a pending webhook destination. Two conditions must both hold: the one-time verification token matches and has not expired, AND the destination answers 2xx to a signed challenge this operation POSTs to the endpoint’s stored url. The challenge body is {“type”:“webhook.verification”,“token”:“<verification_token>”,“webhook_id”:}. It carries x-0xinsider-event-type: webhook.verification plus the same x-0xinsider-timestamp and x-0xinsider-signature headers a delivery carries, signed with that endpoint’s signing_secret, so verify it exactly as you verify a delivery. Answer any 2xx within 10 seconds; the challenge response body is never read, and the challenge follows no redirects. A status outside 2xx, no answer within 10 seconds, or a url that does not resolve to a publicly routable address leaves status at pending_verification and returns 422. The token alone never activates an endpoint.

Source

pub async fn prepare_webhook_secret( &self, id: i64, params: &PrepareWebhookSecretParams, ) -> Result<PrepareWebhookSecretResponse>

Prepare a staged builder webhook signing secret

POST /api/v1/webhooks/{id}/rotate-secret/prepare

Creates a pending signing secret while the current secret remains active. Deploy the returned one-time signing_secret to the receiver before calling activate. The response exposes secret_rotation.status=pending; an existing pending secret is returned again so a lost response can be recovered safely.

Source

pub async fn activate_webhook_secret( &self, id: i64, params: &ActivateWebhookSecretParams, ) -> Result<ActivateWebhookSecretResponse>

Activate a staged builder webhook signing secret

POST /api/v1/webhooks/{id}/rotate-secret/activate

Promotes the prepared signing secret to current, returns it once, and signs every delivery with both the new and previous secrets for one hour. Call retire after the receiver has completed its rollout. The current secret remains available through the existing immediate rotate-secret route for emergency replacement.

Source

pub async fn retire_webhook_secret( &self, id: i64, params: &RetireWebhookSecretParams, ) -> Result<RetireWebhookSecretResponse>

Retire the previous builder webhook signing secret

POST /api/v1/webhooks/{id}/rotate-secret/retire

Ends the one-hour dual-signature overlap and removes the previous signing secret from future delivery authorization. Call this after the receiver accepts the activated secret. The operation is idempotent and does not return signing_secret.

Source

pub async fn rotate_webhook_secret( &self, id: i64, params: &RotateWebhookSecretParams, ) -> Result<RotateWebhookSecretResponse>

Rotate a builder webhook signing secret

POST /api/v1/webhooks/{id}/rotate-secret

Rotates the endpoint signing secret and returns the new signing_secret once in the response. Returns 409 with error.reason=webhook_delivery_in_progress while a live delivery freezes the current signing nonce; retry after it completes.

Source

pub async fn get_health( &self, params: &GetHealthParams, ) -> Result<GetHealthResponse>

Health check

GET /api/v1/health

Returns API health status. No authentication required. Limited to 120 requests per minute per IP.

Source

pub async fn get_health_with_etag( &self, params: &GetHealthParams, ) -> Result<Tagged<GetHealthResponse>>

Health check, with the response’s ETag.

GET /api/v1/health

Self::get_health, returning the ETag validator the response carried beside the decoded body. Send it back as if_none_match on the next read: an unchanged resource answers Error::NotModified { etag }.

Source

pub async fn create_mcp_json_rpc_response( &self, body: &CreateMcpJsonRpcResponseBody, params: &CreateMcpJsonRpcResponseParams, ) -> Result<Option<CreateMcpJsonRpcResponseResponse>>

Remote MCP endpoint (JSON-RPC)

POST /api/v1/mcp

Model Context Protocol (MCP) Streamable HTTP transport. Accepts one JSON-RPC 2.0 request or notification. ID-bearing initialize, ping, tools/list, and tools/call requests (and server/discover on revision 2026-07-28) receive one same-ID JSON-RPC response; an unknown method, or a notification method sent with an id, answers JSON-RPC error -32601. Every ID-less message (a notification, known or not) receives HTTP 202 with no body and runs nothing; an unsolicited response message receives HTTP 400 with a JSON-RPC -32600 body and no id. Unknown cancellation IDs are ignored; cancellation does not stop running tool work. Only tools/call is counted against the per-minute request budget, the monthly quota and pay-as-you-go usage; every other message, with or without a key, spends none of them and reports the per-IP budget in RateLimit-* (since 2026-10-18). Remote MCP exposes 55 read-only tools for public V1 read operations: get_leaderboard, get_trader, batch_get_traders, get_large_trades, get_large_trade, get_large_trades_history, get_whale_trades, get_whale_trade, get_whale_trades_history, get_edge_hunter, list_games, get_game, get_market_flow, batch_get_market_flow, get_market_intel, batch_get_market_intel, get_sharp_money_flows, get_smart_money_flows, get_market_snapshot, get_suspicious_trades, get_suspicious_trade, get_insider_radar, get_insider_radar_flag, get_positions, get_position_timeline, get_position_timeline_by_id, search_markets, search_content, explore_markets, get_event_replay_since, list_webhooks, get_webhook, get_daily_report_snapshot, get_weekly_report_snapshot, get_monthly_report_snapshot, get_report, get_trader_export_snapshot, get_coverage, get_platforms, get_large_positions, get_trending_wallets, get_trader_pnl, get_pick_of_the_day, get_pick_of_the_day_ledger_entry, get_pick_of_the_day_archive, get_trader_context, get_trader_categories, get_trader_grade_at, get_market_holders, get_market_candles, get_combo_fills, get_combos_summary, get_combo, get_large_trade_counterparty_executions, get_large_trade_counterparty_makers. Webhook create/update/delete/verify/rotate operations are intentionally not exposed as remote MCP tools. The remote endpoint advertises tools only; it does not implement resources/list, resources/read, prompts/list, or prompts/get. Each tool dispatches to the matching /api/v1/* handler in-process so auth, rate limits, and payload shape match. initialize, notifications/initialized, ping and tools/list answer without a credential; tools/call (and any GET) need Authorization: Bearer with an OAuth 2.1 access token (see the oauth2 security scheme; a tool outside the token’s scopes returns isError) or an API key, and without one answer HTTP 401 with a WWW-Authenticate challenge whose resource_metadata names the RFC 9728 document at /.well-known/oauth-protected-resource/api/v1/mcp. A key in a ?token= query parameter is rejected on every authenticated route (the public routes ignore it) with 401 invalid_api_key and error.reason api_key_in_query, because URLs land in shell history, browser history, and logs; a client that can only take a URL can run the @0xinsider/mcp stdio package. The endpoint is stateless: it mints no Mcp-Session-Id and ignores one a client sends. Origin header, when present, is validated against the 0xinsider + localhost allowlist. tools/call arguments are validated against the tool’s advertised inputSchema before anything runs: an unknown key, a wrong type, an out-of-range number, a value outside an enum or an argument combination outside a oneOf answers a tool result with isError true whose text names the field, never a query built from the arguments that fit; a tools/call without name, or whose arguments is not an object, answers JSON-RPC error -32602 and runs nothing. Every tools/call result carries the REST envelope’s meta verbatim (request_id, cached, cache_age_s, cost, and the route’s provenance such as source and completeness on get_whale_trades_history or ranking_source and directional_source on get_edge_hunter) as structuredContent.meta beside the payload’s own keys, and a tool failure carries structuredContent.error with the REST error fields verbatim (code, reason, param, doc_url, retry_at) plus retry_after_seconds, request_id and status, beside its text. The MCP-Protocol-Version request header is checked on every POST: a value outside 2026-07-28, 2025-11-25, 2025-06-18, 2025-03-26 and 2024-11-05 answers HTTP 400 with the standard error envelope (error.code bad_request); an absent header is served as 2025-03-26, the transport specification’s compatibility default. Revision 2026-07-28 also requires the Mcp-Method header (JSON-RPC error -32020 without it) and adds resultType to every result. Browser preflight allows MCP-Protocol-Version, Mcp-Method and Mcp-Name.

Returns None when the API answers with an empty success body.

Source

pub async fn get_reports( &self, granularity: Granularity, period: &str, ) -> Result<GetReportsResponse>

Unified report snapshot (granularity selector)

GET /api/v1/reports

Unified convenience route (#4975) that consolidates the three singular report routes. Dispatches to the exact per-granularity cap (daily 50, weekly 100, monthly 200) and date window the legacy /api/v1/reports/{daily,weekly,monthly} routes use, so the response body is byte-identical to the matching legacy route for the same period. An ISO weekly period is a durable canonical snapshot; an explicit weekly from,to pair is an exact ephemeral range limited to 31 inclusive UTC days and never creates a persisted snapshot identity. A wider explicit range returns 400 invalid_query. The three legacy routes stay live. Also reachable via the MCP get_report selector.

Source

pub async fn get_daily_report_snapshot( &self, date: &str, ) -> Result<GetDailyReportSnapshotResponse>

Daily report snapshot

GET /api/v1/reports/daily

Returns a dated daily whale-activity report snapshot with source_range, snapshot.status, completeness, and reconciliation metadata. Report whale volume is local whale-alert activity volume, not provider lifetime trader volume.

Source

pub async fn get_weekly_report_snapshot( &self, params: &GetWeeklyReportSnapshotParams, ) -> Result<GetWeeklyReportSnapshotResponse>

Weekly report snapshot

GET /api/v1/reports/weekly

Returns a weekly whale-activity report snapshot. Pass an ISO YYYY-WW token for a durable canonical snapshot, or an exact from/to UTC range of at most 31 inclusive days for an ephemeral response. The response identifies closed ranges as final and current ranges as rolling.

Source

pub async fn get_monthly_report_snapshot( &self, month: &str, ) -> Result<GetMonthlyReportSnapshotResponse>

Monthly report snapshot

GET /api/v1/reports/monthly

Returns a UTC monthly whale-activity report snapshot with source_range, completeness, and reconciliation metadata.

Source

pub async fn get_resolution_report( &self, date: &str, params: &GetResolutionReportParams, ) -> Result<GetResolutionReportResponse>

Get the daily resolution report

GET /api/v1/reports/resolutions

One UTC day of resolved sports and esports markets: how often the side the profitable (S, A and B grade) wallets held won, beside how often every qualifying wallet’s side won, one vote each. markets lists each market they held a position in, with the consensus side, its confidence, and the full roster of wallets, their side, cost and realized P&L; view=totals drops it. Counts cover the whole day before that list is filtered. A past day is served from its retained build: generated_at says when it was built and source_updates_pending that a newer build has been requested. 503 read_model_warming while the day has no build yet; one has been requested.

Source

pub async fn get_resolution_report_with_etag( &self, date: &str, params: &GetResolutionReportParams, ) -> Result<Tagged<GetResolutionReportResponse>>

Get the daily resolution report, with the response’s ETag.

GET /api/v1/reports/resolutions

Self::get_resolution_report, returning the ETag validator the response carried beside the decoded body. Send it back as if_none_match on the next read: an unchanged resource answers Error::NotModified { etag }.

Source

pub async fn get_trader_export_snapshot( &self, address: &str, ) -> Result<GetTraderExportSnapshotResponse>

Trader export snapshot metadata

GET /api/v1/trader/{address}/export

Returns export source-range, completeness, volume reconciliation, row-count estimate, and large-export policy for one trader. To download the full dataset programmatically, POST to this same path to submit an async export job (json | ndjson | csv), then poll the status route and follow the download route once ready.

Source

pub async fn submit_trader_export( &self, address: &str, params: &SubmitTraderExportParams, ) -> Result<TraderExportJob>

Submit a trader dataset export job

POST /api/v1/trader/{address}/export

Queues an async export of the trader’s full dataset in the requested format (json default, ndjson, or csv) and returns the job resource. Poll the status route at poll_after_s until terminal is true, then follow the download route while status is ‘ready’ (until expires_at, 24 hours from submit). Reuse: without fresh=true, a live job for the same trader and format inside its retention window (queued, running, reconcile_required or ready) is returned with 200 instead of a new one, except a job its owner asked to cancel, which is never reused; its data_as_of says what its file is a snapshot of. With fresh=true only a queued job (which has not started reading) is reused; a running, reconciling or ready job is left alone and a new job is reserved, which consumes quota like any new job. Both decisions run under one per-account lock, so two concurrent submits cannot both reserve when one would have reused the other. Quotas are the per-user daily and per-address hourly export caps, keyed on the API key owner so an API key never widens its owner’s quota. New full exports select one database read generation before delivery. JSON and NDJSON add export_metadata.generation (id, selected_at, consistency=repeatable_read, source_watermarks for positions, native P&L, categories and all matching trades), position_generation, provider_observations (provider values and observation/revision metadata), category_data_as_of, pnl_date_from/to, pnl_rows, pnl_row_limit (3650), markets_rows, markets_row_limit (50000), summary_basis and trades_basis. Summary counts cover all stored markets; the markets section retains its existing latest-activity 50000-row limit and P&L its earliest-3650-days limit. These are database consistency and section coverage facts, not a claim that providers observed every section simultaneously. CSV appends export_generation, export_selected_at and export_consistency columns. Downloaded objects also expose x-amz-meta-export-generation, x-amz-meta-export-selected-at, x-amz-meta-export-consistency, x-amz-meta-export-trades and x-amz-meta-export-provenance headers, including empty CSV files. Historical artifacts remain unchanged. Select CSV columns by name; JSON/NDJSON readers may ignore the additive fields. A read exceeding 600 seconds or an uncompressed artifact exceeding 2 GiB fails without publishing a partial file.

Source

pub async fn get_trader_export_status( &self, address: &str, job_id: i64, ) -> Result<TraderExportJob>

Poll a trader export job

GET /api/v1/trader/{address}/export/status

Returns the job resource for a submitted export: status (queued | running | cancel_requested | reconcile_required | ready | failed | expired | cancelled), terminal, next_action and poll_after_s, the lifecycle timestamps, the retention window (expires_at) and, once the file is written, data_as_of and the artifact identity. A ready job past expires_at reads expired; a queued, running or reconciling job past expires_at reads failed. Work stops at retention safe points; completion and reconciliation cannot publish ready after expiry. The failure event carries failure_reason expired_before_completion and next_action resubmit; delivery never extends retention. A running job its owner cancelled reads cancel_requested until the worker stops, then cancelled. failed, cancelled and expired jobs stay readable for 48 hours, then 404.

Source

pub async fn cancel_trader_export( &self, address: &str, job_id: i64, ) -> Result<TraderExportJob>

Cancel a trader export job

POST /api/v1/trader/{address}/export/cancel

Cancels a submitted export and returns the job resource, the same shape the status route returns. A queued job reads cancelled at once and no worker will start it. A running job reads cancel_requested until the worker reaches its next safe point, then cancelled: the worker checks every 5 seconds while it reads the snapshot and between upload parts, and once more at the last point before the file is published; a storage request already in flight finishes first (each is bounded at 120 seconds), and a partial upload is discarded. If the worker itself stops first, the job reads cancelled as that worker shuts down for a deploy, and otherwise within a minute of its 30-minute lease lapsing. A job a cancel can no longer reach is returned unchanged with 200: once its upload is being completed it finishes as ready (or reconcile_required, then ready or failed), and ready, failed, expired and cancelled jobs are terminal. A cancel never deletes a ready file; compare status before and after. Repeating the request is safe: it converges on the same state and never repeats a transition, so a client that lost the answer can send it again or read the status route. While terminal is false, poll the status route after poll_after_s. Quota is unchanged: the submit’s reservation keeps counting toward the per-user daily and per-address hourly export caps for its full window, as a failed export’s does, and a job its owner asked to cancel is never reused by a later submit, which reserves a new job. Webhook endpoints subscribed to export_job_cancelled receive it when the job reaches cancelled. An unknown job id, or a job owned by another account or submitted for another trader, answers 404 exactly as the status route does.

Source

pub async fn download_trader_export( &self, address: &str, job_id: i64, ) -> Result<Download>

Download a finished trader export

GET /api/v1/trader/{address}/export/download

Redirects (302) to a presigned URL lasting at most one hour and no later than the job’s expires_at for the finished export file while the job status is ‘ready’ and expires_at has not passed. The file is gzip-compressed and served with the format’s Content-Type (application/json, application/x-ndjson, or text/csv). Returns 400 while the job is queued, running, cancel_requested or reconciling (poll the status route first) and for a failed or cancelled job (submit a new export); returns 410 with error.code not_found and error.reason export_expired once the retention window has passed, less than one whole second remains when signing, or the file has been retired, so the redirect never points at a file that is gone.

The API answers with a redirect to the file. It is followed once, by a fresh request that carries no credential, and the file comes back as a streaming Download.

Source

pub async fn get_usage(&self) -> Result<Usage>

Inspect current API usage without spending primary request quota

GET /api/v1/usage

Returns the authenticated caller sliding-window request budget, UTC-day usage and monthly quota. This control-plane endpoint remains available for a valid credential after paid data access lapses and does not increment the primary Redis rate-limit counter, monthly quota or API usage table; it shares a separate 100 reads/minute per-user inspection bucket with GET /api/v1/me. UTC-day totals use finalized quarter-hour rollups plus the disjoint raw interval through request time; unavailable rollup progress returns an error. Late accounting records retain their original UTC day when closed-period usage is read or the monthly budget is rebuilt. Current periods reflect persisted records and remain provisional; quota limits, prices and response fields are unchanged.

Source

pub async fn get_market_context_markdown( &self, condition_id: &str, ) -> Result<String>

Get market context (Markdown)

GET /api/v1/market/{condition_id}/context.md

Authenticated, self-contained Polymarket market evidence document. Renders the same typed data as the market snapshot with trust included: identity, outcome labels and tokens, cached quotes, liquidity, live sports, and per-source freshness or unavailable reasons. Provider text is encoded as indented JSON data. Accepts raw or mkt_-prefixed condition IDs. Unknown markets return 404. No ETag or conditional requests; use the snapshot JSON route for those.

Source

pub async fn get_account_identity(&self) -> Result<AccountIdentity>

Identify the authenticated account and credential

GET /api/v1/me

Returns caller-owned account and credential IDs, credential validity, paid-data entitlement and approved scopes. Null scopes mean full legacy developer-key access. Valid credentials can use this control-plane diagnostic path after paid access lapses; data routes still require active paid access. OAuth grants and named integration keys need read scope. Does not return credentials, payment details or personal contact details.

Source

pub async fn submit_whale_dataset( &self, body: &SubmitWhaleDatasetBody, ) -> Result<WhaleDatasetJob>

Create an immutable whale dataset

POST /api/v1/datasets/whale-trades

Extract at most 1,000,000 rows and 256 MiB uncompressed; exceeding either bound fails the job and publishes no partial file. Window and filters freeze on admission; rows and source facts freeze on one worker database snapshot. NDJSON uses exact decimal strings, with no current grade or market enrichment. The manifest checkpoint includes already committed arrivals after to plus later/open writers, with intentional overlap. Paid export scope; jobs are private to the credential owner. Shares the 20/day owner export quota and fair worker admission with trader exports; datasets also have a combined 5/hour quota. Three worker attempts, 24-hour retention, no partial download; cancellation consumes its reservation. Coverage is whale alerts, not all provider fills. Sports and esports markets only (#27430): trades on any other market are left out of the file, and a condition_id naming such a market returns 404 not_found with error.reason market_out_of_scope before any job is reserved.

Source

pub async fn get_whale_dataset_status( &self, job_id: i64, ) -> Result<WhaleDatasetJob>

Read a whale dataset job

GET /api/v1/datasets/whale-trades/{job_id}

Read queued, running, cancel_requested, ready, failed, reconcile_required, expired or cancelled. Poll only while next_action is poll, after poll_after_s. Ready manifests contain count, content and compressed SHA-256, schema, filters, source, horizon, expiry and replay continuation. Deltas do not retain the finite time filter and can repeat rows; deduplicate by wt_ ID (payload.whale_alert_id when not expanded). This is an insertion feed, not update/delete CDC. Paid export scope; jobs are private to the credential owner. Shares the 20/day owner export quota and fair worker admission with trader exports; datasets also have a combined 5/hour quota. Three worker attempts, 24-hour retention, no partial download; cancellation consumes its reservation. Coverage is whale alerts, not all provider fills.

Source

pub async fn download_whale_dataset(&self, job_id: i64) -> Result<Download>

Download an immutable whale dataset

GET /api/v1/datasets/whale-trades/{job_id}/download

Download only a ready artifact; status handles failure and cancellation. Retired artifacts return 410 at serving time. Paid export scope; jobs are private to the credential owner. Shares the 20/day owner export quota and fair worker admission with trader exports; datasets also have a combined 5/hour quota. Three worker attempts, 24-hour retention, no partial download; cancellation consumes its reservation. Coverage is whale alerts, not all provider fills.

The API answers with a redirect to the file. It is followed once, by a fresh request that carries no credential, and the file comes back as a streaming Download.

Source

pub async fn cancel_whale_dataset(&self, job_id: i64) -> Result<WhaleDatasetJob>

Cancel a whale dataset job

POST /api/v1/datasets/whale-trades/{job_id}/cancel

Cancel queued jobs immediately and request running jobs stop at their next safe point. Repeating cancel converges on the current state; cancellation after the completion fence leaves the artifact ready. Paid export scope; jobs are private to the credential owner. Shares the 20/day owner export quota and fair worker admission with trader exports; datasets also have a combined 5/hour quota. Three worker attempts, 24-hour retention, no partial download; cancellation consumes its reservation. Coverage is whale alerts, not all provider fills.

Source

pub async fn get_pick_of_the_day_ledger_entry( &self, pick_id: &str, params: &GetPickOfTheDayLedgerEntryParams, ) -> Result<GetPickOfTheDayLedgerEntryResponse>

Read a pick proof by stable id

GET /api/v1/pick-of-the-day/ledger/{pick_id}

Public read of one published pick by its stable decimal-string pick_id. Same proof gating, canonical bytes, ETag and integrity failures as the ledger. Invalid ids return 400; absent or unpublished picks return 404. Withdrawal from recommendations or the archive does not remove a published ledger entry.

Source

pub async fn get_pick_of_the_day_ledger_entry_with_etag( &self, pick_id: &str, params: &GetPickOfTheDayLedgerEntryParams, ) -> Result<Tagged<GetPickOfTheDayLedgerEntryResponse>>

Read a pick proof by stable id, with the response’s ETag.

GET /api/v1/pick-of-the-day/ledger/{pick_id}

Self::get_pick_of_the_day_ledger_entry, returning the ETag validator the response carried beside the decoded body. Send it back as if_none_match on the next read: an unchanged resource answers Error::NotModified { etag }.

Source

pub async fn create_webhook_verification_attempt( &self, id: i64, body: &CreateWebhookVerificationAttemptRequest, params: &CreateWebhookVerificationAttemptParams, ) -> Result<CreateWebhookVerificationAttemptResponse>

Admit asynchronous webhook verification

POST /api/v1/webhooks/{id}/verification-attempts

Additive asynchronous consent flow; the legacy POST /verify keeps its synchronous 200/422 behavior and retry policy. Commits one durable attempt and returns 202 without awaiting DNS or the receiver. Send the original verification_token. Optional Idempotency-Key replays the original admission response for the same endpoint/token identity; a changed request returns 422 and an in-flight matching key returns 409. Different keys for the same token revision recover the same attempt, including terminal attempts, without new receiver work. Poll status_url for current state; 202 is admission, not activation. A supervised worker sends the existing signed webhook.verification body with an additional verification_attempt_id and x-0xinsider-verification-attempt header, stable across at-least-once retries. Verify the timestamp/raw-body HMAC and answer 2xx. DNS is public-address-pinned; redirects and proxies are refused and response bodies are not read. Timeout/transport/429/5xx failures retry at most 4 attempts, with 5/15/45-second waits, until the earlier token deadline or 15-minute horizon. Other receiver statuses and URL/security refusals are terminal. Activation requires current owner eligibility, endpoint/token/URL/signing identity, unexpired attempt/token and valid lease. Configuration changes or disable/delete cancel obsolete work. Terminal ciphertext is erased; status exposes no token, signing secret, address or receiver body. History is available for 7 days.

Source

pub async fn get_webhook_verification_attempt( &self, id: i64, attempt_id: &str, ) -> Result<GetWebhookVerificationAttemptResponse>

Read webhook verification attempt status

GET /api/v1/webhooks/{id}/verification-attempts/{attempt_id}

Fresh primary-database status for one attempt owned by the authenticated account and webhook. queued/running means consent is pending; only verified activates the unchanged endpoint after receiver 2xx. failed/cancelled/expired are terminal. Polling never issues a challenge or restarts work. At expiry the read reports expired immediately, even before the worker persists expiry. A missing, deleted or cross-owner endpoint/attempt returns 404. Outcomes and actual HTTP status are sanitized; no token, ciphertext, signing secret, destination address or receiver response body is returned. Terminal history is retained for 7 days.

Source

pub async fn list_combo_fills( &self, params: &ListComboFillsParams, ) -> Result<ComboFillsResponse>

List combo fills

GET /api/v1/combos/fills

Returns live Polymarket combo taker fills with exact decimal-string economics, wallet identities, grades, legs, conjunction results, and ingest coverage. Requires an active Pro or Max API key. Listing includes executions with price at most 0.95; use combo detail for complete history. Sports and esports only (#27449): a fill is listed only when every leg of its combo is a sports or esports market; a combo with a leg on any other market never appears, and filters, page sizes and continuations apply after that exclusion. Recent ordering uses an exclusive cursor; largest and payout use offsets. Continuations are based on page fullness and may lead to an empty final page.

Source

pub async fn list_combo_fills_with_etag( &self, params: &ListComboFillsParams, ) -> Result<Tagged<ComboFillsResponse>>

List combo fills, with the response’s ETag.

GET /api/v1/combos/fills

Self::list_combo_fills, returning the ETag validator the response carried beside the decoded body. Send it back as if_none_match on the next read: an unchanged resource answers Error::NotModified { etag }.

Source

pub async fn get_combos_summary( &self, params: &GetCombosSummaryParams, ) -> Result<CombosSummaryResponse>

Get live combo count

GET /api/v1/combos/summary

Returns the stored count of traded combos whose YES conjunction remains pending, with its computation timestamp. Sports and esports only (#27449): a combo with a leg outside them is not counted. Requires an active Pro or Max API key. The count is refreshed by the ingest owner at most every five minutes and cached for 60 seconds. A null live value is unknown, never zero.

Source

pub async fn get_combos_summary_with_etag( &self, params: &GetCombosSummaryParams, ) -> Result<Tagged<CombosSummaryResponse>>

Get live combo count, with the response’s ETag.

GET /api/v1/combos/summary

Self::get_combos_summary, returning the ETag validator the response carried beside the decoded body. Send it back as if_none_match on the next read: an unchanged resource answers Error::NotModified { etag }.

Source

pub async fn get_combo( &self, condition_id: &str, params: &GetComboParams, ) -> Result<ComboResponse>

Get combo detail

GET /api/v1/combos/{condition_id}

Returns one Polymarket combo with its legs, conjunction result, all-time trade totals, newest 100 taker fills, lifecycle totals, and ingest coverage. Requires an active Pro or Max API key. Detail retains execution history above the listing price cap. A malformed combo ID returns 400; an unknown well-formed ID returns 404. A combo with a leg outside sports and esports returns 404 not_found with error.reason=market_out_of_scope (#27449). Null lifecycle or missing leg facts remain unknown.

Source

pub async fn get_combo_with_etag( &self, condition_id: &str, params: &GetComboParams, ) -> Result<Tagged<ComboResponse>>

Get combo detail, with the response’s ETag.

GET /api/v1/combos/{condition_id}

Self::get_combo, returning the ETag validator the response carried beside the decoded body. Send it back as if_none_match on the next read: an unchanged resource answers Error::NotModified { etag }.

Source

pub async fn get_edge_hunter( &self, params: &GetEdgeHunterParams, ) -> Result<Value>

Get Edge Hunter

GET /api/v1/edge-hunter

Max-only canonical Edge Hunter API with board, signals and observations views. An active Pro credential receives 403 forbidden with reason max_plan_required. Default board admission and ordering remain compatible: B grade floor, 30-row cap, 48-hour kickoff horizon, full Max projection and both current outcome quotes. Board outcomes include nullable token_id; the full signal includes nullable backed token_id, ranked_at copied from the frozen signal_created_at, and the existing aggregate category_skill evidence. Quote ts/validated_at and metadata_updated_at retain their separate meanings; none is wallet entry cost. Board quotes read verified display-price and websocket caches only, with no inline provider request or request-triggered REST refresh. Uncached outcomes remain null; retained quotes keep their original observation clock and existing retention bound. Board category, min_grade and limit are configurable; horizon clamps 1..48 and cap clamps 1..30. total counts selected admitted rows before the cap.

Signals exposes full ranked records and category-skill/source evidence with default B grade floor, 12-hour horizon (clamp 1..48), page limit 20 (clamp 1..100) and cursor. Observations requires wider_holder, in_play or emerging_pile and retains full records, verified token IDs, midpoint price marks, holder/provider/directional clocks, snapshot_as_of, degraded, degradation_reasons, coverage and source-generation/category funnel evidence. Required degradation_reasons records each operational cause with canonical sport or null for generation-wide evidence, and is empty only for a healthy generation. For sports and esports reads, the first four coverage counts stay global across cohort/category/selectors; selected_observations counts matching rows before pagination and returned_observations equals data.length. An unmappable legacy category retains its zero-work empty generation with all coverage counts zero and no degradation reasons, without reading the all-sports cache. Intentional capacity_limited input, the one-page holder bound (holder_scan_complete=false, counted in coverage.incomplete_holder_markets) and board_upcoming_status=not_configured do not by themselves degrade a generation. Reconciled terminals prove accounting, not complete holder coverage. Observation horizon stays 48 hours with 12-hour in-play kickoff lookback and 30-second live snapshot ceiling. In-play additionally requires a coherent nonfuture original live-source clock no older than 30 seconds at publication and every served reuse; compute and missed-pass grace never renew that clock. Provider-confirmed live membership is observed independently of rendered scores, quotes and holder enrichment. UI refresh-marker expiry or score invalidation alone does not invalidate a separately observed provider membership. Explicit provider-source invalidation or an invalid, incomplete, future, expired or missing source remains unavailable. When one scheduled observation refresh misses its deadline, other cohorts may keep serving the last snapshot for up to 45 more seconds, with its age in meta.cache_age_s and its completion time in snapshot_as_of; a second missed refresh in a row returns 503 read_model_warming. An expired in-play source returns 503 read_model_warming even within generation grace. Funnel diagnostics describe the warmed generation/category accounting rather than the selected league/type/ID input census. admission=most_held_48h with cohort=wider_holder opts in to the most-held selection instead of the default one: per sport, up to 8 markets kicking off within 48 hours, most graded holders first, then volume, then soonest kickoff, at most 4 per event, recomputed every refresh; every row carries admitted_by most_held_48h, the response echoes admission, and funnel, coverage and degradation describe that selection. Both wider_holder selections add departures: the markets whose card left that selection before kickoff, each with departed_at, the departed card’s last_observed_at and a reason from the funnel’s terminal vocabulary, so a reader can tell a move to view=signals (primary_slate_candidate) from a split holder pile. Every observations snapshot names in signals_as_of the view=signals snapshot whose listed markets it excluded from wider_holder. A sport’s pregame wider_holder holders are read only while a wider_holder or emerging_pile request named that sport, or named no category, within the last 30 minutes (default and most_held_48h counted apart; in-play reads never pause). A request whose sport was not read, or one without a category while any sport was not, returns 503 read_model_warming with Retry-After: 45 and restarts those reads; the retry serves the same rows as before, and that sport’s admitted markets count as capacity_limited in the global coverage counts while they were not read.

Every view accepts kind, sports, leagues, market_types, condition_ids, event_slugs, series_slugs and legacy category. Repeat array query keys or supply comma-separated values inside a key; at most 100 supplied values per axis before deduplication. Values normalize, sort and deduplicate. Lists match OR and axes match AND. Sports and machine leagues normalize case-insensitively; market_types/event_slugs/series_slugs remain trimmed exact case-sensitive provider strings. category=NFL reaches Football, while leagues=nfl selects NFL only. Selection runs before cap/pagination, binds advanced cursors/ETags, and is echoed in optional top-level selection whenever a selector or nonblank category is requested. Missing selected metadata excludes a row. Unknown well-formed market IDs or provider identities return empty results.

Each row adds required nullable market_context for authoritative discovery. A frozen observed sidecar gives required nullable league, series_slug, market_type and metadata_updated_at; a pre-selector cached generation can yield context:null on an unselected read. metadata_updated_at is exactly local market_canonical.updated_at, never a provider observation clock or current-time fallback. A selected read without sidecar readiness returns 503 read_model_warming with Retry-After: 5, never silently wider results. SDK selected reads enforce strict query validation and require the echo.

All public views read warmed generations only; missing or stale readiness returns 503 read_model_warming with Retry-After: 5. Advanced views use private,no-cache with ETag/If-None-Match/304; no request holder/provider computation. Default board has no ETag/pagination and private,no-store. Invalid view-specific parameters return 400. Replaced snapshots or changed cursor-bound filters return cursor_expired. Price marks are market midpoints, not wallet entry cost; volume is provider taker shares. Nullable/omitted evidence and db_only/degraded provenance remain explicit. Analytics is not an execution instruction.

Source

pub async fn get_edge_hunter_with_etag( &self, params: &GetEdgeHunterParams, ) -> Result<Tagged<Value>>

Get Edge Hunter, with the response’s ETag.

GET /api/v1/edge-hunter

Self::get_edge_hunter, returning the ETag validator the response carried beside the decoded body. Send it back as if_none_match on the next read: an unchanged resource answers Error::NotModified { etag }.

Source§

impl Client

Source

pub async fn open_stream(&self, options: &StreamOptions) -> Result<StreamReader>

Open GET /api/v1/stream and return a reader that delivers frames as they arrive.

A status other than 200 is an Error::Api (a 429 carries retry_after); a 200 that is not text/event-stream is an Error::Stream. The stream has no deadline.

use oxinsider::{Client, StreamOptions};

let client = Client::from_env()?;
let mut cursor: Option<i64> = None; // the last seq you processed
let mut options = StreamOptions::default().events(["WhaleTradesInserted"]);
options.last_event_id = cursor;
let mut reader = client.open_stream(&options).await?;
while let Some(frame) = reader.next().await? {
    if frame.resync {
        // The resume point is outside the retained window: refetch state, then continue.
        continue;
    }
    cursor = frame.seq;
    println!("{} {:?}", frame.event_type, frame.seq);
}
// The server closed the stream: reconnect with `cursor`.

Trait Implementations§

Source§

impl Clone for Client

Source§

fn clone(&self) -> Self

Returns a duplicate of the value. Read more
1.0.0 (const: unstable) · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for Client

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T> Instrument for T

Source§

fn instrument(self, span: Span) -> Instrumented<Self> ⓘ

Instruments this type with the provided Span, returning an Instrumented wrapper. Read more
Source§

fn in_current_span(self) -> Instrumented<Self> ⓘ

Instruments this type with the current Span, returning an Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> PolicyExt for T
where T: ?Sized,

Source§

fn and<P, B, E>(self, other: P) -> And<T, P>
where T: Sized + Policy<B, E>, P: Policy<B, E>,

Create a new Policy that returns Action::Follow only if self and other return Action::Follow. Read more
Source§

fn or<P, B, E>(self, other: P) -> Or<T, P>
where T: Sized + Policy<B, E>, P: Policy<B, E>,

Create a new Policy that returns Action::Follow if either self or other returns Action::Follow. Read more
Source§

impl<T> Same for T

Source§

type Output = T

Should always be Self
Source§

impl<T> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = !

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, !>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
Source§

impl<T> WithSubscriber for T

Source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self> ⓘ
where S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a WithDispatch wrapper. Read more
Source§

fn with_current_subscriber(self) -> WithDispatch<Self> ⓘ

Attaches the current default Subscriber to this type, returning a WithDispatch wrapper. Read more