Skip to main content
If you’ve built bots or agents on Polymarket, this guide maps every concept, endpoint, and code pattern to the Limitless equivalents so you can port your code in minutes.
This guide reflects Polymarket’s CLOB V2. The V1 clients (py-clob-client, @polymarket/clob-client, the Rust rs-clob-client, and the Polymarket/agents framework) were archived in May 2026 and no longer work against production. If you’re still on V1 code you have to move to V2 regardless, so the mappings below target V2.

What changed

Polymarket (CLOB V2)Limitless
ChainPolygon (137)Base (8453)
CollateralpUSD (6 decimals, wraps USDC.e)USDC (6 decimals)
REST hostsgamma-api.polymarket.com (markets, search) + clob.polymarket.com (orderbook, pricing, trading) + data-api.polymarket.com (positions, trades, activity)api.limitless.exchange (unified)
WebSocketwss://ws-subscriptions-clob.polymarket.comwss://ws.limitless.exchange
Auth modelL1/L2 — derive API creds with EIP-712, sign every request with HMAC across 5 POLY_* headersHMAC-signed scoped API tokens (lmts-api-key/lmts-timestamp/lmts-signature) for all integrations; granular scopes for partner integrations
SDKsV2 CLOB clients @polymarket/clob-client-v2, py_clob_client_v2 (V1 clients archived)Python SDK, TypeScript SDK, Go SDK, Rust SDK, or direct REST
Wallet typesEOA, Proxy, Gnosis Safe, 1271 (signatureType 0-3)EOA, or server-managed wallets via delegated signing
Market lookupconditionId or clobTokenIdsslug or address
Token IDsclobTokenIds paired with outcomes by indextokens.yes = Yes, tokens.no = No
Order signingEIP-712 verifyingContract = CTF Exchange V2, domain version "2"EIP-712 verifyingContract = venue.exchange, domain version "1"
Order structV2 dropped taker, nonce, feeRateBps, expiration from the signed order and added timestamp, metadata, builderClassic CTF fields: taker, expiration, nonce, feeRateBps (no timestamp/metadata/builder)
Order freshnessV2 order carries a mandatory creation timestamp (ms); GTD orders have a ~1 minute expiration thresholdOptional timestamp + recvWindow on POST /orders; stale orders rejected with HTTP 425. See Receive Window.
Neg-riskSeparate Neg Risk CTF Exchange V2 contractHandled via venue system — extra approval to venue.adapter for SELL

Authentication

Polymarket requires deriving API credentials (apiKey, secret, passphrase) through an L1 EIP-712 handshake, then signing every request with HMAC-SHA256 across 5 POLY_* headers. Limitless uses a scoped API token (a token ID + secret) generated in the UI. You sign each request with HMAC-SHA256 and send three headers: lmts-api-key, lmts-timestamp, and lmts-signature.
from py_clob_client_v2 import ClobClient

client = ClobClient(host="https://clob.polymarket.com", chain_id=137, key=private_key)
creds = client.create_or_derive_api_key()

client = ClobClient(
    host="https://clob.polymarket.com",
    chain_id=137,
    key=private_key,
    creds=creds,
)
No POLY_* headers, but you do sign each request: derive the three lmts-* headers with sign_request(TOKEN_ID, SECRET, method, path, body) for every authenticated call. The timestamp must be within 30 seconds of server time. See the full Authentication guide for details, and the Programmatic API guide for partner scopes (sub-account management and delegated order signing).

Endpoint mapping

Market data

ActionPolymarket (CLOB V2)Limitless
List active marketsGET gamma-api.polymarket.com/markets?closed=falseGET /markets/active
Get market by slugGET gamma-api.polymarket.com/markets/slug/:slugGET /markets/:slug
Search marketsGET gamma-api.polymarket.com/public-search?q=...GET /markets/search?query=...
OrderbookGET clob.polymarket.com/book?token_id=...GET /markets/:slug/orderbook
Price historyGET clob.polymarket.com/prices-history?market=...GET /markets/:slug/historical-price
Midpoint priceGET clob.polymarket.com/midpoint?token_id=...Compute from orderbook response

