River Markets API
Search and discover prediction markets across Kalshi and Polymarket.
Search and discover prediction markets across Kalshi and Polymarket.
Every API here is available over the APIs.io API and to AI agents over MCP.
One button, every client — Claude, Cursor, VS Code and the rest.
https://apis.io/mcp
find_apisBrowse and filter every API in the catalog.get_api_artifactsOne API's artifacts, grouped by type.get_openapiThe primary OpenAPI for this API.find_similar_apisAPIs that look like this one.apis_io_searchSTART HERE — APIs, providers and tags for one query, each with its total.resolveTurn a domain, URL or GitHub org into the provider it belongs to.find_cohortsEvery scored population of providers in the catalog.curl "https://apis.io/api/v1/apis/river-markets-markets-api"
curl "https://apis.io/api/v1/apis?limit=25"
Discovery needs no key. Ratings and market analysis are Pro.
Free tier, no form to fill in. Signing in shares your email address with us — we store it to create your key and to recognise you if you sign in with another provider. See our Privacy Policy and Terms.
A second provider on the same verified email joins the account you already have.
openapi: 3.2.0
info:
title: River Markets API
description: '# Getting Started
River Markets provides users with a unified prime brokerage interface for prediction markets, including
- Liquidity aggregation
- Order routing
- Market and data search
This API guide will help you get up and running quickly.'
contact:
name: River Markets
url: https://rivermarkets.com/
license:
name: Proprietary
version: 1.0.0
servers:
- url: https://api.rivermarkets.com
security:
- SignedRequestKeyId: []
SignedRequestTimestamp: []
SignedRequestSignature: []
- BearerAuth: []
tags:
- name: Markets
description: Search and discover prediction markets across Kalshi and Polymarket.
paths:
/v1/markets/search:
get:
tags:
- Markets
summary: Search Markets
description: 'Search and browse markets across all exchanges.
Supports:
- Exact match on ticker, condition_id, slug, river_id
- Full-text search on name, description, tags
- Browse by filters (no query required)
- Results ranked by relevance
We default to active markets in the view so things are faster'
operationId: search_markets_v1_markets_search_get
parameters:
- name: q
in: query
required: false
schema:
anyOf:
- type: string
minLength: 1
- type: 'null'
description: Search query
title: Q
description: Search query
- name: exchange_name
in: query
required: false
schema:
anyOf:
- type: string
- type: 'null'
description: Filter by exchange name (KALSHI, POLYMARKET)
title: Exchange Name
description: Filter by exchange name (KALSHI, POLYMARKET)
- name: category
in: query
required: false
schema:
anyOf:
- type: array
items:
type: string
- type: 'null'
description: Filter by canonical category. Repeat the param to filter to multiple categories (Sports, Crypto, Politics, Finance, Entertainment, Science & Tech, Weather, World Affairs, Health, Social, Other).
title: Category
description: Filter by canonical category. Repeat the param to filter to multiple categories (Sports, Crypto, Politics, Finance, Entertainment, Science & Tech, Weather, World Affairs, Health, Social, Other).
- name: subcategory
in: query
required: false
schema:
anyOf:
- type: string
- type: 'null'
description: Filter by subcategory (e.g. Basketball, Football)
title: Subcategory
description: Filter by subcategory (e.g. Basketball, Football)
- name: status
in: query
required: false
schema:
anyOf:
- $ref: '#/components/schemas/InstrumentStatus'
- type: 'null'
description: Filter by instrument status
title: Status
description: Filter by instrument status
- name: expiration_date_start
in: query
required: false
schema:
anyOf:
- type: string
format: date-time
- type: 'null'
description: Start of expiration date range (inclusive, ISO 8601)
title: Expiration Date Start
description: Start of expiration date range (inclusive, ISO 8601)
- name: expiration_date_end
in: query
required: false
schema:
anyOf:
- type: string
format: date-time
- type: 'null'
description: End of expiration date range (exclusive, ISO 8601)
title: Expiration Date End
description: End of expiration date range (exclusive, ISO 8601)
- name: start_datetime_after
in: query
required: false
schema:
anyOf:
- type: string
format: date-time
- type: 'null'
description: Filter to markets with start_datetime >= this (ISO 8601)
title: Start Datetime After
description: Filter to markets with start_datetime >= this (ISO 8601)
- name: start_datetime_before
in: query
required: false
schema:
anyOf:
- type: string
format: date-time
- type: 'null'
description: Filter to markets with start_datetime < this (ISO 8601)
title: Start Datetime Before
description: Filter to markets with start_datetime < this (ISO 8601)
- name: event_ticker
in: query
required: false
schema:
anyOf:
- type: string
- type: 'null'
description: Filter by event_ticker (exact match)
title: Event Ticker
description: Filter by event_ticker (exact match)
- name: last_price_min
in: query
required: false
schema:
anyOf:
- type: number
maximum: 1.0
minimum: 0.0
- type: 'null'
description: Filter to markets with last_price >= this value (0.0–1.0 YES probability).
title: Last Price Min
description: Filter to markets with last_price >= this value (0.0–1.0 YES probability).
- name: last_price_max
in: query
required: false
schema:
anyOf:
- type: number
maximum: 1.0
minimum: 0.0
- type: 'null'
description: Filter to markets with last_price <= this value (0.0–1.0 YES probability).
title: Last Price Max
description: Filter to markets with last_price <= this value (0.0–1.0 YES probability).
- name: volume_min
in: query
required: false
schema:
anyOf:
- type: integer
minimum: 0
- type: 'null'
description: Filter to events whose total summed volume >= this value.
title: Volume Min
description: Filter to events whose total summed volume >= this value.
- name: sort_by
in: query
required: false
schema:
anyOf:
- type: string
- type: 'null'
description: 'Sort mode for event-based pagination: trending, volume, newest, ending-soon, start-time, price'
title: Sort By
description: 'Sort mode for event-based pagination: trending, volume, newest, ending-soon, start-time, price'
- name: limit
in: query
required: false
schema:
type: integer
maximum: 1000
minimum: 1
description: Maximum number of results
default: 20
title: Limit
description: Maximum number of results
- name: offset
in: query
required: false
schema:
type: integer
minimum: 0
description: Offset for pagination
default: 0
title: Offset
description: Offset for pagination
- name: event_limit
in: query
required: false
schema:
anyOf:
- type: integer
maximum: 2000
minimum: 1
- type: 'null'
description: Paginate by events instead of markets. Up to 200 by default; values above that require start_datetime_after or start_datetime_before.
title: Event Limit
description: Paginate by events instead of markets. Up to 200 by default; values above that require start_datetime_after or start_datetime_before.
- name: event_offset
in: query
required: false
schema:
anyOf:
- type: integer
minimum: 0
- type: 'null'
description: Event offset for event-based pagination
title: Event Offset
description: Event offset for event-based pagination
responses:
'200':
description: Successful Response
content:
application/json:
schema:
$ref: '#/components/schemas/MarketSearchResponse'
'422':
description: Validation Error
content:
application/json:
schema:
$ref: '#/components/schemas/HTTPValidationError'
/v1/markets/lookup:
get:
tags:
- Markets
summary: Lookup Markets
description: Batch lookup markets by river_ids.
operationId: lookup_markets_v1_markets_lookup_get
parameters:
- name: river_ids
in: query
required: true
schema:
type: string
description: Comma-separated list of river IDs
title: River Ids
description: Comma-separated list of river IDs
responses:
'200':
description: Successful Response
content:
application/json:
schema:
$ref: '#/components/schemas/MarketLookupResponse'
'422':
description: Validation Error
content:
application/json:
schema:
$ref: '#/components/schemas/HTTPValidationError'
/v1/markets/match:
get:
tags:
- Markets
summary: Match Market
description: Exact match a single market by Kalshi ticker or Polymarket slug. Supply exactly one.
operationId: match_market_v1_markets_match_get
parameters:
- name: ticker
in: query
required: false
schema:
anyOf:
- type: string
- type: 'null'
description: Exact Kalshi ticker (e.g. KXNBA-26-DET)
title: Ticker
description: Exact Kalshi ticker (e.g. KXNBA-26-DET)
- name: slug
in: query
required: false
schema:
anyOf:
- type: string
- type: 'null'
description: Exact Polymarket slug (e.g. will-the-denver-nuggets-win-the-2026-nba-finals)
title: Slug
description: Exact Polymarket slug (e.g. will-the-denver-nuggets-win-the-2026-nba-finals)
responses:
'200':
description: Successful Response
content:
application/json:
schema:
$ref: '#/components/schemas/MarketSearchResult'
'422':
description: Validation Error
content:
application/json:
schema:
$ref: '#/components/schemas/HTTPValidationError'
/v1/markets/match/batch:
post:
tags:
- Markets
summary: Match Markets Batch
description: 'Batch-resolve many markets by exact Kalshi ticker and/or Polymarket slug in one request.
The single-market ``/match`` endpoint costs one round-trip per identifier, so resolving
thousands of mappings serially is slow. This endpoint takes lists of tickers and/or slugs
(send them together in one call) and returns a full market row — including ``river_id``,
``ticker``, ``slug`` and ``exchange_name`` — for every identifier that matched. Identifiers
with no match come back in ``unmatched_tickers`` / ``unmatched_slugs``.'
operationId: match_markets_batch_v1_markets_match_batch_post
requestBody:
content:
application/json:
schema:
$ref: '#/components/schemas/MarketMatchBatchRequest'
required: true
responses:
'200':
description: Successful Response
content:
application/json:
schema:
$ref: '#/components/schemas/MarketMatchBatchResponse'
'422':
description: Validation Error
content:
application/json:
schema:
$ref: '#/components/schemas/HTTPValidationError'
components:
schemas:
HTTPValidationError:
properties:
detail:
items:
$ref: '#/components/schemas/ValidationError'
type: array
title: Detail
type: object
title: HTTPValidationError
InstrumentStatus:
type: string
enum:
- active
- closed
- inactive
title: InstrumentStatus
description: "Instrument status derived in instruments table from exchange-specific fields.\n\nKalshi: maps from kalshi_markets.status\n - 'active' -> ACTIVE\n - everything else (closed, determined, finalized, settled, amended, inactive, initialized) -> CLOSED\n\nPolymarket: maps from polymarket_markets.closed and polymarket_markets.active booleans\n - closed=true -> CLOSED\n - active=true (and not closed) -> ACTIVE\n - else (active=false, closed=false) -> INACTIVE"
MarketMatchBatchResponse:
properties:
results:
items:
$ref: '#/components/schemas/MarketSearchResult'
type: array
title: Results
unmatched_tickers:
items:
type: string
type: array
title: Unmatched Tickers
unmatched_slugs:
items:
type: string
type: array
title: Unmatched Slugs
type: object
required:
- results
title: MarketMatchBatchResponse
description: 'Batch exact-match response.
``results`` holds a full market row (with ``river_id``, ``ticker``, ``slug`` and
``exchange_name``) for every identifier that matched. Identifiers with no match are
echoed back in ``unmatched_tickers`` / ``unmatched_slugs`` so callers can tell the
difference between "not found" and a lookup they forgot to send.'
ValidationError:
properties:
loc:
items:
anyOf:
- type: string
- type: integer
type: array
title: Location
msg:
type: string
title: Message
type:
type: string
title: Error Type
input:
title: Input
ctx:
type: object
title: Context
type: object
required:
- loc
- msg
- type
title: ValidationError
MarketMatchBatchRequest:
properties:
tickers:
items:
type: string
type: array
title: Tickers
description: Exact Kalshi tickers to resolve (e.g. KXNBA-26-DET).
slugs:
items:
type: string
type: array
title: Slugs
description: Exact Polymarket / Polymarket-US slugs to resolve (e.g. will-the-denver-nuggets-win-the-2026-nba-finals).
type: object
title: MarketMatchBatchRequest
description: 'Batch exact-match request: resolve many Kalshi tickers / Polymarket slugs at once.'
MarketLookupResponse:
properties:
results:
items:
$ref: '#/components/schemas/MarketSearchResult'
type: array
title: Results
type: object
required:
- results
title: MarketLookupResponse
description: Schema for batch market lookup response.
MarketSearchResult:
properties:
river_id:
type: integer
title: River Id
exchange_value:
type: integer
title: Exchange Value
exchange_name:
type: string
title: Exchange Name
ticker:
anyOf:
- type: string
- type: 'null'
title: Ticker
token_id_yes:
anyOf:
- type: string
- type: 'null'
title: Token Id Yes
token_id_no:
anyOf:
- type: string
- type: 'null'
title: Token Id No
slug:
anyOf:
- type: string
- type: 'null'
title: Slug
tick_size_price:
type: number
title: Tick Size Price
tick_size_qty:
type: number
title: Tick Size Qty
tick_structure:
type: string
title: Tick Structure
default: uniform
minimum_order_size:
type: number
title: Minimum Order Size
expiration_datetime:
anyOf:
- type: string
- type: 'null'
title: Expiration Datetime
description: Market expiration datetime (UTC)
start_datetime:
anyOf:
- type: string
- type: 'null'
title: Start Datetime
description: Game/event start datetime (UTC). Populated for sports; null otherwise.
neg_risk:
type: boolean
title: Neg Risk
neg_risk_id:
anyOf:
- type: string
- type: 'null'
title: Neg Risk Id
condition_id:
anyOf:
- type: string
- type: 'null'
title: Condition Id
name:
type: string
title: Name
subtitle:
anyOf:
- type: string
- type: 'null'
title: Subtitle
description:
anyOf:
- type: string
- type: 'null'
title: Description
status:
$ref: '#/components/schemas/InstrumentStatus'
category:
type: string
title: Category
default: Other
subcategory:
anyOf:
- type: string
- type: 'null'
title: Subcategory
rank:
anyOf:
- type: number
- type: 'null'
title: Rank
event_ticker:
anyOf:
- type: string
- type: 'null'
title: Event Ticker
event_title:
anyOf:
- type: string
- type: 'null'
title: Event Title
volume:
anyOf:
- type: integer
- type: 'null'
title: Volume
volume_24h:
anyOf:
- type: integer
- type: 'null'
title: Volume 24H
series_ticker:
anyOf:
- type: string
- type: 'null'
title: Series Ticker
image:
anyOf:
- type: string
- type: 'null'
title: Image
last_price:
anyOf:
- type: number
- type: 'null'
title: Last Price
description: Most recent YES price normalized to 0.0–1.0 (probability). Kalshi cents / 100; Polymarket already 0.0–1.0. NULL when no trade has been observed.
primary_entity_name:
anyOf:
- type: string
- type: 'null'
title: Primary Entity Name
description: Resolved display name of the canonical structured-target entity (player, team, competitor, …) referenced by the market. NULL for markets without a structured target. Kalshi only — Polymarket has no equivalent concept.
type: object
required:
- river_id
- exchange_value
- exchange_name
- tick_size_price
- tick_size_qty
- minimum_order_size
- neg_risk
- name
- status
title: MarketSearchResult
description: Schema for a single market search result.
MarketSearchResponse:
properties:
query:
type: string
title: Query
results:
items:
$ref: '#/components/schemas/MarketSearchResult'
type: array
title: Results
total:
type: integer
title: Total
total_events:
anyOf:
- type: integer
- type: 'null'
title: Total Events
type: object
required:
- query
- results
- total
title: MarketSearchResponse
description: Schema for market search response.
securitySchemes:
SignedRequestKeyId:
type: apiKey
in: header
name: X-River-Key-Id
description: UUID of your API key (from Settings → API Keys).
SignedRequestTimestamp:
type: apiKey
in: header
name: X-River-Timestamp
description: Current unix seconds. Must be within 30s of server time.
SignedRequestSignature:
type: apiKey
in: header
name: X-River-Signature
description: 'Base64 Ed25519 signature over the canonical request: METHOD\nPATH\nSORTED_QUERY\nTIMESTAMP\nSHA256(body) hex. See /api-reference/authentication for the full recipe.'
BearerAuth:
type: http
scheme: bearer
description: JWT bearer token for web client authentication.