api.outcome.sh

The whole API is one endpoint.

POST SQL, get JSON rows back. The full reference fits on this page: two endpoints, five tables, and an MCP server.

POST/v1/executeGET/v1/schemaPOST/v1/mcp
01getting started

First query

  1. 01Sign in at app.outcome.sh. Magic-link email, no password and no credit card.
  2. 02Create a key under API Keys in the dashboard. Keys are shown once.
  3. 03POST your SQL:
quickstart.sh
curl -X POST https://api.outcome.sh/v1/execute \
  -H "Authorization: Bearer $OUTCOME_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"query": "SELECT max(created_time) AS last_trade FROM trades"}'

Playground

The playground runs the same queries in the browser, with the live schema, charts, and an AI analyst. No key required.

MCP

Claude, Cursor, and other MCP clients can explore the schema and run queries through the MCP server.

02authentication

API keys

Every request authenticates with an API key in the Authorization header. Keys are created and revoked in the dashboard, belong to your team, and spend the team credit balance. The same key works for the execute endpoint and the MCP server.

header
Authorization: Bearer $OUTCOME_API_KEY
  • Each key is rate limited to 60 requests per second.
  • A leaked key can spend your credits. Revoke it from the API Keys page.
  • GET /v1/schema is public and needs no key.
03the endpoint

Execute a query

POST/v1/execute

Runs a single read-only SQL statement against the warehouse and returns the rows as a JSON array. SELECT, WITH, EXPLAIN, and SHOW are permitted; anything that writes, touches system tables, or reaches external data is rejected before it runs.

Request body

querystringrequired

A single read-only SQL statement. Results are capped at 10,000 rows and 15 minutes of execution.

finalbooleanoptional

When true, applies the ClickHouse FINAL modifier so markets and trades return merged, deduplicated rows instead of occasional pre-merge duplicates. Slower and scans more; use it only when exact dedup matters.

Example

request.sh
curl -X POST https://api.outcome.sh/v1/execute \
  -H "Authorization: Bearer $OUTCOME_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"query": "SELECT ticker, yes_price FROM markets ORDER BY volume_24h DESC LIMIT 5"}'
200 OK
[
  { "ticker": "KXMENWORLDCUP-26-ES", "yes_price": 0.999 },
  { "ticker": "KXTECHLAYOFFS-26", "yes_price": 0.911 },
  { "ticker": "KXUSAALIENS-27", "yes_price": 0.074 }
]

Usage headers

Every successful response reports what the query cost:

X-Credits-Usedintegeralways

Credits charged for this query.

X-Duration-Msintegeralways

Wall-clock execution time in milliseconds.

X-Bytes-Readintegeralways

Bytes the warehouse scanned to answer the query.

X-Credits-Remainingintegerwhen charged

Team balance after the charge. Omitted when the query was not charged.

response headers
HTTP/2 200
X-Credits-Used: 3
X-Duration-Ms: 118
X-Bytes-Read: 204811520
X-Credits-Remaining: 49997
04introspection

The live schema

GET/v1/schema

Returns every queryable table with its columns and exact ClickHouse types. Public, no key required. Feed it to a code generator or an agent before it writes SQL.

request.sh
curl https://api.outcome.sh/v1/schema
200 OK
{
  "tables": [
    {
      "name": "markets",
      "columns": [
        { "name": "venue", "type": "LowCardinality(String)" },
        { "name": "ticker", "type": "String" },
        { "name": "yes_price", "type": "Float64" },
        ...
      ]
    },
    ...
  ]
}
05data reference

The tables

Kalshi is live today, more venues are on the way. The venue column tells rows apart, so queries keep working as venues are added.

Outcome is an analytical warehouse, not a trading feed: new markets, trades, and book updates land within five minutes of the venue. Build research and dashboards on it, not HFT.

Types below are simplified. For exact ClickHouse types, use GET /v1/schema.

markets

One row per market, updated continuously while it trades: title, status, current prices, volume, liquidity, and the rules text that defines resolution.

markets.sql
-- What does the market believe right now?
SELECT title, round(yes_price * 100, 1) AS chance
FROM markets
WHERE status = 'open'
ORDER BY volume_24h DESC
LIMIT 5
columns
  • venuetext
  • tickertext
  • event_tickertext
  • titletext
  • yes_sub_titletext
  • no_sub_titletext
  • statustext
  • resulttext
  • market_typetext
  • strike_typetext
  • yes_pricefloat
  • no_pricefloat
  • yes_bidtext
  • yes_asktext
  • no_bidtext
  • no_asktext
  • volumefloat
  • volume_24hfloat
  • liquiditytext
  • yes_liquidityfloat
  • no_liquidityfloat
  • notional_valuetext
  • rules_primarytext
  • rules_secondarytext
  • open_timetimestamp
  • created_timetimestamp
  • close_timetimestamp
  • expiration_timetimestamp
  • expected_expiration_timetimestamp
  • updated_timetimestamp
  • is_deleteduint8