Trading

ActionPolymarket (CLOB V2)Limitless
Place orderPOST clob.polymarket.com/orderPOST /orders
Cancel orderDELETE clob.polymarket.com/order (orderID in body)DELETE /orders/:orderId
Cancel multipleDELETE clob.polymarket.com/orders (array of ids in body)POST /orders/cancel-batch
Cancel allDELETE clob.polymarket.com/cancel-allDELETE /orders/all/:slug
Get open ordersGET clob.polymarket.com/data/ordersGET /markets/:slug/user-orders
Get order by IDGET clob.polymarket.com/data/order/:idPOST /orders/status/batch

Portfolio

ActionPolymarket (CLOB V2)Limitless
Get positionsGET data-api.polymarket.com/positions?user=0x...GET /portfolio/positions
Get tradesGET clob.polymarket.com/data/trades?maker_address=0x...GET /portfolio/trades
PnL chartN/AGET /portfolio/pnl-chart
Trade historyGET data-api.polymarket.com/activity?user=0x...GET /portfolio/history

Fetch a market

# Gamma for metadata, CLOB for trading, data-api for positions/activity
market = requests.get(
    "https://gamma-api.polymarket.com/markets/slug/will-trump-win"
).json()

# clobTokenIds and outcomes are parallel arrays — pair by index, don't assume [0] = Yes
token_ids = market["clobTokenIds"]
outcomes = market["outcomes"]
yes_idx = outcomes.index("Yes")
token_id = token_ids[yes_idx]

condition_id = market["conditionId"]
neg_risk = market["negRisk"]
tick_size = market["orderPriceMinTickSize"]

Build and sign an order

Both sides use EIP-712 typed data, but the order field sets diverge in V2. Polymarket V2 dropped taker, nonce, feeRateBps, and expiration from the signed struct and added timestamp, metadata, and builder. Limitless keeps the classic CTF-style fields (taker, expiration, nonce, feeRateBps) and has no timestamp/metadata/builder. Key differences:
FieldPolymarket (CLOB V2)Limitless
chainId137 (Polygon)8453 (Base)
verifyingContractCTF Exchange V2 0xE111180000d2663C0091e4f400237545B87B996B (Neg Risk: 0xe2222d279d744050d28e00520010520000310F59)venue.exchange from market data
name (EIP-712 domain)"Polymarket CTF Exchange" (same for Neg Risk)"Limitless CTF Exchange"
version (EIP-712 domain)"2""1"
tokenId sourceclobTokenIds[i] paired with outcomes[i]tokens.yes / tokens.no
Fields dropped vs your V1 codetaker, nonce, feeRateBps, expirationn/a
Fields addedtimestamp (ms), metadata, buildern/a
feeRateBpsNot in the signed order (removed in V2)Must match your profile’s rank.feeRateBps on fee-bearing markets
On Limitless, for markets that charge a fee the signed feeRateBps must equal your profile’s rank.feeRateBps (from GET /profiles/me) or the order is rejected. Hardcoding 0 works only on zero-fee markets — read the rate from your profile and sign with it. Polymarket V2 has no feeRateBps field at all, so this is a Limitless-specific step.
from py_clob_client_v2 import OrderArgs, OrderType, PartialCreateOrderOptions, Side

resp = client.create_and_post_order(
    order_args=OrderArgs(token_id=token_id, price=0.50, size=10, side=Side.BUY),
    options=PartialCreateOrderOptions(tick_size="0.01"),
    order_type=OrderType.GTC,
)
ownerId is your Limitless profile ID — a required field on every POST /orders request. Fetch it once from GET /profiles/me (the id field) and cache it. Polymarket’s SDK handles this internally; on Limitless you pass it explicitly.
If you use the Python SDK, TypeScript SDK, Go SDK, or Rust SDK, order building and signing are handled for you — similar to how Polymarket’s V2 CLOB clients (@polymarket/clob-client-v2, py_clob_client_v2) work.

