# Limitless Exchange API Documentation ## Introduction The Limitless Exchange API provides programmatic access to a decentralized prediction market platform built on the Base blockchain (Chain ID: 8453). The platform enables users to trade on binary outcome markets using USDC as collateral, supporting both Central Limit Order Book (CLOB) and Automated Market Maker (AMM) trading mechanisms. The API offers comprehensive functionality for authentication, market browsing, order management, portfolio tracking, and real-time market data via WebSocket connections. The API implements a venue-based architecture for CLOB markets, where each market is associated with specific smart contract addresses that must be fetched dynamically before trading. All trading operations require EIP-712 structured data signatures and checksummed (EIP-55) Ethereum addresses. The platform supports multiple market types including simple binary markets, grouped NegRisk markets with shared collateral, and AMM-based liquidity pools, making it suitable for both retail traders and algorithmic trading systems. ## Authentication ### Get Signing Message Retrieve a nonce-based signing message required for wallet authentication. ```bash curl https://api.limitless.exchange/auth/signing-message ``` **Response:** ``` Welcome to Limitless.exchange! Please sign this message to verify your identity. Nonce: 0xa1b2c3d4e5f67890... ``` **Python Example:** ```python import requests from eth_account import Account from eth_account.messages import encode_defunct API_URL = "https://api.limitless.exchange" def authenticate(private_key): # Step 1: Get signing message response = requests.get(f"{API_URL}/auth/signing-message") signing_message = response.text # Step 2: Sign with wallet account = Account.from_key(private_key) address = account.address # Checksummed by default message = encode_defunct(text=signing_message) signature = account.sign_message(message) # Step 3: Login and get session cookie headers = { 'x-account': address, 'x-signing-message': '0x' + signing_message.encode('utf-8').hex(), 'x-signature': '0x' + signature.signature.hex(), 'Content-Type': 'application/json' } response = requests.post( f"{API_URL}/auth/login", headers=headers, json={"client": "eoa"} ) session_cookie = response.cookies.get('limitless_session') user_data = response.json() return session_cookie, user_data # Usage session, user = authenticate("0xYOUR_PRIVATE_KEY") print(f"Authenticated as: {user['account']}") print(f"Session: {session}") ``` ### Verify Authentication Check if the current session is authenticated. ```bash curl https://api.limitless.exchange/auth/verify-auth \ --cookie "limitless_session=YOUR_SESSION_COOKIE" ``` **Response (200 OK):** ```json "0x1234567890123456789012345678901234567890" ``` **Response (401 Unauthorized):** ```json { "message": "The token cookie is required" } ``` ## Market Discovery ### Browse Active Markets List all active prediction markets with pagination and sorting. ```python import requests API_URL = "https://api.limitless.exchange" def browse_markets(page=1, limit=10, sort_by="volume", category_id=None): """Browse active markets with filtering and pagination.""" params = { "page": page, "limit": limit, "sortBy": sort_by # Options: volume, liquidity, newest } url = f"{API_URL}/markets/active" if category_id: url = f"{API_URL}/markets/active/{category_id}" response = requests.get(url, params=params) response.raise_for_status() data = response.json() print(f"Total markets: {data['totalMarketsCount']}") for market in data['data']: print(f"\nTitle: {market['title']}") print(f"Slug: {market['slug']}") print(f"Type: {market['tradeType']} - {market['marketType']}") print(f"Prices: YES {market['prices'][0]}%, NO {market['prices'][1]}%") print(f"Volume: ${market['volumeFormatted']}") print(f"Liquidity: ${market['liquidityFormatted']}") print(f"Deadline: {market['expirationDate']}") return data # Usage markets = browse_markets(page=1, limit=5, sort_by="volume") ``` **Response Structure:** ```json { "data": [ { "id": 7495, "address": "0x76d3e2098Be66Aa7E15138F467390f0Eb7349B9b", "title": "$DOGE above $0.21652 on Sep 1, 12:00 UTC?", "slug": "dollardoge-above-dollar021652-on-sep-1-1200-utc", "prices": [42.8, 57.2], "volume": "164109293", "volumeFormatted": "164.109293", "liquidity": "50000000", "liquidityFormatted": "50.000000", "tradeType": "amm", "marketType": "single", "expirationTimestamp": 1756728000000, "positionIds": ["19633204485790857949828516737993423758628930235371629943999544859324645414627", "..."] } ], "totalMarketsCount": 150 } ``` ### Get Market Details Fetch comprehensive market information including venue addresses (critical for trading). ```python def get_market_with_venue(slug): """ Fetch market details including venue contract addresses. IMPORTANT: Cache this data per market as venue is static. """ response = requests.get(f"{API_URL}/markets/{slug}") response.raise_for_status() market = response.json() print(f"Market: {market['title']}") print(f"Type: {market.get('tradeType', 'clob')}") # CRITICAL: Venue addresses for CLOB markets if 'venue' in market: venue = market['venue'] print(f"\nVenue Exchange: {venue['exchange']}") print(f"Venue Adapter: {venue.get('adapter', 'N/A')}") print("\nIMPORTANT: Use venue.exchange as verifyingContract in EIP-712 signing") # Position IDs (YES = [0], NO = [1]) if 'positionIds' in market: print(f"\nYES Token ID: {market['positionIds'][0]}") print(f"NO Token ID: {market['positionIds'][1]}") return market # Usage with caching MARKET_CACHE = {} def get_cached_market(slug): if slug not in MARKET_CACHE: MARKET_CACHE[slug] = get_market_with_venue(slug) return MARKET_CACHE[slug] market = get_cached_market("btc-100k-2024") ``` ### Search Markets Search markets using semantic similarity. ```python def search_markets(query, limit=10, similarity_threshold=0.5): """Search markets with semantic matching.""" params = { "query": query, "limit": limit, "similarityThreshold": similarity_threshold } response = requests.get(f"{API_URL}/markets/search", params=params) response.raise_for_status() results = response.json() for market in results['data']: print(f"Match: {market['title']}") print(f"Slug: {market['slug']}") return results # Usage results = search_markets("cryptocurrency bitcoin price", limit=5) ``` ## Trading Operations ### Get Orderbook Retrieve current bids and asks for a CLOB market. ```python def get_orderbook(slug): """Fetch orderbook with current orders.""" response = requests.get(f"{API_URL}/markets/{slug}/orderbook") response.raise_for_status() orderbook = response.json() print(f"Best Bid: {orderbook['bids'][0]['price'] if orderbook['bids'] else 'None'}") print(f"Best Ask: {orderbook['asks'][0]['price'] if orderbook['asks'] else 'None'}") print(f"Last Trade: {orderbook['lastTradePrice']}") print(f"Adjusted Midpoint: {orderbook['adjustedMidpoint']}") # Display order depth print("\nTop 5 Bids:") for bid in orderbook['bids'][:5]: print(f" Price: {bid['price']}, Size: {bid['size']}") print("\nTop 5 Asks:") for ask in orderbook['asks'][:5]: print(f" Price: {ask['price']}, Size: {ask['size']}") return orderbook # Usage orderbook = get_orderbook("btc-100k-2024") ``` ### Get Historical Prices Retrieve historical price data with configurable intervals. ```python def get_historical_prices(slug, interval="1d", from_date=None, to_date=None): """ Fetch historical price data. Intervals: 1h, 6h, 1d, 1w, 1m, all """ params = {"interval": interval} if from_date: params["from"] = from_date # ISO 8601 format if to_date: params["to"] = to_date response = requests.get( f"{API_URL}/markets/{slug}/historical-price", params=params ) response.raise_for_status() data = response.json() for series in data: print(f"\nToken: {series['title']}") print(f"Prices: {len(series['prices'])} data points") # Show first and last price if series['prices']: first = series['prices'][0] last = series['prices'][-1] print(f"First: {first['price']} at {first['timestamp']}") print(f"Last: {last['price']} at {last['timestamp']}") return data # Usage history = get_historical_prices( "btc-100k-2024", interval="1d", from_date="2024-01-01T00:00:00Z", to_date="2024-01-31T23:59:59Z" ) ``` ## Order Management ### Create Order with EIP-712 Signature Complete order creation flow with proper venue address usage. ```python from eth_account import Account from eth_account.messages import encode_typed_data import time import random CHAIN_ID = 8453 # Base mainnet def create_and_sign_order( private_key, market_slug, token_type, # "YES" or "NO" price_dollars, # e.g., 0.65 for 65 cents amount, # number of shares session_cookie, user_data ): """ Complete order creation with venue-based EIP-712 signing. CRITICAL: Fetches venue.exchange from market data for verifyingContract. """ # Step 1: Get market data (cached) market = get_cached_market(market_slug) # CRITICAL: Extract venue exchange for EIP-712 signing venue_exchange = market["venue"]["exchange"] print(f"Using venue exchange: {venue_exchange}") # Get token ID (positionIds[0] = YES, positionIds[1] = NO) token_id = market["positionIds"][0] if token_type == "YES" else market["positionIds"][1] # Step 2: Calculate amounts (USDC has 6 decimals) scaling_factor = 1_000_000 total_cost = price_dollars * amount maker_amount = int(total_cost * scaling_factor) taker_amount = int(amount * scaling_factor) account = Account.from_key(private_key) maker_address = account.address # Already checksummed # Step 3: Create order payload order = { "salt": int(time.time() * 1000) + random.randint(0, 1000), "maker": maker_address, "signer": maker_address, "taker": "0x0000000000000000000000000000000000000000", "tokenId": token_id, "makerAmount": str(maker_amount), "takerAmount": str(taker_amount), "expiration": "0", "nonce": "0", "feeRateBps": str(user_data.get("rank", {}).get("feeRateBps", 0)), "side": 0, # 0 = BUY, 1 = SELL "signatureType": 0 # 0 = EOA } # Step 4: EIP-712 signing with venue.exchange as verifyingContract domain = { "name": "Limitless CTF Exchange", "version": "1", "chainId": CHAIN_ID, "verifyingContract": venue_exchange # CRITICAL: From market's venue } types = { "Order": [ {"name": "salt", "type": "uint256"}, {"name": "maker", "type": "address"}, {"name": "signer", "type": "address"}, {"name": "taker", "type": "address"}, {"name": "tokenId", "type": "uint256"}, {"name": "makerAmount", "type": "uint256"}, {"name": "takerAmount", "type": "uint256"}, {"name": "expiration", "type": "uint256"}, {"name": "nonce", "type": "uint256"}, {"name": "feeRateBps", "type": "uint256"}, {"name": "side", "type": "uint8"}, {"name": "signatureType", "type": "uint8"} ] } # Convert string fields to int for signing message = {**order} message["tokenId"] = int(message["tokenId"]) message["makerAmount"] = int(message["makerAmount"]) message["takerAmount"] = int(message["takerAmount"]) message["expiration"] = int(message["expiration"]) message["nonce"] = int(message["nonce"]) message["feeRateBps"] = int(message["feeRateBps"]) signable = encode_typed_data(domain, types, message) signed = account.sign_message(signable) signature = '0x' + signed.signature.hex() # Step 5: Submit order order["signature"] = signature order["price"] = price_dollars payload = { "order": order, "ownerId": user_data["id"], "orderType": "GTC", # or "FOK" "marketSlug": market_slug } response = requests.post( f"{API_URL}/orders", json=payload, cookies={"limitless_session": session_cookie} ) response.raise_for_status() result = response.json() print(f"\nOrder Created!") print(f"Order ID: {result['order'].get('id', 'N/A')}") print(f"Status: {result['order'].get('status', 'PENDING')}") return result # Usage import os private_key = os.environ["PRIVATE_KEY"] session, user = authenticate(private_key) order = create_and_sign_order( private_key=private_key, market_slug="btc-100k-2024", token_type="YES", price_dollars=0.65, amount=100, session_cookie=session, user_data=user ) ``` ### Cancel Orders Cancel individual or multiple orders. ```python def cancel_order(session_cookie, order_id): """Cancel a specific order.""" response = requests.delete( f"{API_URL}/orders/{order_id}", cookies={"limitless_session": session_cookie} ) response.raise_for_status() result = response.json() print(f"Order {order_id} cancelled: {result['message']}") return result def cancel_batch(session_cookie, order_ids): """Cancel multiple orders in a batch.""" payload = {"orderIds": order_ids} response = requests.post( f"{API_URL}/orders/cancel-batch", json=payload, cookies={"limitless_session": session_cookie} ) response.raise_for_status() result = response.json() print(f"Cancelled: {len(result['canceled'])} orders") if result.get('failed'): print(f"Failed: {len(result['failed'])} orders") for failure in result['failed']: print(f" - {failure['orderId']}: {failure['message']}") return result def cancel_all_market_orders(session_cookie, market_slug): """Cancel all orders in a specific market.""" response = requests.delete( f"{API_URL}/orders/all/{market_slug}", cookies={"limitless_session": session_cookie} ) response.raise_for_status() result = response.json() print(f"Cancelled all orders in {market_slug}") return result # Usage cancel_order(session, "6f52b6d2-6c9e-4a5c-8a4f-28ab4b7ff203") cancel_batch(session, ["order-id-1", "order-id-2"]) cancel_all_market_orders(session, "btc-100k-2024") ``` ### Get User Orders Retrieve user's open or matched orders for a market. ```python def get_user_orders(session_cookie, market_slug, statuses=["LIVE"], limit=100): """ Get user orders for a market. Statuses: LIVE (open), MATCHED (filled) """ params = { "statuses": statuses, "limit": limit } response = requests.get( f"{API_URL}/markets/{market_slug}/user-orders", params=params, cookies={"limitless_session": session_cookie} ) response.raise_for_status() data = response.json() print(f"Found {len(data['orders'])} orders") for order in data['orders']: print(f"\nOrder ID: {order['id']}") print(f"Side: {order['side']}") print(f"Price: {order['price']}") print(f"Quantity: {order['quantity']}") print(f"Status: {order['status']}") return data # Usage orders = get_user_orders(session, "btc-100k-2024", statuses=["LIVE"]) ``` ## Portfolio Management ### Get Positions Retrieve all portfolio positions with P&L calculations. ```python def get_positions(session_cookie): """Get all portfolio positions with rewards.""" response = requests.get( f"{API_URL}/portfolio/positions", cookies={"limitless_session": session_cookie} ) response.raise_for_status() data = response.json() # Rewards summary rewards = data['rewards'] print(f"Today's Rewards: {rewards['todaysRewards']}") print(f"Total Unpaid: {rewards['totalUnpaidRewards']}") # CLOB positions print(f"\nCLOB Positions: {len(data['clob'])}") for pos in data['clob']: market = pos['market'] positions = pos['positions'] print(f"\n Market: {market['title']}") print(f" YES - Cost: {positions['yes']['cost']}, PnL: {positions['yes']['unrealizedPnl']}") print(f" NO - Cost: {positions['no']['cost']}, PnL: {positions['no']['unrealizedPnl']}") # AMM positions print(f"\nAMM Positions: {len(data['amm'])}") for pos in data['amm']: print(f" Market: {pos['market']['title']}") print(f" Tokens: {pos['outcomeTokenAmount']}") print(f" Collateral: {pos['collateralAmount']}") return data # Usage positions = get_positions(session) ``` ### Get Trade History Retrieve paginated trade history with filtering. ```python def get_history(session_cookie, page=1, limit=20, from_date=None, to_date=None): """ Get portfolio history including trades, splits, merges, and conversions. Dates in ISO 8601 format: "2024-01-01T00:00:00.000Z" """ params = { "page": page, "limit": limit } if from_date: params["from"] = from_date if to_date: params["to"] = to_date response = requests.get( f"{API_URL}/portfolio/history", params=params, cookies={"limitless_session": session_cookie} ) response.raise_for_status() data = response.json() print(f"Total entries: {data['totalCount']}") for entry in data['data']: market = entry['market'] print(f"\n{entry['strategy']}: {market['title']}") print(f"Outcome: Index {entry['outcomeIndex']}") print(f"Amount: {entry['outcomeTokenAmount']}") print(f"Price: {entry['outcomeTokenPrice']}") print(f"Tx: {entry.get('transactionHash', 'N/A')}") return data # Usage history = get_history( session, page=1, limit=50, from_date="2024-01-01T00:00:00.000Z", to_date="2024-12-31T23:59:59.999Z" ) ``` ### Check Trading Allowance Verify USDC approval for trading. ```python def check_allowance(session_cookie, trading_type="clob", spender=None): """ Check USDC allowance for trading. Types: clob, negrisk Spender: Optional override (e.g., specific venue exchange address) """ params = {"type": trading_type} if spender: params["spender"] = spender response = requests.get( f"{API_URL}/portfolio/trading/allowance", params=params, cookies={"limitless_session": session_cookie} ) response.raise_for_status() data = response.json() print(f"Allowance: {data['allowance']}") print(f"Has Minimum: {data['hasMinimumAllowance']}") print(f"Checked Address: {data['checkedAddress']}") if not data['hasMinimumAllowance']: print("\nWARNING: Insufficient allowance!") print("You need to approve USDC spending on-chain.") return data # Usage allowance = check_allowance(session, trading_type="clob") ``` ## WebSocket Integration ### Real-time Market Data Connect to WebSocket for live price updates and position changes. ```python import socketio import asyncio import os class LimitlessWebSocket: """Real-time market data and position updates.""" def __init__(self, session_cookie=None): self.session_cookie = session_cookie self.sio = socketio.AsyncClient() self.setup_handlers() def setup_handlers(self): @self.sio.event(namespace='/markets') async def connect(): print("✓ Connected to /markets") if self.session_cookie: await self.sio.emit( 'authenticate', f'Bearer {self.session_cookie}', namespace='/markets' ) @self.sio.event(namespace='/markets') async def newPriceData(data): print(f"Price Update: {data['marketAddress']}") prices = data['updatedPrices'] print(f" YES: {prices['yes']}, NO: {prices['no']}") @self.sio.event(namespace='/markets') async def orderbookUpdate(data): print(f"Orderbook Update: {data['marketSlug']}") # Handle orderbook changes @self.sio.event(namespace='/markets') async def positions(data): print(f"Position Update: {data['account']}") print(f" Positions: {len(data['positions'])}") @self.sio.event(namespace='/markets') async def disconnect(): print("✗ Disconnected from /markets") async def connect(self): options = {'transports': ['websocket']} if self.session_cookie: options['headers'] = { 'Cookie': f'limitless_session={self.session_cookie}' } await self.sio.connect( 'wss://ws.limitless.exchange', namespaces=['/markets'], **options ) async def subscribe_markets(self, market_addresses=None, market_slugs=None): """ Subscribe to market updates. market_addresses: List of AMM market addresses market_slugs: List of CLOB market slugs """ payload = {} if market_addresses: payload['marketAddresses'] = market_addresses if market_slugs: payload['marketSlugs'] = market_slugs await self.sio.emit('subscribe_market_prices', payload, namespace='/markets') print(f"✓ Subscribed to markets") if self.session_cookie: await self.sio.emit('subscribe_positions', payload, namespace='/markets') print(f"✓ Subscribed to positions") async def wait(self): await self.sio.wait() # Usage async def main(): private_key = os.environ["PRIVATE_KEY"] session, user = authenticate(private_key) ws = LimitlessWebSocket(session_cookie=session) await ws.connect() # Subscribe to both AMM and CLOB markets await ws.subscribe_markets( market_addresses=["0x76d3e2098Be66Aa7E15138F467390f0Eb7349B9b"], market_slugs=["btc-100k-2024"] ) print("Listening for updates... Press Ctrl+C to stop") await ws.wait() # Run asyncio.run(main()) ``` ## Summary The Limitless Exchange API provides a robust, production-ready interface for interacting with decentralized prediction markets on the Base blockchain. The primary use cases include algorithmic trading bots that monitor orderbooks and execute trades based on market conditions, portfolio management applications that track positions and calculate P&L across multiple markets, and market-making systems that provide liquidity through continuous bid-ask quotes. The venue-based architecture ensures proper contract address usage for each market, while EIP-712 signatures provide cryptographic security for all trading operations. Integration patterns typically involve caching market data (especially venue addresses) to minimize API calls, using session-based authentication with proper private key management, implementing WebSocket connections for real-time price monitoring and position updates, and handling token approvals on-chain before executing trades. The API supports both simple binary outcome markets and complex NegRisk grouped markets, with comprehensive error handling and response validation. All monetary values use 6-decimal precision for USDC, and addresses must be properly checksummed according to EIP-55 standards. For production deployments, developers should implement retry logic, rate limiting awareness, and secure secret management for private keys and session cookies.