Skip to main content

Prerequisites

Before placing orders, initialize the required clients:
import { ethers } from 'ethers';
import {
  HttpClient,
  MarketFetcher,
  OrderClient,
} from '@limitless-exchange/sdk';

const httpClient = new HttpClient({
  baseURL: 'https://api.limitless.exchange',
  apiKey: process.env.LIMITLESS_API_KEY,
});

const wallet = new ethers.Wallet(process.env.PRIVATE_KEY!);
const marketFetcher = new MarketFetcher(httpClient);

const orderClient = new OrderClient({
  httpClient,
  wallet,
  marketFetcher, // Optional: enables venue caching for faster order creation
});

OrderClient Constructor

OptionTypeRequiredDescription
httpClientHttpClientYesAuthenticated HTTP client
walletethers.WalletYesWallet for EIP-712 signing
marketFetcherMarketFetcherNoEnables automatic venue caching. If omitted, venue data is fetched on each order.
The OrderClient automatically fetches your user profile data (including ownerId) from the API on the first order. You do not need to supply it manually.

Token Approvals

Before trading, your wallet must approve the relevant venue contracts to spend USDC and conditional tokens. This is a one-time setup per venue.

Approval Requirements

Market TypeUSDC ApprovalConditional Token Approval
CLOBVenue exchange contractVenue exchange contract
NegRiskNegRisk exchange contractNegRisk exchange contract

Manual Approval with ethers

import { ethers } from 'ethers';

const USDC_ADDRESS = '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913'; // USDC on Base
const ERC20_ABI = [
  'function approve(address spender, uint256 amount) returns (bool)',
];
const ERC1155_ABI = [
  'function setApprovalForAll(address operator, bool approved)',
];

const provider = new ethers.JsonRpcProvider('https://mainnet.base.org');
const signer = new ethers.Wallet(process.env.PRIVATE_KEY!, provider);

async function approveVenue(venueExchange: string) {
  const usdc = new ethers.Contract(USDC_ADDRESS, ERC20_ABI, signer);
  const tx1 = await usdc.approve(venueExchange, ethers.MaxUint256);
  await tx1.wait();
  console.log('USDC approved for', venueExchange);
}
Run approvals once per venue. After approval, all subsequent orders on markets using that venue work without additional on-chain transactions.

GTC Orders (Good-Til-Cancelled)

GTC orders remain on the orderbook until filled, cancelled, or the market resolves. Specify price (probability between 0 and 1) and size (whole number of shares).

Buy YES shares

const market = await marketFetcher.getMarket('btc-100k-weekly');

const result = await orderClient.createOrder({
  marketSlug: market.slug,
  tokenId: market.positionIds[0], // YES token
  side: 'BUY',
  price: 0.65,
  size: 100,
  orderType: 'GTC',
});

console.log('Order created:', result.id);

Sell NO shares

const result = await orderClient.createOrder({
  marketSlug: market.slug,
  tokenId: market.positionIds[1], // NO token
  side: 'SELL',
  price: 0.40,
  size: 50,
  orderType: 'GTC',
});

GTC Parameter Reference

ParameterTypeDescription
marketSlugstringMarket slug identifier
tokenIdstringYES or NO token ID from market.positionIds
side'BUY' | 'SELL'Order side
pricenumberPrice between 0 and 1 (exclusive)
sizenumberNumber of shares (whole number)
orderType'GTC'Order type

FOK Orders (Fill-Or-Kill)

FOK orders execute immediately against the existing orderbook or are rejected entirely. Instead of price and size, specify makerAmount which represents the total value to trade.

Buy with FOK

For a BUY FOK order, makerAmount is the amount of USDC you are willing to spend:
const result = await orderClient.createOrder({
  marketSlug: market.slug,
  tokenId: market.positionIds[0], // YES token
  side: 'BUY',
  makerAmount: 50, // Spend up to 50 USDC
  orderType: 'FOK',
});

Sell with FOK

For a SELL FOK order, makerAmount is the number of shares you want to sell:
const result = await orderClient.createOrder({
  marketSlug: market.slug,
  tokenId: market.positionIds[0], // YES token
  side: 'SELL',
  makerAmount: 100, // Sell 100 shares
  orderType: 'FOK',
});

FOK Parameter Reference

ParameterTypeDescription
marketSlugstringMarket slug identifier
tokenIdstringYES or NO token ID from market.positionIds
side'BUY' | 'SELL'Order side
makerAmountnumberBUY: USDC to spend. SELL: shares to sell.
orderType'FOK'Order type

NegRisk Orders

When trading NegRisk markets, always use the submarket slug — not the group slug. Fetch the group to discover submarkets, then fetch the specific submarket to get its token IDs.
// 1. Fetch the NegRisk group
const group = await marketFetcher.getMarket('us-election-2024');

// 2. Pick a submarket
const submarket = group.submarkets[0];

// 3. Fetch submarket details to get token IDs
const submarketDetail = await marketFetcher.getMarket(submarket.slug);

// 4. Place an order using the submarket slug and token IDs
const result = await orderClient.createOrder({
  marketSlug: submarketDetail.slug,
  tokenId: submarketDetail.positionIds[0], // YES token
  side: 'BUY',
  price: 0.55,
  size: 25,
  orderType: 'GTC',
});
Passing the group slug to createOrder() will fail. The group slug does not resolve to a tradeable market. Always use the submarket slug.

Cancel Orders

Cancel an open order by its ID and market slug:
await orderClient.cancelOrder({
  orderId: 'order_abc123',
  marketSlug: 'btc-100k-weekly',
});

Order States

StateDescription
OPENOrder is live on the orderbook
PARTIALLY_FILLEDSome shares have been matched
FILLEDOrder fully matched
CANCELLEDOrder cancelled by user or system
REJECTEDOrder rejected (insufficient funds, invalid params, etc.)

Error Handling

The SDK throws ApiError for HTTP-level failures. Inspect status and message for details.
import { ApiError } from '@limitless-exchange/sdk';

try {
  await orderClient.createOrder({
    marketSlug: 'btc-100k-weekly',
    tokenId: market.positionIds[0],
    side: 'BUY',
    price: 0.65,
    size: 100,
    orderType: 'GTC',
  });
} catch (error) {
  if (error instanceof ApiError) {
    switch (error.status) {
      case 400:
        console.error('Bad request:', error.message);
        break;
      case 401:
        console.error('Unauthorized: check your API key');
        break;
      case 429:
        console.error('Rate limited: slow down requests');
        break;
      default:
        console.error(`API error ${error.status}:`, error.message);
    }
  } else {
    throw error;
  }
}
See the Error Handling and Retry guide for retry strategies.

Validation Rules

Prices must be between 0 and 1 (exclusive). A price of 0.65 means you value the outcome at 65%. Prices outside this range will be rejected by the API.
// Valid
price: 0.01
price: 0.99

// Invalid (will throw)
price: 0
price: 1
price: 1.5
price: -0.1
Size must be a positive whole number representing the number of shares. Fractional shares are not supported.
// Valid
size: 1
size: 100
size: 10000

// Invalid (will throw)
size: 0
size: 10.5
size: -1