pmxt is the CCXT for prediction markets: one Python SDK to read data and trade across 15+ venues, including Polymarket, Kalshi, Limitless, Probable, Baozi, Myriad, Opinion, Metaculus, Smarkets, and Hyperliquid. Install with pip install pmxt, initialize an exchange, and use the same method signatures regardless of platform. With Dome API absorbed into Polymarket, pmxt is the only independent unified prediction market SDK still standing.
Updated August 2026: This guide now covers pmxt v2.54.0 (PyPI). The big changes since earlier versions: a hosted mode that trades through pmxt’s own infrastructure with a single API key (flagship path since v2.18.0), the
pmxt.Routerclass for cross-venue queries and arbitrage, a newcreate_order()signature usingmarket_id+outcome_idstrings, and apmxt.servernamespace replacing the oldstop_server()/restart_server()functions.
Why pmxt Exists
Prediction markets are fragmented. Polymarket uses EIP-712 signatures on Polygon. Kalshi uses RSA key authentication over REST. Limitless uses its own EIP-712 variant. Each platform has different data formats, different price conventions, and different SDKs.
If you’re building an agent that needs to scan markets across multiple exchanges — for arbitrage, for intelligence gathering, for portfolio management — you’d normally maintain a dozen separate API integrations. pmxt eliminates that. One pip install, one set of method names, every exchange.
The timing matters: Polymarket acquired Dome (the unified API startup from Y Combinator’s Fall 2025 batch) in February 2026. Dome raised $5.2M and had 50+ developers building on it, but its standalone API is now being folded into Polymarket’s internal tooling. That leaves pmxt as the only open-source, exchange-neutral prediction market SDK available. If you were on Dome, you need to migrate. If you’re starting fresh, pmxt is the default choice.
For context on how pmxt fits into the full autonomous agent architecture, see The Agent Betting Stack Explained. pmxt operates at Layer 3 — Trading, handling market execution while other layers handle identity, wallet management, and intelligence.
Prerequisites
pmxt has an unusual dependency: it requires both Python and Node.js.
The library uses a sidecar architecture — a local Node.js server runs on port 3847 and handles all exchange communication. The Python SDK is a wrapper that sends requests to this sidecar over HTTP. This means:
- Python 3.8+ — for the SDK itself
- Node.js 18+ — must be installed and the
nodecommand available on your PATH - pip — standard Python package manager
Verify Node.js is accessible:
node --version
# v20.x.x or higher
If you don’t have Node.js, install it from nodejs.org or via your package manager:
# macOS
brew install node
# Ubuntu/Debian
curl -fsSL https://deb.nodesource.com/setup_20.x | sudo -E bash -
sudo apt-get install -y nodejs
# Windows
winget install OpenJS.NodeJS
Installation
pip install pmxt
That’s it. The first time you instantiate an exchange, pmxt automatically downloads and starts the sidecar server. You don’t need to install anything via npm separately — the Python package handles the Node.js dependency internally.
Verify the installation:
import pmxt
api = pmxt.Polymarket()
events = api.fetch_events(query='Fed')
print(f"Found {len(events)} events")
If this prints a count without errors, you’re set. If you see a connection error, check that node is on your PATH.
Note: pmxt.Exchange is an abstract base class — older tutorials that show pmxt.Exchange() with no arguments will raise a TypeError on current versions. Instantiate a concrete venue class like pmxt.Polymarket(), or use pmxt.Router() for cross-venue queries (covered below).
Beyond the Python SDK, pmxt also ships a CLI (npm install -g @pmxt/cli) and an MCP server (npx -y @pmxt/mcp) that lets you query markets from Claude or Cursor — handy for exploration before you write any code.
Core Concepts: Events, Markets, Outcomes
pmxt normalizes prediction market data into a three-level hierarchy that’s consistent across all exchanges:
┌─────────────────────────────────────────────────────────┐
│ Event │
│ "Who will Trump nominate as Fed Chair?" │
│ │
│ ┌─────────────────────┐ ┌─────────────────────┐ │
│ │ Market │ │ Market │ │
│ │ "Kevin Warsh?" │ │ "Kevin Hassett?" │ │
│ │ │ │ │ │
│ │ ┌────┐ ┌────┐ │ │ ┌────┐ ┌────┐ │ │
│ │ │ Yes│ │ No │ │ │ │ Yes│ │ No │ │ │
│ │ │0.45│ │0.55│ │ │ │0.20│ │0.80│ │ │
│ │ └────┘ └────┘ │ │ └────┘ └────┘ │ │
│ │ (Outcomes) │ │ (Outcomes) │ │
│ └─────────────────────┘ └─────────────────────┘ │
└─────────────────────────────────────────────────────────┘
- Event — The broad topic. Contains one or more markets.
- Market — A specific tradeable question within an event.
- Outcome — The actual share you buy or sell (
Yes/No). Each outcome has anoutcome_idthat you pass to trading and data methods.
This distinction matters because different exchanges use different terminology. Polymarket calls them “conditions” and “tokens.” Kalshi calls them “events” and “markets.” pmxt unifies everything into this Event → Market → Outcome hierarchy.
Reading Market Data (No Auth Required)
All read operations work without credentials. Use pmxt.Polymarket() / pmxt.Kalshi() to target a specific exchange, or pmxt.Router() to query every supported venue at once.
Search Events
import pmxt
api = pmxt.Polymarket()
# Search by keyword
events = api.fetch_events(query='Federal Reserve')
for event in events[:5]:
print(f"{event.title}")
print(f" Markets: {len(event.markets)}")
for market in event.markets[:3]:
print(f" {market.title}: Yes={market.yes.price:.2f} No={market.no.price:.2f}")
print()
Search Markets Directly
# Search across all markets
markets = api.fetch_markets(query='Trump', limit=10)
for m in markets:
print(f"{m.title}")
print(f" Yes: {m.yes.price:.2f} | 24h Volume: ${m.volume_24h:,.0f}")
Filter Markets
pmxt includes built-in filtering with text search, structured criteria, and custom predicates:
# Structured filter: high-volume markets with low yes price
undervalued = api.filter_markets(markets, {
'volume_24h': {'min': 10000},
'price': {'outcome': 'yes', 'max': 0.3}
})
# Custom predicate: markets with big price swings
volatile = api.filter_markets(markets,
lambda m: abs(m.yes.price_change_24h) > 0.05
)
Fetch a Single Market by Slug
market = api.fetch_market(slug='will-trump-win')
print(f"{market.title}: {market.yes.price:.2f}")
Query Every Venue at Once with Router
For exchange-agnostic browsing, use pmxt.Router — it fans a single query out across all supported venues and adds cross-venue methods you won’t find on individual exchange classes:
router = pmxt.Router()
# Same market question grouped across venues
clusters = router.fetch_matched_market_clusters(query='Fed rate cut')
# Side-by-side prices for matched markets
comparison = router.compare_market_prices(query='Fed rate cut')
# SQL over the whole cross-venue dataset
rows = router.sql("SELECT title, venue, yes_price FROM markets WHERE volume_24h > 10000")
Router also powers fetch_arbitrage() and fetch_hedges(), covered in the arbitrage section below.
Price History and OHLCV Data
pmxt provides candlestick data at multiple resolutions — essential for backtesting and technical analysis.
import pmxt
poly = pmxt.Polymarket()
# Find a market
markets = poly.fetch_markets(query='Bitcoin price')
outcome_id = markets[0].yes.outcome_id # CRITICAL: use outcome_id, not market_id
# Fetch hourly candles
candles = poly.fetch_ohlcv(outcome_id, resolution='1h', limit=100)
for candle in candles[-5:]:
print(f" Open: {candle.open:.3f} High: {candle.high:.3f} "
f"Low: {candle.low:.3f} Close: {candle.close:.3f} "
f"Vol: {candle.volume:.0f}")
Available resolutions: '1m', '5m', '15m', '1h', '6h', '1d' — and recent versions accept arbitrary intervals where the venue supports them.
Common mistake: Passing the market ID instead of the outcome ID. On Polymarket, outcome_id maps to the CLOB Token ID. On Kalshi, it maps to the Market Ticker. Always use outcome.outcome_id.
Order Book Analysis
The order book reveals real liquidity — what you’ll actually pay when you trade.
import pmxt
poly = pmxt.Polymarket()
markets = poly.fetch_markets(query='Fed rate decision')
outcome = markets[0].yes
# Fetch current order book
book = poly.fetch_order_book(outcome.outcome_id)
print(f"Best bid: {book.bids[0].price:.3f} ({book.bids[0].size} contracts)")
print(f"Best ask: {book.asks[0].price:.3f} ({book.asks[0].size} contracts)")
spread = (book.asks[0].price - book.bids[0].price) * 100
print(f"Spread: {spread:.1f}%")
# Top 5 levels
print("\nBids:")
for level in book.bids[:5]:
print(f" {level.price:.3f} x {level.size}")
print("\nAsks:")
for level in book.asks[:5]:
print(f" {level.price:.3f} x {level.size}")
Simulate Execution Price
Before placing a large order, check what you’d actually pay given current liquidity:
# How much would 100 contracts cost?
avg_price = poly.get_execution_price(book, side='buy', amount=100)
print(f"Average fill price for 100 contracts: {avg_price:.4f}")
# Detailed execution with partial fill info
result = poly.get_execution_price_detailed(book, side='buy', amount=100)
print(f"Price: {result.price:.4f}")
print(f"Filled: {result.filled_amount}/{100}")
print(f"Fully filled: {result.fully_filled}")
This is critical for agent trading — you don’t want to assume best-ask pricing on a 1,000-contract order.
Hosted Mode: One API Key for Every Venue
New since v2.18.0 (June 2026): hosted mode is now pmxt’s flagship path. Instead of managing per-exchange credentials and a local sidecar, you get a single
pmxt_api_keyfrom the pmxt.dev dashboard and pmxt routes requests throughapi.pmxt.dev(data) andtrade.pmxt.dev(orders), with funds held in a PreFundedEscrow account.
pip install "pmxt[hosted]"
import pmxt
import os
exchange = pmxt.Polymarket(pmxt_api_key=os.getenv('PMXT_API_KEY'))
Three things to know before choosing hosted mode:
- Trading coverage differs. Hosted-mode order placement currently covers Polymarket, Opinion, and Limitless. Kalshi and most other venues still require self-hosting with venue-native credentials (see the next section).
fetch_balance()means something different. In self-hosted mode it returns your USDC balance on the venue’s CLOB; in hosted mode it returns your escrow USDC balance with pmxt.- Some position fields are
Optional. In hosted mode,Position.outcome_label,entry_price, andunrealized_pnlcan beNone— always guard before comparing or formatting them (the trading-bot example below does this).
The local no-credentials sidecar flow (localhost:3847) still works and remains the simplest way to start for read-only data. If you want to run the server itself on your own infrastructure, that now lives in a separate pmxt-core package — see the pmxt docs for details.
Trading: Setup and Order Placement (Self-Hosted)
Self-hosted trading requires exchange-specific credentials. Each exchange has its own authentication mechanism, but pmxt normalizes the trading interface.
Polymarket Authentication
Polymarket uses an Ethereum wallet private key. You’ll need your wallet’s private key and optionally a proxy address for proxy-wallet trading.
import pmxt
import os
exchange = pmxt.Polymarket(
private_key=os.getenv('POLYMARKET_PRIVATE_KEY'),
proxy_address=os.getenv('POLYMARKET_PROXY_ADDRESS'), # Optional
signature_type='gnosis-safe' # Default
)
For a full guide on Polymarket authentication including wallet types and signature types, see the Polymarket API Tutorial.
Kalshi Authentication
Kalshi uses RSA key-pair authentication — simpler than Polymarket’s wallet-based system.
exchange = pmxt.Kalshi(
api_key=os.getenv('KALSHI_API_KEY'),
private_key=os.getenv('KALSHI_PRIVATE_KEY') # RSA private key
)
For Kalshi-specific setup and API key generation, see the Kalshi API Guide.
Limitless Authentication
exchange = pmxt.Limitless(
api_key=os.getenv('LIMITLESS_API_KEY'),
private_key=os.getenv('LIMITLESS_PRIVATE_KEY') # EIP-712 signing key
)
Other venues follow the same pattern with their own credential parameters — Probable gained Web3 wallet authentication in v2.54.0. Check the pmxt docs for each venue’s exact parameters.
Check Balance
balance = exchange.fetch_balance()
print(f"Available: ${balance[0].available:.2f}")
Place a Limit Order
markets = exchange.fetch_markets(query='Trump')
market = markets[0]
order = exchange.create_order(
market_id=market.market_id,
outcome_id=market.yes.outcome_id,
side='buy',
type='limit',
price=0.33,
amount=100 # 100 contracts
)
print(f"Order ID: {order.id}")
print(f"Status: {order.status}")
Note the signature: create_order() takes market_id and outcome_id as strings. Older pmxt versions accepted an outcome= object parameter — that no longer exists, so update any code written against pre-2.5x releases. For advanced flows, the lower-level build_order() / submit_order() pair splits construction from submission.
Place a Market Order
order = exchange.create_order(
market_id=market.market_id,
outcome_id=market.yes.outcome_id,
side='buy',
type='market',
amount=50
)
Check Order Status
order = exchange.fetch_order(order.id)
print(f"Status: {order.status}")
print(f"Filled: {order.filled}/{order.amount}")
Cancel an Order
cancelled = exchange.cancel_order(order.id)
print(f"Cancelled: {cancelled.status}")
View Open Orders
open_orders = exchange.fetch_open_orders()
for o in open_orders:
print(f" {o.side} {o.amount} @ {o.price}")
Check Positions
positions = exchange.fetch_positions()
for pos in positions:
print(f"{pos.outcome_label}: {pos.size} shares @ ${pos.entry_price:.3f}")
print(f" Unrealized P&L: ${pos.unrealized_pnl:.2f}")
In hosted mode, outcome_label, entry_price, and unrealized_pnl may be None — check before formatting. Your fill history is available via fetch_my_trades().
WebSocket Streaming
For real-time applications — trading bots, dashboards, arbitrage scanners — polling is too slow. pmxt provides WebSocket streaming through watch_* methods.
Stream Order Book Updates
import pmxt
poly = pmxt.Polymarket()
markets = poly.fetch_markets(query='Fed Chair')
outcome = markets[0].yes
# Continuous order book stream
while True:
book = poly.watch_order_book(outcome.outcome_id)
if book.bids and book.asks:
spread = book.asks[0].price - book.bids[0].price
print(f"Bid: {book.bids[0].price:.3f} | Ask: {book.asks[0].price:.3f} | Spread: {spread:.4f}")
Stream Trades
while True:
trades = poly.watch_trades(outcome.outcome_id)
for trade in trades:
print(f"{trade.side.upper()} {trade.amount} @ {trade.price:.3f}")
Caution — known bug (August 2026): WebSocket subscription deduplication currently ignores every argument after the first, so calling watch_trades() with multiple different subscriptions can silently return data from the wrong stream. Track issue #2036 and, until it’s fixed, use one subscription per outcome or separate exchange instances for concurrent streams.
Stream Wallet Activity
while True:
activity = poly.watch_address('0xYOUR_ADDRESS', ['trades', 'positions'])
print(f"Trades: {len(activity.trades)}, Positions: {len(activity.positions)}")
Clean Up
Always close WebSocket connections when done:
poly.close()
Cross-Exchange Arbitrage Scanner
One of pmxt’s killer use cases: scanning the same market across venues simultaneously for price discrepancies. This used to require fetching from each exchange and fuzzy-matching titles yourself — pmxt.Router now does the matching for you:
import pmxt
router = pmxt.Router()
# Markets matched across venues, grouped into clusters
clusters = router.fetch_matched_market_clusters(query='Fed rate cut')
for cluster in clusters:
for m in cluster.markets:
print(f" {m.venue}: {m.title} YES={m.yes.price:.3f}")
# Or go straight to the opportunities
opportunities = router.fetch_arbitrage(query='Fed rate cut', min_spread=0.02)
for opp in opportunities:
print(f"\n{opp.title}")
print(f" Buy on {opp.buy_venue} @ {opp.buy_price:.3f}")
print(f" Sell on {opp.sell_venue} @ {opp.sell_price:.3f}")
print(f" Spread: {opp.spread:.3f} ({opp.spread*100:.1f}%)")
Router also offers fetch_hedges() for offsetting positions across venues and compare_market_prices() for a plain side-by-side view without the arbitrage math.
For a production-grade arbitrage system with fuzzy matching, risk-adjusted sizing, and execution logic, see the Cross-Market Arbitrage Guide.
Complete Trading Bot Example
Here’s a minimal but functional agent pattern — scan markets, evaluate opportunities, and place orders:
import pmxt
import os
def run_agent():
exchange = pmxt.Polymarket(
private_key=os.getenv('POLYMARKET_PRIVATE_KEY'),
proxy_address=os.getenv('POLYMARKET_PROXY_ADDRESS')
)
# 1. Check available capital
balance = exchange.fetch_balance()
available = balance[0].available
print(f"Available balance: ${available:.2f}")
if available < 10:
print("Insufficient balance")
return
# 2. Scan for opportunities
markets = exchange.fetch_markets(query='2026', limit=100)
# 3. Filter: high volume, low price (potential undervaluation)
targets = exchange.filter_markets(markets, {
'volume_24h': {'min': 5000},
'price': {'outcome': 'yes', 'max': 0.25}
})
print(f"\nFound {len(targets)} potential targets")
for market in targets[:3]:
# 4. Check order book liquidity
book = exchange.fetch_order_book(market.yes.outcome_id)
if not book.asks:
continue
spread = book.asks[0].price - book.bids[0].price if book.bids else 1.0
if spread > 0.05:
print(f" Skipping {market.title[:50]} — spread too wide ({spread:.3f})")
continue
# 5. Simulate execution
exec_price = exchange.get_execution_price(book, 'buy', 20)
print(f"\n Target: {market.title[:60]}")
print(f" Yes price: {market.yes.price:.3f}")
print(f" Avg fill for 20 contracts: {exec_price:.4f}")
print(f" Spread: {spread:.4f}")
# 6. Place order (conservative limit)
order = exchange.create_order(
market_id=market.market_id,
outcome_id=market.yes.outcome_id,
side='buy',
type='limit',
price=round(market.yes.price * 0.98, 2), # 2% below market
amount=20
)
print(f" Order placed: {order.id} ({order.status})")
# 7. Review positions
# In hosted mode, outcome_label and unrealized_pnl can be None — guard first
positions = exchange.fetch_positions()
print(f"\nActive positions: {len(positions)}")
for pos in positions[:5]:
label = pos.outcome_label or pos.outcome_id
if pos.unrealized_pnl is None:
print(f" {label}: P&L unavailable")
continue
pnl_symbol = '+' if pos.unrealized_pnl > 0 else ''
print(f" {label}: {pnl_symbol}${pos.unrealized_pnl:.2f}")
exchange.close()
if __name__ == '__main__':
run_agent()
This is a starting point. A production agent needs error handling, retry logic, rate limiting, and risk management. For the full autonomous agent architecture, see The Agent Betting Stack Explained.
Migrating from Dome API
Polymarket acquired Dome in February 2026. The standalone Dome API is being sunset. If you have existing Dome code, pmxt provides an automatic migration tool:
# Automatically convert Dome API calls to pmxt
npx dome-to-pmxt ./src
This codemod handles the common patterns: import rewrites, method name changes, and authentication restructuring.
Key Differences from Dome
| Feature | Dome API | pmxt |
|---|---|---|
| Hosting | Cloud-hosted (Dome servers) | Local sidecar (your machine) |
| Auth | Single API key from Dome | Per-exchange credentials |
| Data ownership | Dome proxied all data | Direct exchange connection |
| Cost | Paid tiers for high volume | Free and open-source (MIT) |
| Vendor risk | Acquired → sunsetting | Independent, community-driven |
| Exchanges | Polymarket, Kalshi | 15+ venues (Polymarket, Kalshi, Limitless, Metaculus, Smarkets, and more) |
The fundamental architectural difference: Dome was a hosted proxy — all your API calls went through Dome’s servers. pmxt runs entirely on your infrastructure. No third-party server in the middle, no data routing through someone else’s cloud, no vendor lock-in.
For a detailed Dome vs pmxt comparison with benchmarks, see the Dome vs pmxt vs OddsPapi Comparison.
Historical Data Archive
pmxt maintains a free data archive at archive.pmxt.dev with hourly snapshots of prediction market order book and trade data in Parquet format. Useful for:
- Backtesting trading strategies
- Training ML models on prediction market data
- Research and academic analysis
The archive covers Polymarket, Kalshi, Limitless, and Opinion data, published as hourly Parquet snapshots plus a free JSON API, licensed CC-BY 4.0.
Sidecar Architecture: What You Need to Know
pmxt’s sidecar pattern is worth understanding because it affects deployment:
┌────────────────┐ HTTP/localhost:3847 ┌─────────────────┐
│ Your Python │ ──────────────────────────────────> │ pmxt Sidecar │
│ Application │ <────────────────────────────────── │ (Node.js) │
└────────────────┘ │ │
│ ┌───────────┐ │
│ │Polymarket │ │
│ │ Adapter │ │
│ ├───────────┤ │
│ │ Kalshi │ │
│ │ Adapter │ │
│ ├───────────┤ │
│ │ Limitless │ │
│ │ Adapter │ │
│ └───────────┘ │
└─────────────────┘
Implications for deployment:
- Server environments need Node.js installed alongside Python
- Docker containers should include both Python and Node.js runtimes
- Port 3847 must be available (configurable if conflicted)
- Memory — the sidecar process consumes additional RAM for caching
- Health monitoring — you need to watch both your Python app and the sidecar
If the sidecar crashes or gets stuck, use the pmxt.server management namespace:
import pmxt
# Check status and health
print(pmxt.server.status())
print(pmxt.server.health())
# Start, stop, or restart the sidecar
pmxt.server.restart()
pmxt.server.stop()
# Tail the last 50 log lines when debugging
print(pmxt.server.logs(50))
The old top-level pmxt.stop_server() and pmxt.restart_server() functions still work but emit a DeprecationWarning — migrate to the namespace above. And if you want to run the server on separate infrastructure rather than as an auto-started sidecar, that’s now packaged separately as pmxt-core.
Python vs TypeScript SDK: Method Name Mapping
pmxt maintains both Python and TypeScript SDKs. Python uses snake_case, TypeScript uses camelCase. The methods are otherwise identical:
| Python | TypeScript | Purpose |
|---|---|---|
fetch_events() | fetchEvents() | Search events |
fetch_markets() | fetchMarkets() | Search markets |
fetch_market() | fetchMarket() | Single market lookup |
fetch_ohlcv() | fetchOHLCV() | Candlestick data |
fetch_order_book() | fetchOrderBook() | Order book depth |
fetch_trades() | fetchTrades() | Trade history |
create_order() | createOrder() | Place order |
cancel_order() | cancelOrder() | Cancel order |
fetch_order() | fetchOrder() | Order status |
fetch_open_orders() | fetchOpenOrders() | All open orders |
fetch_positions() | fetchPositions() | Current positions |
fetch_balance() | fetchBalance() | Account balance |
watch_order_book() | watchOrderBook() | Stream order book |
watch_trades() | watchTrades() | Stream trades |
outcome.outcome_id | outcome.outcomeId | Outcome identifier |
market.market_id | market.marketId | Market identifier |
filter_markets() | filterMarkets() | Filter/search |
Supported Exchanges Reference
As of v2.54.0, pmxt supports 15+ venues (full list on GitHub). Read data works everywhere without credentials. Trading support depends on the mode: hosted-mode order placement covers Polymarket, Opinion, and Limitless; everything else requires self-hosting with venue-native credentials.
| Venue | Read Data | Hosted Trading | Self-Hosted Trading |
|---|---|---|---|
| Polymarket | Yes | Yes | Yes — wallet private key + EIP-712 |
| Polymarket US | Yes | — | Venue-native credentials |
| Kalshi (+ KalshiDemo) | Yes | — | Yes — API key + RSA private key |
| Limitless | Yes | Yes | Yes — API key + EIP-712 |
| Opinion | Yes | Yes | Venue-native credentials |
| Probable | Yes | — | Yes — Web3 wallet auth (new in v2.54.0) |
| Baozi | Yes | — | Venue-native credentials |
| Myriad | Yes | — | Venue-native credentials |
| Metaculus | Yes | — | Venue-native credentials |
| Smarkets | Yes | — | Venue-native credentials |
| Hyperliquid | Yes | — | Venue-native credentials |
| Gemini Titan | Yes | — | Venue-native credentials |
| SuiBets | Yes | — | Venue-native credentials |
| Rain | Yes | — | Venue-native credentials |
| Hunch | Yes | — | Venue-native credentials |
There’s also a Mock exchange for testing and the Router class that aggregates across all venues.
Troubleshooting
“Connection refused” on startup — Node.js isn’t installed or isn’t on your PATH. Run node --version to check.
“Port 3847 already in use” — A previous sidecar instance didn’t shut down cleanly. Run pmxt.server.stop() or kill the orphaned Node process manually.
Empty results from fetch_markets() — Some exchanges only return active markets. Try broader queries or check if the exchange is accessible from your location (Polymarket is geoblocked in the US for non-KYC users on the global API).
“outcome_id not found” errors — You’re passing a market ID where an outcome ID is expected. Always use market.yes.outcome_id or market.no.outcome_id for data and trading methods.
Stale data — The sidecar caches market data. Call exchange.load_markets(reload=True) to force a fresh fetch.
Where pmxt Fits in the Agent Betting Stack
pmxt is a Layer 3 — Trading tool. It handles market execution — finding markets, analyzing prices, and placing orders. A complete autonomous betting agent needs all four layers:
| Layer | What It Does | Tools |
|---|---|---|
| Layer 1 — Identity | Agent proves who it is | Moltbook, SIWE, ENS |
| Layer 2 — Wallet | Agent holds and spends money | Coinbase Agentic Wallets, Safe |
| Layer 3 — Trading | Agent finds and executes bets | pmxt, Polymarket CLOB, Kalshi API |
| Layer 4 — Intelligence | Agent analyzes and decides | Claude, Polyseer, CrewAI |
For the complete architecture, see The Agent Betting Stack Explained.
What’s Next
- Prediction Market API Reference — Side-by-side endpoint documentation for all platforms, including pmxt
- Dome vs pmxt vs OddsPapi Comparison — Full comparison with benchmarks and decision framework
- Build a Polymarket Trading Bot — End-to-end bot development guide
- Cross-Market Arbitrage Guide — Production arbitrage strategies across prediction markets and sportsbooks
- py_clob_client Reference — If you need direct Polymarket API access without the unified layer
- Agent Betting Stack — The complete four-layer agent architecture
- Tool Directory — Every tool in the prediction market agent ecosystem
