Skip to main content

prediction-markets-api

CI Python 3.10+ License: MIT

A unified Python client for prediction markets. Currently supports Polymarket, with Kalshi and Limitless adapters planned.

Features

  • Unified API across multiple prediction market venues
  • Type-safe data models with Pydantic validation
  • Normalized data: Prices in 0..1 probability space, UTC timestamps, explicit currencies
  • Safe trading: dry_run mode ON by default
  • Async-first design with httpx
  • Well-tested with recorded API fixtures

Installation

pip install prediction-markets-api

For development:

pip install prediction-markets-api[dev]

Quick Start

Reading Market Data

import asyncio
from prediction_markets_api import PolymarketClient


async def main():
    async with PolymarketClient() as client:
        # List active markets
        async for market in client.list_markets(closed=False, limit=5):
            print(f"\n{market.question}")
            print(f"  Volume: ${market.volume:,.2f}")

            for outcome in market.outcomes:
                print(f"  - {outcome.name}: {outcome.price:.2%}")

            # Get order book for first outcome
            if market.outcomes:
                outcome = market.outcomes[0]
                book = await client.get_order_book(outcome.id)

                if book.bids and book.asks:
                    print(f"  Spread: {book.bids[0].price:.4f} / {book.asks[0].price:.4f}")


asyncio.run(main())

Getting Prices

import asyncio
from prediction_markets_api import PolymarketClient
from prediction_markets_api.models.base import OrderSide


async def main():
    async with PolymarketClient() as client:
        # Get first market with outcomes
        async for market in client.list_markets(closed=False, limit=10):
            if market.outcomes:
                outcome = market.outcomes[0]
                break

        # Get best executable prices
        buy_price = await client.get_price(outcome_id=outcome.id, side=OrderSide.BUY)
        sell_price = await client.get_price(outcome_id=outcome.id, side=OrderSide.SELL)

        print(f"Best ask (to buy): {buy_price.price:.4f}")
        print(f"Best bid (to sell): {sell_price.price:.4f}")


asyncio.run(main())

Getting Historical Trades

import asyncio
from prediction_markets_api import PolymarketClient


async def main():
    async with PolymarketClient() as client:
        # Get first market with outcomes
        async for market in client.list_markets(closed=False, limit=10):
            if market.outcomes:
                outcome = market.outcomes[0]
                break

        # Get historical trades
        async for trade in client.get_trades(outcome_id=outcome.id, limit=10):
            print(f"{trade.timestamp}: {trade.side.value} {trade.size} @ {trade.price}")


asyncio.run(main())

How It Works

Data Model Contract

All venues return data through a unified model with strict guarantees:

Price Normalization:

  • Prices are normalized to 0..1 probability space (e.g., 0.65 = 65% probability)
  • Original venue prices preserved in raw_price field for reference
  • OrderSide.BUY means buying the outcome (get_price returns best ask)
  • OrderSide.SELL means selling the outcome (get_price returns best bid)

Timestamps:

  • All timestamps are timezone-aware UTC (never naive datetimes)
  • Created dates, end dates, and trade timestamps use datetime with tzinfo=timezone.utc

Currency:

  • All amounts include explicit currency (Currency.USD, Currency.USDC)
  • No ambiguous currency-free numbers

Partial Responses:

  • Markets with incomplete data are flagged with partial=True
  • Only raise errors for invalid data, not incomplete data
  • Allows graceful degradation when APIs return partial information

Order Book Semantics:

  • Bids sorted descending (best bid = highest price first)
  • Asks sorted ascending (best ask = lowest price first)
  • Books validated to never be crossed (best_bid < best_ask)
  • Sizes: best_bid_size, best_ask_size, open_interest, liquidity

Market Status:

  • Each market includes venue_status (raw venue string) and status (normalized enum)
  • Normalized statuses: MarketStatus.OPEN, MarketStatus.CLOSED, MarketStatus.RESOLVED
  • resolved=True means the outcome is known (may still be settling)
  • winning_outcome: str | None contains the name of the winning outcome when resolved (e.g., "Yes", "No")
  • Unknown venue statuses log a warning and return status=None (not silently mapped)

Status Mapping Table:

