# Skewtape docs (https://docs.skewtape.com/) > Prediction-market data from eight venues in one schema. The API reference, the MCP server, monthly deltas and the error codes. Skewtape puts prediction-market data from eight venues in one schema: markets, trades, candles, fees and resolutions, with matched events that link the same question across venues. These docs cover the REST API, the MCP server for agents and the monthly delta files. ## Start here - [API reference](/reference): every route of the public API, from the OpenAPI spec. The spec itself is at [/openapi.yaml](/openapi.yaml). - [MCP server](/mcp): add Skewtape to Claude Code, Cursor, VS Code or any MCP client. - [Monthly deltas](/deltas): keep a local copy of the History snapshot current. - [Errors](/errors): every error code the API returns, and what to do about it. ## For agents - [/llms.txt](/llms.txt) lists every page with its URL. - [/llms-full.txt](/llms-full.txt) holds every page as Markdown, one after another. - Every page is also served as Markdown at its own URL with `.md` added, for example [/mcp.md](/mcp.md). A request that prefers `text/markdown` gets the Markdown at the page URL itself. The docs show no live data. Every example on these pages is static, and the API reference sends no request to the API. Powered by Kalshi Research # MCP server (https://docs.skewtape.com/mcp) > Add Skewtape to Claude Code, Cursor, VS Code or any MCP client. Search the docs for free; read markets, matched events, fills, candles and resolutions with an API key. Skewtape runs a remote MCP server at `https://api.skewtape.com/mcp` (Streamable HTTP). Add it to your agent and it can search these docs for free, and with a Skewtape API key on the Builder or Pro plan read the same data as the REST API. ## Tools | Tool | Key | Returns | Same as | | --- | --- | --- | --- | | `search_docs` | None | Docs pages that match a query, with an excerpt | | | `read_doc` | None | One docs page as Markdown | | | `get_markets` | Builder | Markets from every venue in one schema | `GET /v1/markets` | | `get_matched_events` | Builder | Cross-venue matched events with their member markets | `GET /v1/matched_events` | | `get_fills` | Builder | Trade-level fills for a market, a matched event or a venue | `GET /v1/fills` | | `get_candles` | Builder | 1m, 1h or 1d candles for a market, a matched event or a venue | `GET /v1/candles` | | `get_resolutions` | Builder | How, when and on what source each market resolved | `GET /v1/resolutions` | The docs tools never return market data. They read the same text as `/llms-full.txt`, and need no key. A `search_docs` query is at most 256 bytes and 16 words. The data tools take the same filters as their REST routes and answer a page of rows in `data`, with `next_cursor` for the next page: pass it back as `cursor`. A data tool returns 25 rows unless you ask for another `limit` (1 to 1,000). `get_markets` with a `venue` and a single `market_ids` entry reads that one market, and `get_matched_events` with an `event_id` reads that one event. ## Set up your client Create a key in the dashboard. Send it as the `X-API-Key` header, or as `Authorization: Bearer `. Leave the key out to use the docs tools only. ### Claude Code ```bash claude mcp add --transport http skewtape https://api.skewtape.com/mcp --header "X-API-Key: $SKEWTAPE_API_KEY" ``` Without a key: ```bash claude mcp add --transport http skewtape https://api.skewtape.com/mcp ``` Run `/mcp` in Claude Code to check that `skewtape` is connected. ### Cursor In `.cursor/mcp.json` (one project) or `~/.cursor/mcp.json` (every project): ```json { "mcpServers": { "skewtape": { "url": "https://api.skewtape.com/mcp", "headers": { "X-API-Key": "${env:SKEWTAPE_API_KEY}" } } } } ``` ### VS Code In `.vscode/mcp.json`. VS Code asks for the key once and stores it: ```json { "inputs": [ { "type": "promptString", "id": "skewtape-key", "description": "Skewtape API key", "password": true } ], "servers": { "skewtape": { "type": "http", "url": "https://api.skewtape.com/mcp", "headers": { "X-API-Key": "${input:skewtape-key}" } } } } ``` ### Claude Desktop For the docs tools alone, add `https://api.skewtape.com/mcp` as a custom connector. To send a key, run the server through `mcp-remote` in `claude_desktop_config.json`: ```json { "mcpServers": { "skewtape": { "command": "npx", "args": ["mcp-remote", "https://api.skewtape.com/mcp", "--header", "Authorization:${SKEWTAPE_AUTH}"], "env": { "SKEWTAPE_AUTH": "Bearer st_live_your_key" } } } } ``` ### Any other client Point a Streamable HTTP client at `https://api.skewtape.com/mcp` with the key in `X-API-Key` or `Authorization: Bearer`. The server is stateless: it keeps no session between requests and sends no notifications. ## Plans, limits and usage - The data tools need a key on the Builder or Pro plan. A key on the Free or History plan gets `forbidden_plan`, which names the plan to move to. Free and History keys, and no key at all, use the docs tools. - Each data tool call is one API call. It counts against your plan's calls a second and calls a month exactly as the REST route does, and shows in your dashboard usage under that route, for example `GET /v1/markets`. - The docs tools are free and limited to about 60 calls a minute from one address. ## Data policy The data tools follow the same data policy as the REST API. Some venues' fields are null with `blocked: true` and a `block_reason_code`. A venue whose data is withheld is left out of lists, and a read of one of its markets answers `blocked`. Matched event fields are Skewtape's own data and are never withheld; a member on a withheld venue keeps its ids. ## Errors A refused call is a tool error whose text is the REST error envelope, so your agent sees the same codes: `unauthorized`, `forbidden_plan`, `blocked`, `not_found`, `invalid_request`, `rate_limited`, `quota_exceeded` and `internal`. A `rate_limited` error carries `retry_after_seconds`, and a `quota_exceeded` error carries `resets_at`, when the month's count starts again. ```json { "error": { "code": "unauthorized", "message": "The data tools need a Skewtape API key. Send it as the X-API-Key header, or as Authorization: Bearer, in the settings of this MCP server.", "docs_url": "https://docs.skewtape.com/errors#unauthorized", "request_id": "8f2c41d07a9e4b1c" } } ``` ## Example Sample, 12 Nov 2026. A `get_matched_events` call with `{"min_confidence": 0.95, "resolution_equal": true, "limit": 1}` answers one page like this (members shortened): ```json { "data": [ { "event_id": "st_evt_00042", "question_canonical": "Will US CPI for October 2026 be above 3.0 percent year on year?", "category": "economics", "confidence": 0.97, "resolution_equal": true, "rule_note": "Both venues settle on the BLS release for October 2026.", "status": "approved", "members": [ { "venue": "polymarket", "market_id": "0x5e1c...", "pairwise_confidence": 0.97, "status": "active" }, { "venue": "kalshi", "market_id": "KXCPIYOY-26OCT-T3.0", "pairwise_confidence": 0.97, "status": "active" } ] } ], "next_cursor": "eyJ2IjoxfQ" } ``` `confidence` is how sure Skewtape is that the members are the same event, not a probability of the outcome. Powered by Kalshi Research # Monthly deltas (https://docs.skewtape.com/deltas) > Keep a local copy of the History snapshot current. Each month's delta takes last month's snapshot to this month's, with new month files whole and the reference tables as changed rows. Apply it with DuckDB. The History snapshot holds every table whole. Each month a delta takes last month's snapshot to the new one, so a local copy stays current without downloading the whole snapshot again. The delta for December 2026 lives in `deltas/2026-12/` and takes the published `history/2026-11/` to the published `history/2026-12/`. ## What a delta holds | Path | What it is | | --- | --- | | `manifest.json` | The parent and next snapshots it joins, each named by its manifest's sha256; the next snapshot's `venues` and `omitted_venues`; and one entry per changed file with its `op`, its `source` and the source's `sha256` | | `CHECKSUMS.sha256` | The sha256 of every file of the delta | | `snapshot/` | A file of the next snapshot, whole: a new or replaced month of fills or candles, a text file, and the next snapshot's `manifest.json` and `CHECKSUMS.sha256` | | `changes/.parquet` | The rows of `markets`, `resolutions`, `matched_events` or `fees` that changed, each with an `op` | Each entry of `files` in the manifest has one `op`: - `add` or `replace`: copy `snapshot/` to ``. Closed months never change in place, so a month file is new or replaced whole. - `delete`: remove ``. - `change`: apply the rows of `changes/` to the table ``. Each row has an `op` column and a `row` column holding the table's columns. With `upsert`, the row replaces the row with the same key or is added. With `delete`, the row holds its key columns only, and the row with that key is removed. The keys are: | Table | Key | | --- | --- | | `markets` | `venue`, `market_id` | | `resolutions` | `venue`, `market_id`, `seq` | | `matched_events` | `event_id` | | `fees` | `venue`, `category`, `effective_from` | Applying a delta twice gives the same result. The manifest's `schema_break` is `true` when the schema changed between the two snapshots. The reference tables then ship whole, with `replace`. A venue the next snapshot omits (`omitted_venues`) leaves through the delta: its month files are deleted and its rows are deletes by key. The manifest lists the next snapshot's `CHECKSUMS.sha256` and `manifest.json` last. Copy them last too, so a folder whose manifest is still the parent's is a delta not yet fully applied, and running the apply again finishes it. ## Apply a delta with DuckDB Run this with Python and the `duckdb` package in the folder that holds both `history/2026-11` and `deltas/2026-12`. It refuses a folder that holds neither of the delta's two snapshots, a delta whose `CHECKSUMS.sha256` does not list its `manifest.json`, any file of the delta whose sha256 is not the one its manifest names, and any path that leaves the folder. ```python import hashlib import json import pathlib import shutil import duckdb KEYS = { "markets.parquet": ["venue", "market_id"], "resolutions.parquet": ["venue", "market_id", "seq"], "matched_events.parquet": ["event_id"], "fees.parquet": ["venue", "category", "effective_from"], } def sha256(path): digest = hashlib.sha256() with open(path, "rb") as f: for block in iter(lambda: f.read(1 << 20), b""): digest.update(block) return digest.hexdigest() def inside(root, rel): """root / rel, refusing a path that would leave root.""" parts = pathlib.PurePosixPath(rel).parts if not parts or rel.startswith("/") or ".." in parts: raise ValueError(f"refusing the path {rel!r}") return root.joinpath(*parts) def apply_delta(snapshot, delta): snapshot, delta = pathlib.Path(snapshot), pathlib.Path(delta) raw = (delta / "manifest.json").read_bytes() listed = (delta / "CHECKSUMS.sha256").read_text().splitlines() if f"{hashlib.sha256(raw).hexdigest()} manifest.json" not in listed: raise ValueError("the delta's CHECKSUMS.sha256 does not list its manifest.json") manifest = json.loads(raw) parent, nxt = manifest["parent"], manifest["next"] if sha256(snapshot / "manifest.json") not in (parent["manifest_sha256"], nxt["manifest_sha256"]): raise ValueError(f"{snapshot} holds neither {parent['bundle_id']} nor {nxt['bundle_id']}") con = duckdb.connect() for f in manifest["files"]: target = inside(snapshot, f["path"]) if f["op"] == "delete": target.unlink(missing_ok=True) continue source = inside(delta, f["source"]) if sha256(source) != f["sha256"]: raise ValueError(f"{source} is not the file the delta's manifest names") if f["op"] in ("add", "replace"): target.parent.mkdir(parents=True, exist_ok=True) shutil.copyfile(source, target) elif f["op"] == "change": keys = ", ".join(KEYS[f["path"]]) new = target.with_name(target.name + ".new") con.sql( f""" SELECT * FROM ( SELECT base.* FROM read_parquet($target) AS base ANTI JOIN (SELECT row.* FROM read_parquet($changes)) AS ch USING ({keys}) UNION ALL BY NAME SELECT row.* FROM read_parquet($changes) WHERE op = 'upsert' ) ORDER BY {keys} """, params={"target": str(target), "changes": str(source)}, ).write_parquet(str(new)) new.replace(target) else: raise ValueError(f"unknown op {f['op']!r} for {f['path']}") apply_delta("history/2026-11", "deltas/2026-12") ``` The folder `history/2026-11` then holds December's snapshot. Its month and text files are byte for byte the published ones. The four reference tables hold the same rows, but DuckDB writes its own Parquet bytes, so those four files do not match `CHECKSUMS.sha256`. To compare them, compare rows. Its `manifest.json` is the published one, so next month's delta applies to it in turn. Powered by Kalshi Research # Errors (https://docs.skewtape.com/errors) > Every error code the Skewtape API returns, what happened and what to do. Each code has its own section, linked from the docs_url of every error. Every error from the API has the same shape, and its `docs_url` links to the section of this page for its code, for example `https://docs.skewtape.com/errors#invalid_request`. The MCP server's data tools return the same envelope as their tool error. ## The error shape ```json { "error": { "code": "invalid_request", "message": "[Which parameter to fix, and why]", "docs_url": "https://docs.skewtape.com/errors#invalid_request", "request_id": "[request id]", "plan": null, "resets_at": null, "upgrade_url": null, "block_reason_code": null } } ``` | Field | What it holds | | --- | --- | | `code` | The error code: one of the sections below. | | `message` | A plain sentence saying what to do. | | `docs_url` | The section of this page for the code. | | `request_id` | The same value as the `X-Request-Id` response header. Quote it when you write to us. | | `plan` | On `forbidden_plan`: the plan that unlocks the request. Null otherwise. | | `resets_at` | On `quota_exceeded`: when the monthly quota resets, in UTC. Null otherwise. | | `upgrade_url` | On `quota_exceeded`: where to change plan. Null otherwise. | | `block_reason_code` | On `blocked`: why the data policy withholds the venue's data. Null otherwise. | ## `unauthorized` HTTP 401. **What happened:** the `X-API-Key` header is missing, holds a key we do not know, or holds a key that was revoked. **What to do:** send an active key in the `X-API-Key` header. If the key was revoked, use another key on your account or create a new one in the dashboard. ## `forbidden_plan` HTTP 403. **What happened:** your plan does not include this request. **What to do:** the `plan` field names the plan that unlocks it. [Compare plans](https://www.skewtape.com/#pricing). ## `blocked` HTTP 403. **What happened:** the data policy withholds this venue's data. `block_reason_code` says why: `licence_undecided`, `licence_derived_only` or `geo_policy`. A read of one of the venue's markets answers `blocked` whether or not the market exists. **What to do:** read the other venues. Lists leave a withheld venue out, or show its fields as null with `blocked: true`. Matched event fields are Skewtape's own data and are never withheld; a member on a withheld venue keeps its ids. ## `not_found` HTTP 404. **What happened:** the resource you asked for does not exist, or your plan does not show it. The answer never says which. **What to do:** check the id and the path in your request. ## `invalid_request` HTTP 400. **What happened:** a parameter is missing, or has a value we cannot use. **What to do:** the `message` says which parameter to fix. Fix it and send the request again. ## `rate_limited` HTTP 429. **What happened:** too many requests in a short time for your plan's calls a second. **What to do:** wait the number of seconds in the `Retry-After` header, then retry. ## `quota_exceeded` HTTP 429. **What happened:** your account has used all the API calls in its monthly limit. Calls from all your keys count toward the same limit. **What to do:** `resets_at` says when the count starts again, and `upgrade_url` links to the plans. ## `internal` HTTP 500. **What happened:** something failed on our side, not in your request. **What to do:** if it keeps happening, write to [hello@skewtape.com](mailto:hello@skewtape.com) with the `request_id` from the error. Powered by Kalshi Research # API reference (https://docs.skewtape.com/reference) > Every route of the public Skewtape API, generated from its OpenAPI spec. Send your key in the X-API-Key header; every list answers a page of rows in data, with next_cursor for the next page. These pages are generated from the public OpenAPI spec, which is also served as it is at [/openapi.yaml](/openapi.yaml). Generate a client from it, or read it alongside these pages. - **Base URL:** `https://api.skewtape.com` - **Authentication:** send your API key in the `X-API-Key` header. Create keys in the dashboard. - **Pagination:** every list answers `{"data": [...], "next_cursor": ...}`. Pass `next_cursor` back as `cursor` for the next page; it is null on the last page. `limit` sets the page size. - **Errors:** every error has the same shape and links to its section of [Errors](/errors). The reference shows no live data and sends no request: there is no "try it" panel. Call the API from your own code with your key. Powered by Kalshi Research # Live feed (WebSocket) (https://docs.skewtape.com/reference/connectFeed) > WebSocket upgrade (ADR-0013, ARCHITECTURE 10). Builder and Pro plans; the plans table says how many connections an account may hold (Builder 3, Pro 10, counted across all its keys). Not served by a generated handler: this operation documents the protocol. Authentication: the X-API-Key header on the upgrade, or the first message {"op":"auth","key":"st_live_..."} within 10 seconds. Messages from the client: {"op":"subscribe","topics":["markets","resolutions","trades","event_spread"], "venues":["kalshi"],"markets":[{"venue":"polymarket","market_id":"0x..."}], "events":["st_evt_00042"],"resume_from":123} replaces the subscription (topics required; the filters are ORed and optional; the events filter, matched event ids, narrows event_spread, whose rows belong to an event and not a venue, so the venue and market filters do not narrow them; it needs that topic: with other topics alone it is refused, and beside event_spread it leaves those topics to the venue and market filters, whole when there are none; a market filter cuts a trades_batch to its markets' fills); {"op":"ping"} answers {"op":"pong"} at once, even during a replay. markets, resolutions and trades need Builder or above, event_spread Pro; a plan that loses a topic during a connection is told (error topic_not_in_plan) and keeps its other topics. Trades (E15-T3) are fills from venue streams, one event per fill with the stored row's identity (venue, tx_hash, log_index) up to 400 a second per venue, the rest of the second folded into one trades_batch; the hourly walk stores the row afterwards, and a leader that takes over re-reads the last blocks, so dedupe by identity. event_spread is one approved matched event's fee-adjusted spread, at most one event per matched event per five seconds (FeedSpread). Right after subscribed, a subscription to event_spread receives the latest spread of each event it names as snapshot messages (op snapshot, FeedSnapshot): a state with no seq, never a point to resume from; the replay then carries the other topics and live spreads follow as events, so a replay returns the latest per event, never each tick. A subscription sent before the instance has read the log once is answered error not_ready: send it again. Subscribes sent while the connection is busy (a replay, a slow write) are not queued: the latest one is applied when the connection is free, and each earlier one that never ran is answered error superseded. Messages from the server, each with op: hello (connection_id, plan, the current seq, the replay window and the reconnect schedule), subscribed (what was accepted, replay_to, masked_venues), snapshot (one FeedSnapshot: the latest spread of a matched event, no seq), event (one FeedEvent: seq, topic, kind, venue, market_id, at, payload), resync (resume_from is older than the replay window: read the API for what you missed, live events follow), reconnect (sent at 55 minutes with resume_from; the connection closes at 59 minutes, before the 60-minute platform limit), error (code, message). Replay (D2): resume_from replays the stored events after that seq that match the subscription, then switches to live with no gap and no duplicate; a client keeps the last seq it saw. Every connection also receives the feed's own gap notices (topic feed, kind gap) when a venue stream reconnected: the stored data has no gap, so read the API for that window or wait for the next hourly run. Policy (ADR-0028): a subscription naming a venue the live_data policy blocks is refused (error venue_blocked); a masked venue's events ship with the listed fields null and blocked true. Close codes: 4000 reconnect (the age limit, or a registration that expired while the server could not heartbeat; both carry resume_from), 4001 unauthorized (also when the key is revoked during the connection: keys and plans are read again every few minutes), 4003 plan (no feed, or a topic the plan no longer covers), 4008 connection limit, 4009 slow consumer (the reason carries resume_from), 4029 too many failed key lookups, 4400 bad message, 1001 server restarting. `GET /v1/feed` Base URL: `https://api.skewtape.com` WebSocket upgrade (ADR-0013, ARCHITECTURE 10). Builder and Pro plans; the plans table says how many connections an account may hold (Builder 3, Pro 10, counted across all its keys). Not served by a generated handler: this operation documents the protocol. Authentication: the X-API-Key header on the upgrade, or the first message {"op":"auth","key":"st_live_..."} within 10 seconds. Messages from the client: {"op":"subscribe","topics":["markets","resolutions","trades","event_spread"], "venues":["kalshi"],"markets":[{"venue":"polymarket","market_id":"0x..."}], "events":["st_evt_00042"],"resume_from":123} replaces the subscription (topics required; the filters are ORed and optional; the events filter, matched event ids, narrows event_spread, whose rows belong to an event and not a venue, so the venue and market filters do not narrow them; it needs that topic: with other topics alone it is refused, and beside event_spread it leaves those topics to the venue and market filters, whole when there are none; a market filter cuts a trades_batch to its markets' fills); {"op":"ping"} answers {"op":"pong"} at once, even during a replay. markets, resolutions and trades need Builder or above, event_spread Pro; a plan that loses a topic during a connection is told (error topic_not_in_plan) and keeps its other topics. Trades (E15-T3) are fills from venue streams, one event per fill with the stored row's identity (venue, tx_hash, log_index) up to 400 a second per venue, the rest of the second folded into one trades_batch; the hourly walk stores the row afterwards, and a leader that takes over re-reads the last blocks, so dedupe by identity. event_spread is one approved matched event's fee-adjusted spread, at most one event per matched event per five seconds (FeedSpread). Right after subscribed, a subscription to event_spread receives the latest spread of each event it names as snapshot messages (op snapshot, FeedSnapshot): a state with no seq, never a point to resume from; the replay then carries the other topics and live spreads follow as events, so a replay returns the latest per event, never each tick. A subscription sent before the instance has read the log once is answered error not_ready: send it again. Subscribes sent while the connection is busy (a replay, a slow write) are not queued: the latest one is applied when the connection is free, and each earlier one that never ran is answered error superseded. Messages from the server, each with op: hello (connection_id, plan, the current seq, the replay window and the reconnect schedule), subscribed (what was accepted, replay_to, masked_venues), snapshot (one FeedSnapshot: the latest spread of a matched event, no seq), event (one FeedEvent: seq, topic, kind, venue, market_id, at, payload), resync (resume_from is older than the replay window: read the API for what you missed, live events follow), reconnect (sent at 55 minutes with resume_from; the connection closes at 59 minutes, before the 60-minute platform limit), error (code, message). Replay (D2): resume_from replays the stored events after that seq that match the subscription, then switches to live with no gap and no duplicate; a client keeps the last seq it saw. Every connection also receives the feed's own gap notices (topic feed, kind gap) when a venue stream reconnected: the stored data has no gap, so read the API for that window or wait for the next hourly run. Policy (ADR-0028): a subscription naming a venue the live_data policy blocks is refused (error venue_blocked); a masked venue's events ship with the listed fields null and blocked true. Close codes: 4000 reconnect (the age limit, or a registration that expired while the server could not heartbeat; both carry resume_from), 4001 unauthorized (also when the key is revoked during the connection: keys and plans are read again every few minutes), 4003 plan (no feed, or a topic the plan no longer covers), 4008 connection limit, 4009 slow consumer (the reason carries resume_from), 4029 too many failed key lookups, 4400 bad message, 1001 server restarting. No key needed. ## Parameters | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `X-API-Key` | header | string | no | The API key; or send the auth message first. | ## Responses ### 101 Switching Protocols. The WebSocket carries the messages described above. ### 401 Missing or invalid session (or API key on data routes) ### 503 The instance is shutting down; connect again. Powered by Kalshi Research # Get one market (https://docs.skewtape.com/reference/getMarket) > One market by venue and market_id. Missing or hidden from the plan is 404; the response never says which. A venue the data policy blocks is 403 blocked, whether or not the market exists. `GET /v1/markets/{venue}/{market_id}` Base URL: `https://api.skewtape.com` One market by venue and market_id. Missing or hidden from the plan is 404; the response never says which. A venue the data policy blocks is 403 blocked, whether or not the market exists. Send your API key in the `X-API-Key` header. ## Parameters | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `venue` | path | string | yes | | | `market_id` | path | string | yes | | ## Responses ### 200 Market | Field | Type | Required | Description | | --- | --- | --- | --- | | `block_reason_code` | string,null | yes | Why fields are withheld: licence_undecided, licence_derived_only or geo_policy. Null when blocked is false. | | `blocked` | boolean | yes | True when the data policy withholds some of this row's fields for this request. Those fields are null and block_reason_code says why. | | `category` | string,null | no | Venue category label when present. | | `close_at` | string,null (date-time) | no | Time the market closed for trading, UTC. | | `first_trade_at` | string,null (date-time) | no | Time of the first known trade, UTC. | | `last_trade_at` | string,null (date-time) | no | Time of the latest known trade, UTC. | | `market_id` | string | yes | Venue-native market identifier. | | `open_at` | string,null (date-time) | no | Time the market opened for trading, UTC. | | `outcomes` | array of Outcome | yes | Selectable outcomes with labels and token ids. | | `quality_flags` | array of string | yes | Quality markers such as volume_quality_flagged. | | `question` | string,null | no | Market question text as published by the venue. | | `rules_hash` | string,null | no | Hash of rules_text used to detect rule changes. Null when the data policy withholds rules_text. | | `rules_text` | string,null | no | Full resolution rules text when available. | | `schema_version` | string | yes | Data schema version for this row. | | `slug` | string,null | no | Short public slug when the venue publishes one. | | `source_kind` | string: chain, venue_api, bulk_dataset | yes | Source that supplied the market text. | | `status` | string: open, closed, resolved, voided | yes | Life-cycle status of the market. | | `updated_at` | string (date-time) | yes | Time this row was last updated, UTC. | | `venue` | string | yes | Venue key that owns this market. | | `venue_event_id` | string,null | no | The venue's own grouping key for sibling markets, when it publishes one: for Polymarket the neg-risk market id of a neg-risk group, else the Gamma event id. Not a Skewtape matched event id. | ### 400 Bad request (validation, 11th active key, etc.) ### 401 Missing or invalid session (or API key on data routes) ### 403 forbidden_plan: the plan does not allow this request, and error.plan names the unlocking plan. blocked: the data policy withholds this venue's data, and error.block_reason_code says why. ### 404 Resource not found ### 429 rate_limited (Retry-After) or quota_exceeded (resets_at, upgrade_url). See RateLimited and QuotaExceeded response components. ### 500 Internal error; message never leaks internals Powered by Kalshi Research # Get one matched event (https://docs.skewtape.com/reference/getMatchedEvent) > One matched event by event_id. Missing or hidden from the plan is 404; the response never says which. `GET /v1/matched_events/{event_id}` Base URL: `https://api.skewtape.com` One matched event by event_id. Missing or hidden from the plan is 404; the response never says which. Send your API key in the `X-API-Key` header. ## Parameters | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `event_id` | path | string | yes | | ## Responses ### 200 Matched event | Field | Type | Required | Description | | --- | --- | --- | --- | | `category` | string,null | no | Category of the event. | | `confidence` | number (double) | yes | Overall confidence, the minimum pairwise_confidence of active members. | | `event_id` | string | yes | Stable matched event identifier. | | `members` | array of MatchedEventMember | yes | Member markets, nested. member_count and venue_count count active members and are derived, not stored. | | `method_version` | string | yes | Version of the matching method used for this matched event. | | `question_canonical` | string | yes | Canonical question this matched event stands for. | | `resolution_equal` | boolean | yes | True only if all active members resolve identically. | | `resolution_source` | string,null | no | Source text the event resolves on. | | `resolves_at` | string,null (date-time) | no | Expected resolution time, UTC. | | `reviewed_at` | string (date-time) | yes | Time of the review decision, UTC. | | `reviewer` | string | yes | Name or id of the reviewer who approved this matched event. | | `reviewer_notes` | string,null | no | Full reviewer notes. Available on Pro and above plans. | | `rule_note` | string | yes | Plain-words note on rule differences. Required when resolution_equal is false. | | `schema_version` | string | yes | Data schema version for this row. | | `status` | string: approved, needs_rereview, retired | yes | Review status of the matched event. | | `updated_at` | string (date-time) | yes | Time this matched event row was last updated, UTC. | ### 400 Bad request (validation, 11th active key, etc.) ### 401 Missing or invalid session (or API key on data routes) ### 403 Plan does not allow this request; error.plan names the unlocking plan ### 404 Resource not found ### 429 rate_limited (Retry-After) or quota_exceeded (resets_at, upgrade_url). See RateLimited and QuotaExceeded response components. ### 500 Internal error; message never leaks internals Powered by Kalshi Research # Get one two-member matched event (https://docs.skewtape.com/reference/getPair) > Thin alias of getMatchedEvent for events with exactly two active members. pair_id carries the event_id value. Missing, hidden, or not a two-member event is 404. `GET /v1/pairs/{pair_id}` Base URL: `https://api.skewtape.com` Thin alias of getMatchedEvent for events with exactly two active members. pair_id carries the event_id value. Missing, hidden, or not a two-member event is 404. Send your API key in the `X-API-Key` header. ## Parameters | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `pair_id` | path | string | yes | | ## Responses ### 200 Pair | Field | Type | Required | Description | | --- | --- | --- | --- | | `confidence` | number (double) | yes | Match confidence from 0 to 1. | | `members` | array of PairMember | yes | The two active member markets. | | `method_version` | string | yes | Version of the matching method used for this pair. | | `pair_id` | string | yes | Identifier of the two-member matched event. Carries the event_id value (st_evt_...). TODO(decision): whether pair_id keeps a distinct st_pair_ form (ARCHITECTURE 9.1). | | `resolution_equal` | boolean | yes | True when member venues would settle the same question the same way. | | `reviewed_at` | string (date-time) | yes | Time of the review decision, UTC. | | `reviewer` | string | yes | Name or id of the reviewer who approved this pair. | | `reviewer_notes` | string,null | no | Full reviewer notes. Available on Pro and above plans. | | `rule_note` | string | yes | Plain-words note on rule differences. Required when resolution_equal is false. | | `schema_version` | string | yes | Data schema version for this row. | | `status` | string: approved, needs_rereview, retired | yes | Pair status. | | `updated_at` | string (date-time) | yes | Time this pair row was last updated, UTC. | ### 400 Bad request (validation, 11th active key, etc.) ### 401 Missing or invalid session (or API key on data routes) ### 403 Plan does not allow this request; error.plan names the unlocking plan ### 404 Resource not found ### 429 rate_limited (Retry-After) or quota_exceeded (resets_at, upgrade_url). See RateLimited and QuotaExceeded response components. ### 500 Internal error; message never leaks internals Powered by Kalshi Research # List candles (https://docs.skewtape.com/reference/listCandles) > Candles from the lake in (market_id, token_id, ts) order. With event_id, active members are read in (venue, market_id) order, each in that table order. With venue alone, the same key across the venue's markets. Exactly one selector: venue with market_id, event_id alone, or venue alone. from is inclusive and clamped to the plan's history; to is exclusive and must be after from. With venue alone, from and to are at most 7 days apart. Candles count each trade once (an order book match's maker legs, a market maker trade's one row) and arrive within an hour. event_id resolves an approved matched event the caller can see and reads its active members only; members outside the plan's candle venues are left out, and members whose venue's trades rights are not raw contribute no rows. A missing or hidden event_id is 404. Free and History include Polymarket daily candles (res=1d); other venues and resolutions need the builder plan. `GET /v1/candles` Base URL: `https://api.skewtape.com` Candles from the lake in (market_id, token_id, ts) order. With event_id, active members are read in (venue, market_id) order, each in that table order. With venue alone, the same key across the venue's markets. Exactly one selector: venue with market_id, event_id alone, or venue alone. from is inclusive and clamped to the plan's history; to is exclusive and must be after from. With venue alone, from and to are at most 7 days apart. Candles count each trade once (an order book match's maker legs, a market maker trade's one row) and arrive within an hour. event_id resolves an approved matched event the caller can see and reads its active members only; members outside the plan's candle venues are left out, and members whose venue's trades rights are not raw contribute no rows. A missing or hidden event_id is 404. Free and History include Polymarket daily candles (res=1d); other venues and resolutions need the builder plan. Send your API key in the `X-API-Key` header. ## Parameters | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `venue` | query | string | no | Venue key. Required with market_id, or alone for a venue-wide range of at most 7 days. Must not be set with event_id. | | `market_id` | query | string | no | One market. Requires venue. Must not be set with event_id or with venue alone. | | `event_id` | query | string | no | Matched event id. Reads active members only, in (venue, market_id) order. Members outside the plan's candle venues are omitted. Must not be set with venue or market_id. | | `from` | query | string (date-time) | yes | Inclusive start of the time range, UTC. Clamped to the plan's history. | | `to` | query | string (date-time) | yes | Exclusive end of the time range, UTC. Must be after from. With venue alone, at most 7 days after from. | | `res` | query | string: 1m, 1h, 1d | yes | Candle bucket size. | | `limit` | query | integer | no | Page size (ARCHITECTURE 9.4). Default 100, maximum 1,000. May change between pages; the cursor does not bind to limit. | | `cursor` | query | string | no | Opaque pagination cursor from a previous next_cursor. Bound to the operation and its query filters (not limit). Tampered, malformed, foreign-route or foreign-query cursors are 400 invalid_request with no detail. | ## Responses ### 200 Candles page | Field | Type | Required | Description | | --- | --- | --- | --- | | `data` | array of Candle | yes | | | `next_cursor` | string,null | yes | Opaque cursor for the next page, or null when there is no further page (ARCHITECTURE 9.4). Pass as the cursor query parameter. | | `data[].close` | number (double) | yes | Last trade price in the bucket, derived for readers. | | `data[].decimals` | integer (int32) | yes | Scale of volume_raw in venue units. | | `data[].high` | number (double) | yes | Highest trade price in the bucket, derived for readers. | | `data[].low` | number (double) | yes | Lowest trade price in the bucket, derived for readers. | | `data[].market_id` | string | yes | Venue-native market identifier. | | `data[].open` | number (double) | yes | First trade price in the bucket, derived for readers. | | `data[].res` | string: 1m, 1h, 1d | yes | Candle bucket size: 1m, 1h or 1d. | | `data[].schema_version` | string | yes | Data schema version for this row. | | `data[].token_id` | string | yes | Token or outcome identifier for this series. | | `data[].trades` | integer (int64) | yes | Number of trades in the bucket. | | `data[].ts` | string (date-time) | yes | Bucket start time, UTC. | | `data[].venue` | string | yes | Venue key that owns this market. | | `data[].volume` | number (double) | yes | Sum of trade size in the bucket, derived for readers. | | `data[].volume_raw` | integer (int64) | yes | Exact sum of size_raw in the bucket. | ### 400 Bad request (validation, 11th active key, etc.) ### 401 Missing or invalid session (or API key on data routes) ### 403 Plan does not allow this request; error.plan names the unlocking plan ### 404 Resource not found ### 429 rate_limited (Retry-After) or quota_exceeded (resets_at, upgrade_url). See RateLimited and QuotaExceeded response components. ### 500 Internal error; message never leaks internals Powered by Kalshi Research # List fee schedules (https://docs.skewtape.com/reference/listFees) > Fee schedule rows in (venue, category, effective_from) order. Open to every plan. Optional venue filter. `GET /v1/fees` Base URL: `https://api.skewtape.com` Fee schedule rows in (venue, category, effective_from) order. Open to every plan. Optional venue filter. Send your API key in the `X-API-Key` header. ## Parameters | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `venue` | query | string | no | | | `limit` | query | integer | no | Page size (ARCHITECTURE 9.4). Default 100, maximum 1,000. May change between pages; the cursor does not bind to limit. | | `cursor` | query | string | no | Opaque pagination cursor from a previous next_cursor. Bound to the operation and its query filters (not limit). Tampered, malformed, foreign-route or foreign-query cursors are 400 invalid_request with no detail. | ## Responses ### 200 Fees page | Field | Type | Required | Description | | --- | --- | --- | --- | | `data` | array of Fee | yes | | | `next_cursor` | string,null | yes | Opaque cursor for the next page, or null when there is no further page (ARCHITECTURE 9.4). Pass as the cursor query parameter. | | `data[].category` | string | yes | Category this schedule covers, or asterisk for all. | | `data[].effective_from` | string (date-time) | yes | Start of the period this schedule is in force, UTC. | | `data[].effective_to` | string,null (date-time) | no | End of the period this schedule is in force, UTC. Null means open-ended. | | `data[].formula` | string: quadratic, complement, flat_bps, zero | yes | Fee formula. | | `data[].maker_rebate_params` | object | yes | Maker rebate parameters in micro-units keyed by name. | | `data[].min_fee_micros` | integer,null (int64) | no | Minimum fee in micro-units when the venue sets one. | | `data[].rounding` | string: none, up, down, nearest | yes | Rounding mode. | | `data[].schema_version` | string | yes | Data schema version for this row. | | `data[].source_url` | string | yes | Public URL of the fee schedule this row was taken from. | | `data[].taker_params` | object | yes | Taker fee parameters in micro-units keyed by name. | | `data[].venue` | string | yes | Venue key this fee schedule applies to. | ### 400 Bad request (validation, 11th active key, etc.) ### 401 Missing or invalid session (or API key on data routes) ### 403 Plan does not allow this request; error.plan names the unlocking plan ### 429 rate_limited (Retry-After) or quota_exceeded (resets_at, upgrade_url). See RateLimited and QuotaExceeded response components. ### 500 Internal error; message never leaks internals Powered by Kalshi Research # List fills (https://docs.skewtape.com/reference/listFills) > Fills from published lake files in (market_id, ts, log_index, tx_hash) order. With event_id, active members are read in (venue, market_id) order, each in that table order. With venue alone, the same key across the venue's markets. Exactly one selector: venue with market_id, event_id alone, or venue alone. from is inclusive and clamped to the plan's history; to is exclusive and must be after from. With venue alone, from and to are at most 7 days apart. Fills arrive within a day. event_id resolves an approved matched event the caller can see and reads its active members only; members whose venue's trades rights are not raw contribute no rows. A missing or hidden event_id is 404. Free and History plans do not include fills. `GET /v1/fills` Base URL: `https://api.skewtape.com` Fills from published lake files in (market_id, ts, log_index, tx_hash) order. With event_id, active members are read in (venue, market_id) order, each in that table order. With venue alone, the same key across the venue's markets. Exactly one selector: venue with market_id, event_id alone, or venue alone. from is inclusive and clamped to the plan's history; to is exclusive and must be after from. With venue alone, from and to are at most 7 days apart. Fills arrive within a day. event_id resolves an approved matched event the caller can see and reads its active members only; members whose venue's trades rights are not raw contribute no rows. A missing or hidden event_id is 404. Free and History plans do not include fills. Send your API key in the `X-API-Key` header. ## Parameters | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `venue` | query | string | no | Venue key. Required with market_id, or alone for a venue-wide range of at most 7 days. Must not be set with event_id. | | `market_id` | query | string | no | One market. Requires venue. Must not be set with event_id or with venue alone. | | `event_id` | query | string | no | Matched event id. Reads active members only, in (venue, market_id) order. Must not be set with venue or market_id. | | `from` | query | string (date-time) | yes | Inclusive start of the time range, UTC. Clamped to the plan's history. | | `to` | query | string (date-time) | yes | Exclusive end of the time range, UTC. Must be after from. With venue alone, at most 7 days after from. | | `leg` | query | string: maker, taker, all | no | Match leg to return. Default maker. Kalshi publishes maker rows only, so maker and all return the same rows there. | | `limit` | query | integer | no | Page size (ARCHITECTURE 9.4). Default 100, maximum 1,000. May change between pages; the cursor does not bind to limit. | | `cursor` | query | string | no | Opaque pagination cursor from a previous next_cursor. Bound to the operation and its query filters (not limit). Tampered, malformed, foreign-route or foreign-query cursors are 400 invalid_request with no detail. | ## Responses ### 200 Fills page | Field | Type | Required | Description | | --- | --- | --- | --- | | `data` | array of Fill | yes | | | `next_cursor` | string,null | yes | Opaque cursor for the next page, or null when there is no further page (ARCHITECTURE 9.4). Pass as the cursor query parameter. | | `data[].block_number` | integer,null (int64) | no | Block number when known. Null when the venue has no blocks. | | `data[].block_reason_code` | string,null | yes | Why fields are withheld: licence_undecided, licence_derived_only or geo_policy. Null when blocked is false. | | `data[].blocked` | boolean | yes | True when the data policy withholds some of this row's fields for this request. Those fields are null and block_reason_code says why. | | `data[].contract_version` | string | yes | Venue contract version such as v1, v2 or v3; for polymarket the V1 exchanges, the CTF V2 exchanges and ExchangeV3. | | `data[].decimals` | integer (int32) | yes | Scale of the raw size and notional columns. | | `data[].fee_raw` | integer,null (int64) | no | Exact fee in venue raw units when known. | | `data[].leg` | string: maker, taker | yes | Match leg: maker or taker. | | `data[].log_index` | integer (int64) | yes | Log index on chain, or the venue trade sequence when there is no log. | | `data[].maker` | string | yes | Maker address when published. | | `data[].market_id` | string | yes | Venue-native market identifier. | | `data[].match_model` | string: amm, clob | yes | How the trade was matched: clob for an order book match, amm for a trade against an automated market maker pool, where the row is the whole trade with the pool as maker. Rows written before 1.2.0 are clob. | | `data[].notional_raw` | integer (int64) | yes | Exact notional in venue raw units. | | `data[].price` | number,null (double) | yes | Derived convenience price equal to notional_raw divided by size_raw. Null when the data policy withholds it (blocked is true). | | `data[].schema_version` | string | yes | Data schema version for this row. | | `data[].side` | string: buy, sell | yes | Buy or sell from the taker view. | | `data[].size_raw` | integer (int64) | yes | Exact size in venue raw units. | | `data[].taker` | string | yes | Taker address when published. | | `data[].token_id` | string | yes | Token or outcome identifier for this fill. | | `data[].ts` | string (date-time) | yes | Fill time from the block or venue clock, UTC. | | `data[].tx_hash` | string | yes | Transaction hash of the fill. Part of its identity with venue and log_index. | | `data[].venue` | string | yes | Venue key that owns this market. | ### 400 Bad request (validation, 11th active key, etc.) ### 401 Missing or invalid session (or API key on data routes) ### 403 Plan does not allow this request; error.plan names the unlocking plan ### 404 Resource not found ### 429 rate_limited (Retry-After) or quota_exceeded (resets_at, upgrade_url). See RateLimited and QuotaExceeded response components. ### 500 Internal error; message never leaks internals Powered by Kalshi Research # List markets (https://docs.skewtape.com/reference/listMarkets) > Markets in (venue, market_id) order. Filter by venue, up to 100 market_id values, and updated_since. Rights and plan rules apply in the dataset door. `GET /v1/markets` Base URL: `https://api.skewtape.com` Markets in (venue, market_id) order. Filter by venue, up to 100 market_id values, and updated_since. Rights and plan rules apply in the dataset door. Send your API key in the `X-API-Key` header. ## Parameters | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `venue` | query | string | no | Restrict to one venue key. | | `market_id` | query | array of string | no | Restrict to these market ids (at most 100). | | `updated_since` | query | string (date-time) | no | Only markets updated at or after this UTC time. | | `limit` | query | integer | no | Page size (ARCHITECTURE 9.4). Default 100, maximum 1,000. May change between pages; the cursor does not bind to limit. | | `cursor` | query | string | no | Opaque pagination cursor from a previous next_cursor. Bound to the operation and its query filters (not limit). Tampered, malformed, foreign-route or foreign-query cursors are 400 invalid_request with no detail. | ## Responses ### 200 Markets page | Field | Type | Required | Description | | --- | --- | --- | --- | | `data` | array of Market | yes | | | `next_cursor` | string,null | yes | Opaque cursor for the next page, or null when there is no further page (ARCHITECTURE 9.4). Pass as the cursor query parameter. | | `data[].block_reason_code` | string,null | yes | Why fields are withheld: licence_undecided, licence_derived_only or geo_policy. Null when blocked is false. | | `data[].blocked` | boolean | yes | True when the data policy withholds some of this row's fields for this request. Those fields are null and block_reason_code says why. | | `data[].category` | string,null | no | Venue category label when present. | | `data[].close_at` | string,null (date-time) | no | Time the market closed for trading, UTC. | | `data[].first_trade_at` | string,null (date-time) | no | Time of the first known trade, UTC. | | `data[].last_trade_at` | string,null (date-time) | no | Time of the latest known trade, UTC. | | `data[].market_id` | string | yes | Venue-native market identifier. | | `data[].open_at` | string,null (date-time) | no | Time the market opened for trading, UTC. | | `data[].outcomes` | array of Outcome | yes | Selectable outcomes with labels and token ids. | | `data[].quality_flags` | array of string | yes | Quality markers such as volume_quality_flagged. | | `data[].question` | string,null | no | Market question text as published by the venue. | | `data[].rules_hash` | string,null | no | Hash of rules_text used to detect rule changes. Null when the data policy withholds rules_text. | | `data[].rules_text` | string,null | no | Full resolution rules text when available. | | `data[].schema_version` | string | yes | Data schema version for this row. | | `data[].slug` | string,null | no | Short public slug when the venue publishes one. | | `data[].source_kind` | string: chain, venue_api, bulk_dataset | yes | Source that supplied the market text. | | `data[].status` | string: open, closed, resolved, voided | yes | Life-cycle status of the market. | | `data[].updated_at` | string (date-time) | yes | Time this row was last updated, UTC. | | `data[].venue` | string | yes | Venue key that owns this market. | | `data[].venue_event_id` | string,null | no | The venue's own grouping key for sibling markets, when it publishes one: for Polymarket the neg-risk market id of a neg-risk group, else the Gamma event id. Not a Skewtape matched event id. | ### 400 Bad request (validation, 11th active key, etc.) ### 401 Missing or invalid session (or API key on data routes) ### 403 Plan does not allow this request; error.plan names the unlocking plan ### 429 rate_limited (Retry-After) or quota_exceeded (resets_at, upgrade_url). See RateLimited and QuotaExceeded response components. ### 500 Internal error; message never leaks internals Powered by Kalshi Research # List matched events (https://docs.skewtape.com/reference/listMatchedEvents) > Matched events in event_id order. Filters: min_confidence, resolution_equal, venue (any active member), status. Free and History keep events whose resolves_at is null or at most 30 days in the past. Plans see status approved only. `GET /v1/matched_events` Base URL: `https://api.skewtape.com` Matched events in event_id order. Filters: min_confidence, resolution_equal, venue (any active member), status. Free and History keep events whose resolves_at is null or at most 30 days in the past. Plans see status approved only. Send your API key in the `X-API-Key` header. ## Parameters | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `min_confidence` | query | number (double) | no | Minimum overall confidence (inclusive). | | `resolution_equal` | query | boolean | no | | | `venue` | query | string | no | Restrict to events with an active member on this venue. | | `status` | query | string: approved, needs_rereview, retired | no | | | `limit` | query | integer | no | Page size (ARCHITECTURE 9.4). Default 100, maximum 1,000. May change between pages; the cursor does not bind to limit. | | `cursor` | query | string | no | Opaque pagination cursor from a previous next_cursor. Bound to the operation and its query filters (not limit). Tampered, malformed, foreign-route or foreign-query cursors are 400 invalid_request with no detail. | ## Responses ### 200 Matched events page | Field | Type | Required | Description | | --- | --- | --- | --- | | `data` | array of MatchedEvent | yes | | | `next_cursor` | string,null | yes | Opaque cursor for the next page, or null when there is no further page (ARCHITECTURE 9.4). Pass as the cursor query parameter. | | `data[].category` | string,null | no | Category of the event. | | `data[].confidence` | number (double) | yes | Overall confidence, the minimum pairwise_confidence of active members. | | `data[].event_id` | string | yes | Stable matched event identifier. | | `data[].members` | array of MatchedEventMember | yes | Member markets, nested. member_count and venue_count count active members and are derived, not stored. | | `data[].method_version` | string | yes | Version of the matching method used for this matched event. | | `data[].question_canonical` | string | yes | Canonical question this matched event stands for. | | `data[].resolution_equal` | boolean | yes | True only if all active members resolve identically. | | `data[].resolution_source` | string,null | no | Source text the event resolves on. | | `data[].resolves_at` | string,null (date-time) | no | Expected resolution time, UTC. | | `data[].reviewed_at` | string (date-time) | yes | Time of the review decision, UTC. | | `data[].reviewer` | string | yes | Name or id of the reviewer who approved this matched event. | | `data[].reviewer_notes` | string,null | no | Full reviewer notes. Available on Pro and above plans. | | `data[].rule_note` | string | yes | Plain-words note on rule differences. Required when resolution_equal is false. | | `data[].schema_version` | string | yes | Data schema version for this row. | | `data[].status` | string: approved, needs_rereview, retired | yes | Review status of the matched event. | | `data[].updated_at` | string (date-time) | yes | Time this matched event row was last updated, UTC. | ### 400 Bad request (validation, 11th active key, etc.) ### 401 Missing or invalid session (or API key on data routes) ### 403 Plan does not allow this request; error.plan names the unlocking plan ### 429 rate_limited (Retry-After) or quota_exceeded (resets_at, upgrade_url). See RateLimited and QuotaExceeded response components. ### 500 Internal error; message never leaks internals Powered by Kalshi Research # List two-member matched events (https://docs.skewtape.com/reference/listPairs) > Thin alias of matched events with exactly two active members, in the old pair shape (ARCHITECTURE 9.1). Same filters as matched_events. Derived server-side from the same dataset read. `GET /v1/pairs` Base URL: `https://api.skewtape.com` Thin alias of matched events with exactly two active members, in the old pair shape (ARCHITECTURE 9.1). Same filters as matched_events. Derived server-side from the same dataset read. Send your API key in the `X-API-Key` header. ## Parameters | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `min_confidence` | query | number (double) | no | | | `resolution_equal` | query | boolean | no | | | `venue` | query | string | no | | | `status` | query | string: approved, needs_rereview, retired | no | | | `limit` | query | integer | no | Page size (ARCHITECTURE 9.4). Default 100, maximum 1,000. May change between pages; the cursor does not bind to limit. | | `cursor` | query | string | no | Opaque pagination cursor from a previous next_cursor. Bound to the operation and its query filters (not limit). Tampered, malformed, foreign-route or foreign-query cursors are 400 invalid_request with no detail. | ## Responses ### 200 Pairs page | Field | Type | Required | Description | | --- | --- | --- | --- | | `data` | array of Pair | yes | | | `next_cursor` | string,null | yes | Opaque cursor for the next page, or null when there is no further page (ARCHITECTURE 9.4). Pass as the cursor query parameter. | | `data[].confidence` | number (double) | yes | Match confidence from 0 to 1. | | `data[].members` | array of PairMember | yes | The two active member markets. | | `data[].method_version` | string | yes | Version of the matching method used for this pair. | | `data[].pair_id` | string | yes | Identifier of the two-member matched event. Carries the event_id value (st_evt_...). TODO(decision): whether pair_id keeps a distinct st_pair_ form (ARCHITECTURE 9.1). | | `data[].resolution_equal` | boolean | yes | True when member venues would settle the same question the same way. | | `data[].reviewed_at` | string (date-time) | yes | Time of the review decision, UTC. | | `data[].reviewer` | string | yes | Name or id of the reviewer who approved this pair. | | `data[].reviewer_notes` | string,null | no | Full reviewer notes. Available on Pro and above plans. | | `data[].rule_note` | string | yes | Plain-words note on rule differences. Required when resolution_equal is false. | | `data[].schema_version` | string | yes | Data schema version for this row. | | `data[].status` | string: approved, needs_rereview, retired | yes | Pair status. | | `data[].updated_at` | string (date-time) | yes | Time this pair row was last updated, UTC. | ### 400 Bad request (validation, 11th active key, etc.) ### 401 Missing or invalid session (or API key on data routes) ### 403 Plan does not allow this request; error.plan names the unlocking plan ### 429 rate_limited (Retry-After) or quota_exceeded (resets_at, upgrade_url). See RateLimited and QuotaExceeded response components. ### 500 Internal error; message never leaks internals Powered by Kalshi Research # List resolution events (https://docs.skewtape.com/reference/listResolutions) > Resolution events in (venue, market_id, seq) order. Filter by venue and created_since. `GET /v1/resolutions` Base URL: `https://api.skewtape.com` Resolution events in (venue, market_id, seq) order. Filter by venue and created_since. Send your API key in the `X-API-Key` header. ## Parameters | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `venue` | query | string | no | | | `created_since` | query | string (date-time) | no | Only events created at or after this UTC time. | | `limit` | query | integer | no | Page size (ARCHITECTURE 9.4). Default 100, maximum 1,000. May change between pages; the cursor does not bind to limit. | | `cursor` | query | string | no | Opaque pagination cursor from a previous next_cursor. Bound to the operation and its query filters (not limit). Tampered, malformed, foreign-route or foreign-query cursors are 400 invalid_request with no detail. | ## Responses ### 200 Resolutions page | Field | Type | Required | Description | | --- | --- | --- | --- | | `data` | array of Resolution | yes | | | `next_cursor` | string,null | yes | Opaque cursor for the next page, or null when there is no further page (ARCHITECTURE 9.4). Pass as the cursor query parameter. | | `data[].created_at` | string (date-time) | yes | Time this event row was recorded, UTC. | | `data[].event_type` | string: pending, proposed, disputed, reset, resolved, reversed, voided | yes | Resolution step. | | `data[].is_current` | boolean | yes | True when this event is the current state of the market. | | `data[].market_id` | string | yes | Venue-native market identifier. | | `data[].outcome` | string,null | no | Outcome label when this event sets or proposes one. | | `data[].resolved_at` | string,null (date-time) | no | Time associated with this resolution step, UTC. | | `data[].schema_version` | string | yes | Data schema version for this row. | | `data[].seq` | integer (int32) | yes | Order of events for this market. | | `data[].source_kind` | string: chain, venue_api, bulk_dataset | yes | Source of this event. | | `data[].source_ref` | string,null | no | Transaction hash or API reference for this event. | | `data[].source_text` | string,null | no | Source text that explains the resolution step. | | `data[].venue` | string | yes | Venue key that owns this market. | ### 400 Bad request (validation, 11th active key, etc.) ### 401 Missing or invalid session (or API key on data routes) ### 403 Plan does not allow this request; error.plan names the unlocking plan ### 429 rate_limited (Retry-After) or quota_exceeded (resets_at, upgrade_url). See RateLimited and QuotaExceeded response components. ### 500 Internal error; message never leaks internals Powered by Kalshi Research # List enabled venues (https://docs.skewtape.com/reference/listVenues) > Every enabled venue with its key, beta and flags; rights per kind (raw, derived_only or none, without basis or evidence); history_from per kind (null when unknown); and resolution coverage from venue_health when present. Not paged. `GET /v1/venues` Base URL: `https://api.skewtape.com` Every enabled venue with its key, beta and flags; rights per kind (raw, derived_only or none, without basis or evidence); history_from per kind (null when unknown); and resolution coverage from venue_health when present. Not paged. Send your API key in the `X-API-Key` header. ## Responses ### 200 Enabled venues | Field | Type | Required | Description | | --- | --- | --- | --- | | `data` | array of Venue | yes | | | `data[].beta` | boolean | yes | True when the venue is still in beta. | | `data[].coverage` | VenueCoverage | no | | | `data[].flags` | array of string | yes | Venue flags from venue_config. | | `data[].key` | string | yes | Venue key. | | `data[].rights` | array of VenueKindRights | yes | Effective rights and history_from per kind. | ### 400 Bad request (validation, 11th active key, etc.) ### 401 Missing or invalid session (or API key on data routes) ### 403 Plan does not allow this request; error.plan names the unlocking plan ### 429 rate_limited (Retry-After) or quota_exceeded (resets_at, upgrade_url). See RateLimited and QuotaExceeded response components. ### 500 Internal error; message never leaks internals Powered by Kalshi Research