Unified prediction market data API - The ccxt for prediction markets
Project description
PMXT Python SDK
A unified Python interface for prediction market exchanges (Polymarket, Kalshi, Limitless, Opinion, and more).
Note: Use with a PMXT API key (hosted, recommended) or self-host the sidecar locally. Get a key at pmxt.dev/dashboard.
Installation
pip install pmxt
Requirements: Python >= 3.8. The sidecar server is bundled automatically via the pmxt-core dependency — only needed when self-hosting.
Quick Start
Get your API key at pmxt.dev/dashboard. For reads, only pmxt_api_key and wallet_address are required.
import pmxt
# Reads — pmxt_api_key + wallet_address only
client = pmxt.Polymarket(
pmxt_api_key="pmxt_live_...",
wallet_address="0xYourWalletAddress",
)
# Search for markets
markets = client.fetch_markets(query="Trump")
print(markets[0].title)
# Get outcome details
outcome = markets[0].outcomes[0]
print(f"{outcome.label}: {outcome.price * 100:.1f}%")
# Fetch historical data (use outcome.outcome_id!)
candles = client.fetch_ohlcv(
outcome.outcome_id,
resolution="1d",
limit=30,
)
# Get current order book
order_book = client.fetch_order_book(outcome.outcome_id)
spread = order_book.asks[0].price - order_book.bids[0].price
print(f"Spread: {spread * 100:.2f}%")
# Account reads
positions = client.fetch_positions()
balance = client.fetch_balance()
How it works (hosted)
When you pass pmxt_api_key, the SDK talks to the PMXT hosted services:
- Catalog requests go to
api.pmxt.dev(markets, events, order books, OHLCV, trades). - Trading requests go to
trade.pmxt.dev(orders, positions, balances). - The SDK does not spawn a local process.
- For Polymarket and Opinion, PMXT's PreFundedEscrow handles custody — you sign orders with your own key, PMXT settles on-chain.
How it works (self-hosted)
When you omit pmxt_api_key, the Python SDK manages the PMXT sidecar server for you:
- First API call: Checks if server is running
- Auto-start: Starts server if needed (takes ~1-2 seconds)
- Reuse: Multiple Python processes share the same server
- Zero config: Just import and use!
Manual server control (optional)
If you prefer to manage the server yourself:
# Disable auto-start
poly = pmxt.Polymarket(auto_start_server=False)
# Or start the server manually in a separate terminal
# $ pmxt-server
Trading
Hosted trading (recommended)
With a PMXT API key, pass pmxt_api_key, wallet_address, and private_key. The SDK auto-wraps your key into an EIP-712 signer and PMXT settles the order on-chain.
Polymarket
import pmxt
trader = pmxt.Polymarket(
pmxt_api_key="pmxt_live_...",
wallet_address="0xYourWalletAddress",
private_key="0xYourPrivateKey",
)
balance = trader.fetch_balance()
print(f"Available: ${balance[0].available}")
order = trader.create_order(
market_id="market-uuid",
outcome_id="outcome-uuid",
side="buy",
order_type="market",
amount=5.0,
denom="usdc",
slippage_pct=30.0,
)
print(f"Order status: {order.status}")
Opinion
import pmxt
trader = pmxt.Opinion(
pmxt_api_key="pmxt_live_...",
wallet_address="0xYourWalletAddress",
private_key="0xYourPrivateKey",
)
See the full hosted trading guide for venue support, custody model, and limits.
Self-hosted trading (advanced)
When self-hosting, you supply venue credentials directly — no pmxt_api_key. The SDK spawns a local sidecar process.
Polymarket
Requires your Polygon Private Key:
import os
import pmxt
poly = pmxt.Polymarket(
private_key=os.getenv("POLYMARKET_PRIVATE_KEY"),
proxy_address=os.getenv("POLYMARKET_PROXY_ADDRESS"), # Optional
# signature_type='gnosis-safe' (default)
)
# Check balance
balances = poly.fetch_balance()
print(f"Available: ${balances[0].available}")
# Place order (using outcome shorthand)
markets = poly.fetch_markets(query="Trump")
order = poly.create_order(
outcome=markets[0].yes,
side="buy",
type="limit",
amount=10,
price=0.55
)
Kalshi
Requires API Key and Private Key:
import os
import pmxt
kalshi = pmxt.Kalshi(
api_key=os.getenv("KALSHI_API_KEY"),
private_key=os.getenv("KALSHI_PRIVATE_KEY"),
)
# Check positions
positions = kalshi.fetch_positions()
for pos in positions:
print(f"{pos.outcome_label}: ${pos.unrealized_pnl:.2f}")
Limitless
Requires Private Key:
import os
import pmxt
limitless = pmxt.Limitless(
private_key=os.getenv("LIMITLESS_PRIVATE_KEY")
)
# Check balance
balances = limitless.fetch_balance()
print(f"Available: ${balances[0].available}")
API Reference
Market Data Methods
fetch_markets(params?)- Get active markets# Fetch recent markets poly.fetch_markets(limit=20, sort='volume') # Search by text poly.fetch_markets(query='Fed rates', limit=10) # Fetch by slug/ticker poly.fetch_markets(slug='who-will-trump-nominate-as-fed-chair')
filter_markets(markets, query)- Filter markets by keywordfetch_ohlcv(outcome_id, params)- Get historical price candlesfetch_order_book(outcome_id)- Get current order bookfetch_trades(outcome_id, params)- Get trade historyget_execution_price(order_book, side, amount)- Get execution priceget_execution_price_detailed(order_book, side, amount)- Get detailed execution info
Trading Methods (require authentication)
create_order(params)- Place a new ordercancel_order(order_id)- Cancel an open orderfetch_order(order_id)- Get order detailsfetch_open_orders(market_id?)- Get all open orders
Account Methods (require authentication)
fetch_balance()- Get account balancefetch_positions()- Get current positions
Data Models
All methods return clean Python dataclasses:
@dataclass
class UnifiedMarket:
market_id: str # Use this for create_order
title: str
outcomes: List[MarketOutcome]
volume_24h: float
liquidity: float
url: str
# ... more fields
@dataclass
class MarketOutcome:
outcome_id: str # Use this for fetch_ohlcv/fetch_order_book/fetch_trades
label: str # "Trump", "Yes", etc.
price: float # 0.0 to 1.0 (probability)
# ... more fields
See the full API reference for complete documentation.
Important Notes
Use outcome.outcome_id, not market.market_id
For deep-dive methods like fetch_ohlcv(), fetch_order_book(), and fetch_trades(), you must use the outcome ID, not the market ID:
markets = poly.fetch_markets(query="Trump")
outcome_id = markets[0].outcomes[0].outcome_id # Correct
candles = poly.fetch_ohlcv(outcome_id, ...) # Works
candles = poly.fetch_ohlcv(markets[0].market_id, ...) # Wrong!
Prices are 0.0 to 1.0
All prices represent probabilities (0.0 to 1.0). Multiply by 100 for percentages:
outcome = markets[0].outcomes[0]
print(f"Price: {outcome.price * 100:.1f}%") # "Price: 55.3%"
Timestamps are Unix milliseconds
from datetime import datetime
candle = candles[0]
dt = datetime.fromtimestamp(candle.timestamp / 1000)
print(dt)
Development
# Clone the repo
git clone https://github.com/pmxt-dev/pmxt.git
cd pmxt/sdks/python
# Install in development mode
pip install -e ".[dev]"
# Run tests
pytest
License
MIT
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file pmxt-2.49.1.tar.gz.
File metadata
- Download URL: pmxt-2.49.1.tar.gz
- Upload date:
- Size: 2.2 MB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.14.5
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
5ffbce0f3ca532e82a72a4000e32b21682ffa976f75769e5da4c767d22aaa4d9
|
|
| MD5 |
572b9ecf8922d589961bd896b830a214
|
|
| BLAKE2b-256 |
6a15cf6c4871dd32da6bd5fbde35fbb7311280e7c7685a9c1f3c2d8352e649b3
|
File details
Details for the file pmxt-2.49.1-py3-none-any.whl.
File metadata
- Download URL: pmxt-2.49.1-py3-none-any.whl
- Upload date:
- Size: 2.4 MB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.14.5
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
5a4ea9f70b1edd7c80f04c66871ea2c83285678e12f9759c9e33b584e04d68e4
|
|
| MD5 |
4fc47b5495675c5c3b8dd41923b76d67
|
|
| BLAKE2b-256 |
d7754426f147020aab9b156a79b93139b2d958ba1fecc88f1b85b662c5002b35
|