Venue Raw Status Normalized Status Resolved Notes
Kalshi active OPEN False Market is open for trading (filter: open)
initialized CLOSED False Not yet opened, excluded by closed=None (filter: unopened)
determined RESOLVED True Outcome known, settling (filter: closed)
finalized RESOLVED True Fully settled and paid out (filter: settled)
Polymarket open OPEN False Market is open for trading (closed=false)
closed CLOSED False Market closed but outcome not yet determined
resolved RESOLVED True Market resolved, outcome prices settled to 0/1
Limitless FUNDED OPEN False Market is active and funded

Notes:

  • Kalshi filter parameter names differ from response status values. The table shows response values with filter parameters in parentheses.
  • Kalshi resolved markets with result='scalar' (e.g., cancelled events): These are marked as resolved=True with status=RESOLVED but winning_outcome=None since no yes/no outcome won. This is acceptable and documented.
  • Polymarket resolution detection:
    • No explicit resolution status fields in API response (umaResolutionStatuses always empty, resolvedBy/resolutionSource not status indicators)
    • Resolution determined by strict price settlement check: >0.999 and <0.001 thresholds
    • This minimizes false positives from markets trading near certainty but not yet formally resolved
    • Winning outcome automatically detected from settled prices
  • Limitless only exposes FUNDED markets via /markets/active endpoint. Calling list_markets(closed=True) raises NotImplementedError with a clear explanation, since the API does not provide an endpoint for closed/resolved markets.

Adapter Architecture

Each venue implements the BaseClient interface:

import asyncio
from prediction_markets_api import PolymarketClient, KalshiClient


async def main():
    # All clients provide the same methods:
    async with PolymarketClient() as client:
        async for market in client.list_markets(limit=10):  # Standard method
            print(market.question)


asyncio.run(main())

Unified Methods (Read-Only for 0.9):

  • list_markets(closed, limit, page_size, *, venue_params) - List markets with pagination
  • iter_markets(closed) - Iterate all markets (no limit)
  • get_market(market_id) - Get single market by ID
  • get_order_book(outcome_id) - Get sorted, validated order book
  • get_price(outcome_id, side) - Get best executable price
  • get_trades(outcome_id, limit, page_size) - Historical trades iterator (up to limit total, default 100)
  • iter_trades(outcome_id) - Iterate all historical trades (unbounded)

Note: Trading functionality with place_order() is planned for version 1.0. Version 0.9 is read-only.

Venue-Specific Parameters:

  • Venue-specific filters are keyword-only arguments (no **kwargs)
  • Example: client.list_markets(category="politics") for Kalshi
  • Prevents accidental parameter mismatches between venues

API Keys and Configuration

Version 0.9 (Read-Only): No authentication required. All market data, prices, order books, and trades are accessible without API keys.

Trading (Planned for 1.0): Will require API keys via environment variables.

Geographic Restrictions

Be aware of venue restrictions:

  • Polymarket: US users blocked
  • Kalshi: US only (CFTC regulated)
  • Limitless: No restrictions (planned)

Exception Hierarchy

The library provides a consistent exception hierarchy:

from prediction_markets_api import (
    PredictionMarketsError,  # Base exception
    NotFoundError,  # Market/outcome/token not found
    NoLiquidityError,  # Empty order book for existing market
    VenueAPIError,  # HTTP errors, invalid responses
    RateLimitError,  # HTTP 429 rate limit
)