trades

Every trade at tick level, from a market opening to its resolution. Prices are the YES and NO contract prices in dollars, count is the number of contracts.

trades.sql
-- Replay one market's price path
SELECT created_time, yes_price
FROM trades
WHERE ticker = 'KXMENWORLDCUP-26-ES'
ORDER BY created_time
columns
  • venuetext
  • trade_idtext
  • tickertext
  • yes_pricefloat
  • no_pricefloat
  • countfloat
  • taker_sidetext
  • taker_book_sidetext
  • taker_outcome_sidetext
  • created_timetimestamp
  • is_deleteduint8

orderbooks

The latest order book snapshot per market: best bids and asks on both sides, spreads, available liquidity, and the full depth levels as JSON.

orderbooks.sql
-- Tightest markets right now
SELECT ticker, yes_bid, yes_ask, spread_yes
FROM orderbooks
WHERE spread_yes IS NOT NULL
ORDER BY spread_yes ASC
LIMIT 10
columns
  • venuetext
  • tickertext
  • yes_bidfloat
  • yes_askfloat
  • no_bidfloat
  • no_askfloat
  • spread_yesfloat
  • spread_nofloat
  • yes_liquidityfloat
  • no_liquidityfloat
  • yes_levelstext
  • no_levelstext
  • updated_timetimestamp

historical_orderbooks

Every order book update, sequenced. The same shape as orderbooks plus a sequence_number, so you can reconstruct the book at any point in time.

historical_orderbooks.sql
-- Liquidity over time for one market
SELECT updated_time, yes_liquidity, no_liquidity
FROM historical_orderbooks
WHERE ticker = 'KXMENWORLDCUP-26-ES'
ORDER BY sequence_number
columns
  • venuetext
  • tickertext
  • yes_bidfloat
  • yes_askfloat
  • no_bidfloat
  • no_askfloat
  • spread_yesfloat
  • spread_nofloat
  • yes_liquidityfloat
  • no_liquidityfloat
  • yes_levelstext
  • no_levelstext
  • sequence_numberbigint
  • updated_timetimestamp

resolved_markets

The final result of every settled market, one row per resolution.

resolved_markets.sql
-- Resolution outcomes across all settled markets
SELECT result, count() AS markets
FROM resolved_markets
GROUP BY result
columns
  • venuetext
  • tickertext
  • resulttext
06model context protocol

The MCP server

POST/v1/mcp

A remote MCP server for Claude, Cursor, and any other MCP client: the agent discovers the schema and queries the market itself. Streamable HTTP (protocol 2025-06-18), authenticated with your API key, with the same read-only rules, rate limits, and billing as the execute endpoint. Nothing to install or host.

Connect a client

terminal
claude mcp add --transport http outcome https://api.outcome.sh/v1/mcp \
  --header "Authorization: Bearer YOUR_API_KEY"

Tools

list_tablesfree

List every public table with its columns and types. Takes no arguments.

describe_tablefree

Return the schema of a single table by name.

preview_tablebilled

Fetch a bounded sample of rows (up to 100) from one table, optionally restricted to specific columns. The server generates the SQL; no arbitrary queries.

run_querybilled

Execute a read-only SQL query with exactly the same rules, limits, and billing as POST /v1/execute. Returns rows plus row count, bytes read, credits used, and duration.

A typical session: list_tables once, a preview or two, then run_query. Billed tools report credits used in their results.

07billing

Credits and limits

1,000 credits equal one dollar. Only successful queries are charged: one base credit, plus one credit per 100 MB scanned, plus a time cost that rises steeply past a minute:

query durationapprox. time cost
1 second1 credit · $0.001
30 seconds11 credits · $0.011
60 seconds1,000 credits · $1.00
5 minutes5,000 credits · $5.00
10,000 rowsper result set
15 minutesper query
60 req/sper API key

Set spend limits and auto reload in Billing.

08errors

Errors

Errors return a JSON body with an error message and, where a machine needs to react, a stable code:

429 Too Many Requests
{
  "error": "team spend limit exceeded",
  "code": "team_spend_limit_exceeded"
}
400only SELECT/WITH/EXPLAIN/SHOW statements are permitted

The body is malformed, the query is empty, the SQL fails to parse, or the statement is not read-only.

401missing or invalid API key

No Authorization header, or the key is wrong or revoked.

402insufficient credit balance

The team balance cannot cover the query, or the query ran past its byte budget.

413result set exceeds the maximum of 10000 rows

The result is too large. Add a LIMIT or aggregate before returning.

429team spend limit exceeded

The key exceeded 60 requests per second, or the query would exceed the spend limit set in Billing.

503billing unavailable

Transient billing outage. Retry with backoff.

504query exceeded the time limit

The query hit the 15 minute limit.

Start querying

Magic-link sign in, no credit card.

Get started

Questions or missing data? Academics and researchers get free credits. Write to support@outcome.sh.