Porting a Polymarket agent

If you’re adapting a Polymarket trading bot (a V2 CLOB client integration, or a port of the now-archived Polymarket/agents framework), here’s a checklist:
1

Swap credentials

Replace POLYGON_WALLET_PRIVATE_KEY with your existing private key on Base. Add your scoped API token (TOKEN_ID + SECRET) and sign each request with HMAC (lmts-api-key/lmts-timestamp/lmts-signature) instead of Polymarket’s POLY_* headers. You still drop Polymarket’s L1/L2 credential derivation — Limitless tokens are issued in the UI.
2

Collapse the three hosts into one

Replace gamma-api.polymarket.com, clob.polymarket.com, and data-api.polymarket.com all with api.limitless.exchange. Positions and history that you read from data-api move to /portfolio/*.
3

Change chain ID

Update chainId from 137 to 8453 in all EIP-712 domain definitions.
4

Update EIP-712 domain

Change the domain name from "Polymarket CTF Exchange" to "Limitless CTF Exchange", and the version from "2" to "1".
5

Swap token ID fields

Replace clobTokenIds lookups with the market’s tokens object. On Limitless the convention is fixed: tokens.yes = Yes, tokens.no = No (unlike Polymarket, where you pair clobTokenIds against outcomes).
6

Use venue addresses

Fetch venue.exchange from GET /markets/:slug and use it as verifyingContract. On Polymarket V2 you targeted the fixed CTF Exchange V2 contract — on Limitless each market can have its own venue.
7

Rework the order fields

Remove the V2 fields timestamp, metadata, and builder — Limitless doesn’t use them. Add back the classic CTF fields Limitless requires: taker, expiration, nonce, and feeRateBps. Also drop SDK-side params like tick_size and signature_type from your order construction.
8

Add ownerId to order submissions

POST /orders requires an ownerId field (your Limitless profile ID, from GET /profiles/me). Polymarket’s SDK handles this internally; on Limitless you pass it explicitly in every order payload alongside order, orderType, and marketSlug.
9

Update approvals

Approve USDC on Base (not pUSD on Polygon) to venue.exchange. For NegRisk SELL orders, also approve Conditional Tokens to venue.adapter. See venue system.

WebSocket

Polymarket (CLOB V2)Limitless
URLwss://ws-subscriptions-clob.polymarket.com/ws/market and /ws/userwss://ws.limitless.exchange
NamespaceChannel-based (market, user)Socket.IO namespace /markets
Authauth object (apiKey, secret, passphrase) in the user-channel subscribe messageHMAC auth headers (lmts-api-key/lmts-timestamp/lmts-signature) during handshake
See WebSocket events for the full event reference.

Quick reference for agents

If you’re building an LLM-powered agent or bot, here’s the minimal flow:
import os, json, time, hmac, hashlib, base64, requests
from datetime import datetime, timezone
from eth_account import Account
from eth_account.messages import encode_typed_data
from web3 import Web3

API = "https://api.limitless.exchange"
TOKEN_ID = os.environ["LMTS_TOKEN_ID"]      # scoped API token id
SECRET   = os.environ["LMTS_TOKEN_SECRET"]  # base64-encoded secret
PK  = os.environ["PRIVATE_KEY"]      # 0x...
OWNER_ID = int(os.environ["OWNER_ID"])  # Profile ID from GET /profiles/me

def sign_request(method: str, path: str, body: str = "") -> dict:
    timestamp = datetime.now(timezone.utc).isoformat()
    message = f"{timestamp}\n{method}\n{path}\n{body}"
    signature = base64.b64encode(
        hmac.new(base64.b64decode(SECRET), message.encode("utf-8"), hashlib.sha256).digest()
    ).decode("utf-8")
    return {"lmts-api-key": TOKEN_ID, "lmts-timestamp": timestamp, "lmts-signature": signature}

CHAIN_ID = 8453
ZERO_ADDR = "0x0000000000000000000000000000000000000000"
ORDER_TYPES = {
    "EIP712Domain": [
        {"name": "name", "type": "string"},
        {"name": "version", "type": "string"},
        {"name": "chainId", "type": "uint256"},
        {"name": "verifyingContract", "type": "address"},
    ],
    "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"},
    ],
}

# 1. Find a market
markets = requests.get(
    f"{API}/markets/active",
    headers=sign_request("GET", "/markets/active"),
).json()
slug = markets[0]["slug"]

# 2. Get venue + token IDs (cache per market — these are static)
market_path = f"/markets/{slug}"
market = requests.get(
    f"{API}{market_path}",
    headers=sign_request("GET", market_path),
).json()
venue_exchange = market["venue"]["exchange"]
yes_token = market["tokens"]["yes"]

# 3. Build order (BUY 10 YES shares at $0.50)
acct = Account.from_key(PK)
order = {
    "salt": int(time.time() * 1000),
    "maker": Web3.to_checksum_address(acct.address),
    "signer": Web3.to_checksum_address(acct.address),
    "taker": ZERO_ADDR,
    "tokenId": yes_token,
    "makerAmount": 5_000_000,   # 0.50 * 10 * 1e6
    "takerAmount": 10_000_000,  # 10 * 1e6
    "expiration": 0,
    "nonce": 0,
    "feeRateBps": 0,   # must equal rank.feeRateBps on fee-bearing markets (see above)
    "side": 0,
    "signatureType": 0,
}

# 4. Sign (EIP-712) — same encode_typed_data mechanism as Polymarket, different fields
encoded = encode_typed_data({
    "types": ORDER_TYPES,
    "primaryType": "Order",
    "domain": {
        "name": "Limitless CTF Exchange",
        "version": "1",
        "chainId": CHAIN_ID,
        "verifyingContract": Web3.to_checksum_address(venue_exchange),
    },
    "message": order,
})
sig = Account.sign_message(encoded, private_key=PK).signature.hex()

# 5. Submit (HMAC-sign the request over method + path + body)
order_body = json.dumps({
    "order": {**order, "signature": sig},
    "ownerId": OWNER_ID,
    "orderType": "GTC",
    "marketSlug": slug,
})
order_headers = sign_request("POST", "/orders", order_body)
order_headers["Content-Type"] = "application/json"
resp = requests.post(f"{API}/orders", headers=order_headers, data=order_body)
print(resp.json())

Partner and platform migrations

If you’re migrating a platform that manages multiple user accounts on Polymarket (e.g., a trading desk, social trading app, or embedded prediction market), the Limitless Programmatic API provides a first-class partner flow:
Polymarket patternLimitless equivalent
Manage many wallets and private keysCreate sub-accounts with server-managed wallets — no private key management
Sign orders per-user with their keysDelegated signing — server signs via Privy, submit unsigned orders
Derive HMAC creds per integrationDerive scoped API tokens with granular permissions
Recommended setup for partners:
  • Store HMAC credentials on your backend — never expose them to frontends
  • Use the SDK server-side to sign partner-authenticated requests
  • Expose only your own app-specific endpoints to the frontend
  • Keep public market reads (orderbooks, prices) directly in the browser
Both GTC (limit) and FOK (market) order types are supported for delegated orders. See the Delegated Orders SDK pages for full examples.

Next steps

Python quick start

Full end-to-end walkthrough with error handling.

Venue system

How venue contracts and token approvals work.

Programmatic API

Partner integrations, sub-accounts, and delegated signing.

API reference

Complete endpoint documentation.

EIP-712 signing

Full order type definition and field reference.