Exception Types:

  • PredictionMarketsError: Base exception for all library errors
  • NotFoundError: Resource not found (markets, outcomes, tokens)
    • Attributes: resource_type, resource_id
    • Raised on HTTP 404 or empty results
  • NoLiquidityError: No liquidity available (empty order book for an existing market)
    • Attributes: market_id, side (BUY/SELL)
    • Raised by get_price() when the order book has no bids/asks on the requested side
    • Different from NotFoundError (market doesn't exist) vs. market exists but has no liquidity
  • VenueAPIError: HTTP errors, timeouts, invalid JSON responses
    • Attributes: status_code, venue, url
    • Wraps all httpx errors for consistent handling
  • RateLimitError: Rate limit exceeded (HTTP 429)
    • Subclass of VenueAPIError
    • Attributes: retry_after (seconds, from Retry-After header if available)

User Input Errors:

  • ValueError: Invalid parameters (e.g., price not in 0..1, negative size)
  • NotImplementedError: Unsupported operations (e.g., LimitlessClient.list_markets(closed=True))

Example:

import asyncio
from prediction_markets_api import PolymarketClient, NotFoundError, VenueAPIError


async def main():
    async with PolymarketClient() as client:
        try:
            market = await client.get_market("nonexistent")
        except NotFoundError as e:
            print(f"Market not found: {e.resource_id}")
        except VenueAPIError as e:
            print(f"API error: {e.status_code} from {e.venue}")


asyncio.run(main())

Data Model

All venues return data through a unified model:

Core Types

  • Market: Question, outcomes, volume, liquidity, timestamps
  • Outcome: Individual tradeable token (e.g., "Yes"/"No")
  • OrderBook: Sorted bids/asks, never crossed
  • Price: Best executable price for a side
  • Trade: Historical trade record

Key Guarantees

  1. Prices normalized to 0..1 (probability space)
    • Original venue prices preserved in raw_price field
  2. All timestamps are timezone-aware UTC
    • Never naive datetimes
  3. Amounts include explicit currency (USD, USDC)
  4. Partial responses allowed (flagged, not an error)
    • Only raise on invalid data
  5. Order books are sorted and validated
    • Bids descending, asks ascending
    • Never crossed (best_bid < best_ask)

Example: Market Object

from datetime import datetime, timezone
from decimal import Decimal
from prediction_markets_api.models.base import Market, Outcome, Currency

market_id = "0x1234..."
market = Market(
    id=market_id,
    venue="polymarket",
    question="Will Bitcoin reach $100k by Dec 31?",
    outcomes=[
        Outcome(id="token_yes", market_id=market_id, name="Yes", price=Decimal("0.65")),
        Outcome(id="token_no", market_id=market_id, name="No", price=Decimal("0.35")),
    ],
    created_at=datetime(2026, 1, 15, tzinfo=timezone.utc),
    end_date=datetime(2026, 12, 31, tzinfo=timezone.utc),
    volume=Decimal("1250000.50"),
    currency=Currency.USDC,
    partial=False,
)
print(market.question)

Examples

The examples/ directory contains runnable examples:

Compare Same Event Across Venues

python examples/compare_venues.py

Search for the same event (e.g., Bitcoin predictions) across Polymarket and Kalshi, comparing prices and volumes.

Price History Analysis

python examples/price_history.py

Fetch historical trades for an outcome and calculate price statistics, movements, and spreads.

Export to Pandas

pip install prediction-markets-api[pandas]
python examples/export_to_pandas.py

Export market data and trades to pandas DataFrames for statistical analysis, visualization, or CSV export.

See examples/README.md for details.

Trading Configuration

API Keys (Environment Variables Only)

# Polymarket
export POLYMARKET_API_KEY="your_key"
export POLYMARKET_API_SECRET="your_secret"
export POLYMARKET_WALLET_ADDRESS="0x..."

Never hardcode API keys in your code.

Safety Features

  • dry_run=True by default: Orders are validated and signed but not sent
  • max_order_size limit: Configurable per-client safety limit
  • Explicit opt-in for real trading: Must pass dry_run=False

This library is free and open source (MIT). Some links below are referral links: if you sign up through them, the venue may pay the project a share of fees or a bonus. It costs you nothing extra, and using the library never requires them. Plain links are given next to each one.

Venue Sign up Referral? Closed markets Notes
Limitless limitless.exchange yes (r=0D2RQUXIT4) no plain link: https://limitless.exchange
Polymarket polymarket.com no yes
Kalshi kalshi.com no yes

Referral links are defined in src/prediction_markets_api/venues.py (the same registry that generates this table), so you can see exactly what is linked and change it in a fork.

Trading and builder-code attribution are not part of 0.9 (read-only). How builder codes will work, what they cost and how to turn them off will be documented here before 1.0.

Supported Venues

Sign-up links and referral disclosure: see Venues & referral links.

Venue Chain Status Read Trade API Key (Read) API Key (Trade) Geo Restrictions Link
Polymarket Polygon ✅ Implemented ✅ — No Yes US blocked Link
Kalshi None ✅ Implemented ✅ — No Yes US only Link
Limitless Base ✅ Implemented ✅ — No Yes None Link
Hyperliquid Outcomes (HIP-4) Hyperliquid 🚧 Planned — — No Yes None Link
Predict.fun Base 🚧 Planned — — No Yes None Link
Opinion Unknown 🚧 Planned — — No Yes None Link
Pascal Base 🚧 Planned — — No Yes None Link
SX Bet Arbitrum 🚧 Planned — — No Yes None Link
Azuro/Bookmaker.xyz Polygon 🚧 Planned — — No Yes None Link
Jupiter Prediction Solana 🚧 Planned — — No Yes None Link
Polymarket US Unknown 🚧 Planned — — No Yes US only Link
Novig None 🚧 Planned — — No Yes None Link
World Unknown 🚧 Planned — — No Yes None Link
BetDEX Solana 🚧 Planned — — No Yes None Link

Development

Setup

git clone https://github.com/prediction-markets-api/prediction-markets-api.git
cd prediction-markets-api

python -m venv venv
source venv/bin/activate  # or `venv\Scripts\activate` on Windows

pip install -e ".[dev]"

Running Tests

# Run all tests (uses recorded fixtures, no network)
pytest

# Run with coverage
pytest --cov=src/prediction_markets_api --cov-report=html

# Run only live tests (requires network, opt-in)
pytest -m live

# Skip live tests (default)
pytest -m "not live"

Linting and Type Checking

# Format code
ruff format .

# Lint
ruff check .

# Type check
mypy src/prediction_markets_api

CI

GitHub Actions runs:

  • Lint (ruff)
  • Type check (mypy)
  • Tests on Python 3.10, 3.11, 3.12 (Linux, macOS, Windows)

All tests use recorded API fixtures (no live network calls in CI).

Architecture

prediction-markets-api/
├── src/prediction_markets_api/
│   ├── models/
│   │   └── base.py          # Unified data models
│   ├── adapters/
│   │   ├── base.py          # Common client interface
│   │   └── polymarket.py    # Polymarket adapter
│   └── __init__.py
├── tests/
│   ├── fixtures/            # Recorded API responses
│   ├── test_models.py       # Model validation tests
│   ├── test_polymarket.py   # Adapter tests (mocked)
│   └── test_polymarket_live.py  # Live smoke tests (opt-in)
└── pyproject.toml

Contributing

Contributions welcome! Please:

  1. Fork the repository
  2. Create a feature branch (git checkout -b feature/amazing-feature)
  3. Add tests for new functionality
  4. Ensure CI passes (pytest, ruff, mypy)
  5. Submit a Pull Request

License

MIT License - see LICENSE for details.

Roadmap

  • Complete Polymarket trading implementation
  • Add Kalshi adapter
  • Add Limitless adapter
  • WebSocket support for real-time data
  • Portfolio management utilities
  • Order history and fills tracking

Support


Disclaimer: This is an independent open-source project and is not affiliated with or endorsed by any prediction market platform.

Metadata

Release files for prediction-markets-api 0.9.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for prediction-markets-api 0.9.0
File Size Uploaded Provenance Actions
prediction_markets_api-0.9.0.tar.gz 33.8 kB With provenance Details for prediction_markets_api-0.9.0.tar.gz

Built distribution (wheel)

Table of built distributions (wheels) for prediction-markets-api 0.9.0
File Interpreter ABI Platform Actions
prediction_markets_api-0.9.0-py3-none-any.whl Python 3 none any Details for prediction_markets_api-0.9.0-py3-none-any.whl

Total release size: 72.1 kB

Release files / prediction_markets_api-0.9.0.tar.gz

Download URL prediction_markets_api-0.9.0.tar.gz
Size 33.8 kB
Tags Source
SHA-256 checksum
How to use checksums
8d6b6eee9c247dddb6606f1d730b8e72d78297480ea0be87993b4dd6cbf0c442
BLAKE2b-256 checksum
How to use checksums
2d5e56d36852e2f7d97b2771f96a88fc4d1677f5f16f9bd45a129244f1c39173
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 23, 2026.

Transparency log

Release files / prediction_markets_api-0.9.0-py3-none-any.whl

Download URL prediction_markets_api-0.9.0-py3-none-any.whl
Size 38.3 kB
Tags Python 3
SHA-256 checksum
How to use checksums
4a47deefe018ee6cee4c5184fa042eee124a5ba5434972d0688b082cfd9d8e55
BLAKE2b-256 checksum
How to use checksums
c7820910b4ffaf2530f5b28a621e3b54f7003fd5d781e4db5e14fa56ab85a041
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 23, 2026.

Transparency log

Security for prediction-markets-api 0.9.0

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

Full provenance

All 2 files in this release have been uploaded with provenance metadata.

Select from 2 files

Attestation for prediction_markets_api-0.9.0.tar.gz

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 23, 2026.

Transparency log

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Release history Release notifications | RSS feed

This release

0.9.0 This release

2 release files Full provenance

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page