
Security News
RubyGems Adds Cooldown Feature to Bundler for Newly Published Gems
RubyGems and Bundler 4.0.13 introduced an opt-in cooldown feature that delays newly published gems during dependency resolution.
@limitless-exchange/sdk
Advanced tools
v1.0.11 | Production-Ready | Type-Safe | Fully Documented
A TypeScript SDK for interacting with the Limitless Exchange platform, providing type-safe access to CLOB and NegRisk prediction markets.
v1.0.11 Release: Adds optional receive-window controls for normal and delegated order creation. See Changelog for details.
USE AT YOUR OWN RISK
This SDK is provided "as-is" without any warranties or guarantees. Trading on prediction markets involves financial risk. By using this SDK, you acknowledge that:
ALWAYS TEST BEFORE USING IN PRODUCTION WITH REAL FUNDS
For production use, we strongly recommend:
Feedback Welcome: We encourage you to report any bugs, suggest improvements, or contribute to the project. Please submit issues or pull requests on our GitHub repository.
Important: Limitless restricts order placement from US locations due to regulatory requirements and compliance with international sanctions. Before placing orders, builders should verify their location complies with applicable regulations.
createOrder() handles makerAmount, takerAmount, price, and salt returned as JSON stringsnpm install @limitless-exchange/sdk
# or
yarn add @limitless-exchange/sdk
# or
pnpm add @limitless-exchange/sdk
import { HttpClient, MarketFetcher } from '@limitless-exchange/sdk';
// Create HTTP client (no authentication needed)
const httpClient = new HttpClient({
baseURL: 'https://api.limitless.exchange',
// Optional: Add custom headers to all requests
additionalHeaders: {
'X-Custom-Header': 'my-value',
},
});
const marketFetcher = new MarketFetcher(httpClient);
// Get markets sorted by LP rewards
const markets = await marketFetcher.getActiveMarkets({
limit: 8,
sortBy: 'lp_rewards', // 'lp_rewards' | 'ending_soon' | 'newest' | 'high_value'
});
console.log(`Found ${markets.data.length} of ${markets.totalMarketsCount} markets`);
// Pagination (page-based)
const page2 = await marketFetcher.getActiveMarkets({
limit: 8,
page: 2,
sortBy: 'ending_soon',
});
See examples/project-integration/src/active-markets.ts for more examples.
import { HttpClient, MarketPageFetcher } from '@limitless-exchange/sdk';
const httpClient = new HttpClient({
baseURL: 'https://api.limitless.exchange',
});
const pageFetcher = new MarketPageFetcher(httpClient);
// Resolve a page from URL path
const page = await pageFetcher.getMarketPageByPath('/crypto');
// Fetch page markets with dynamic filters
const markets = await pageFetcher.getMarkets(page.id, {
limit: 20,
sort: '-updatedAt',
filters: {
duration: 'hourly',
ticker: ['btc', 'eth'],
},
});
if ('pagination' in markets) {
console.log(`Total markets: ${markets.pagination.total}`);
}
Detailed guide: docs/market-pages/README.md
The SDK uses API keys for authentication. API keys can be obtained from your Limitless Exchange account settings(Click on User Profile).
import { HttpClient } from '@limitless-exchange/sdk';
// Option 1: Automatic from environment variable (recommended)
// Set LIMITLESS_API_KEY in your .env file
const httpClient = new HttpClient({
baseURL: 'https://api.limitless.exchange',
});
// Option 2: Explicit API key
const httpClient = new HttpClient({
baseURL: 'https://api.limitless.exchange',
apiKey: process.env.LIMITLESS_API_KEY,
});
// All requests automatically include X-API-Key header
// For authenticated endpoints like portfolio, orders, etc.
Environment Variables:
Create a local .env file that is not committed to source control:
# Required for authenticated endpoints
LIMITLESS_API_KEY=sk_live_your_api_key_here
# REQUIRED for server-side or local scripts that sign orders (EIP-712)
# Never commit a real private key to the repository.
PRIVATE_KEY=your_private_key_here
Do not ship a raw PRIVATE_KEY in browser bundles, checked-in .env files, or frontend environment variables. For browser apps, use an injected wallet or move signing behind your own backend/BFF.
Use client.portfolio.getProfile() without an address to fetch the authenticated caller's private profile via GET /profiles/me. Passing an address keeps the existing address-based lookup via GET /profiles/:account.
import { Client } from '@limitless-exchange/sdk';
const client = new Client({
baseURL: 'https://api.limitless.exchange',
apiKey: process.env.LIMITLESS_API_KEY,
});
const currentProfile = await client.portfolio.getProfile();
const profileByAddress = await client.portfolio.getProfile(
'0x1676716Ef7F19B5C5d690631CB57cf0bFD900A3d'
);
The SDK also supports partner-scoped HMAC credentials for api-token v3 workflows such as token self-service, partner-account creation, delegated trading, and server-wallet redeem/withdraw.
Use HMAC credentials only in a backend or BFF service. Do not expose partner HMAC secrets in browser bundles, frontend environment variables, or client-side storage.
Recommended setup:
tokenId / secret on your backend.PRIVATE_KEY on your backend or in local development only.See docs/code-samples/api-key-v3/ for the partner HMAC examples.
Use client.partnerAccounts.listAccounts() from a backend or BFF with HMAC-scoped API-token credentials that include account_creation. This endpoint lists or recovers partner-owned child accounts created under the authenticated partner profile.
listAccounts() calls GET /profiles/partner-accountsaccount filters by exact account addresslimit and page are positive integers; limit is capped to 25 before sendingx-on-behalf-ofconst accounts = await client.partnerAccounts.listAccounts({
account: '0x1676716Ef7F19B5C5d690631CB57cf0bFD900A3d',
limit: 100,
page: 1,
});
console.log(accounts.limit); // 25
console.log(accounts.data[0]?.profileId);
Use client.partnerAccounts.checkAllowances(profileId) and client.partnerAccounts.retryAllowances(profileId) only for partner child profiles created with createServerWallet: true.
checkAllowances() calls GET /profiles/partner-accounts/:profileId/allowancesretryAllowances() calls POST /profiles/partner-accounts/:profileId/allowances/retryaccount_creation and delegated_signing scopesprofileId should be the delegated child-profile idimport { Client } from '@limitless-exchange/sdk';
const client = new Client({
baseURL: 'https://api.limitless.exchange',
hmacCredentials: {
tokenId: process.env.LIMITLESS_API_TOKEN_ID!,
secret: process.env.LIMITLESS_API_TOKEN_SECRET!,
},
});
let allowances = await client.partnerAccounts.checkAllowances(352);
if (!allowances.ready) {
// Retry re-checks live chain state and submits only targets still missing.
// A returned "submitted" status means this request submitted a sponsored tx/user operation.
allowances = await client.partnerAccounts.retryAllowances(352);
}
Poll checkAllowances() first. If ready is false and one or more targets are missing or failed with retryable: true, call retryAllowances(), then poll checkAllowances() again after a short delay. Retry 429 responses throw RateLimitError and include retryAfterSeconds in error.data; retry 409 responses throw APIError with status === 409, which means another retry is already running.
For a complete runnable flow, see docs/code-samples/api-key-v3/partner-account-allowances.ts.
Use client.serverWallets only for server-managed wallets created in delegated-signing partner flows with createServerWallet: true.
redeemPositions() calls POST /portfolio/redeemwithdraw() calls POST /portfolio/withdrawwithdraw() also requires the withdrawal scopeonBehalfOf to the delegated child-profile id when withdrawing child server-wallet fundsonBehalfOf only when withdrawing the authenticated caller's own server wallet to an explicit destinationamount for withdraw must be provided in the token smallest unitdestination to use the API default: authenticated partner smart wallet when present, otherwise authenticated partner accountdestination to withdraw directly to the authenticated partner account, authenticated partner smart wallet, or an active withdrawal address allowlisted on the authenticated partner profilepartnerAccounts.addWithdrawalAddress() and partnerAccounts.deleteWithdrawalAddress() manage the allowlist with Privy identity-token auth; API-token auth is not used for those allowlist endpointsimport { Client } from '@limitless-exchange/sdk';
const client = new Client({
baseURL: 'https://api.limitless.exchange',
hmacCredentials: {
tokenId: process.env.LIMITLESS_API_TOKEN_ID!,
secret: process.env.LIMITLESS_API_TOKEN_SECRET!,
},
});
const redeem = await client.serverWallets.redeemPositions({
conditionId: '0x...',
onBehalfOf: 352,
});
const withdraw = await client.serverWallets.withdraw({
amount: '5000000',
onBehalfOf: 352,
});
To withdraw a partner child server wallet directly to a treasury address, allowlist the destination on the authenticated partner profile first. Use the same partner identity for the allowlist call and the same partner HMAC token for the withdraw call.
const identityToken = process.env.LIMITLESS_IDENTITY_TOKEN!;
const treasuryAddress = '0x...';
await client.partnerAccounts.addWithdrawalAddress(identityToken, {
address: treasuryAddress,
label: 'treasury',
});
const treasuryWithdraw = await client.serverWallets.withdraw({
amount: '5000000',
onBehalfOf: 352,
destination: treasuryAddress,
});
const ownWalletTreasuryWithdraw = await client.serverWallets.withdraw({
amount: '5000000',
destination: treasuryAddress,
});
// Optional cleanup when the destination should no longer be active.
await client.partnerAccounts.deleteWithdrawalAddress(identityToken, treasuryAddress);
redeem.hash or withdraw.hash may be an empty string for user-operation submissions. Track those calls using userOperationHash or transactionId.
For a complete runnable flow, see docs/code-samples/api-key-v3/server-wallet-redeem-withdraw.ts.
Important: Before placing orders, you must approve tokens for the exchange contracts. This is a one-time setup per wallet.
CLOB Markets:
market.venue.exchangemarket.venue.exchangeNegRisk Markets:
market.venue.exchangemarket.venue.exchange AND market.venue.adapterRun the approval setup script:
# Copy .env.example and configure your wallet
cp docs/code-samples/.env.example docs/code-samples/.env
# Edit .env and set your PRIVATE_KEY and market slug
# Then run the approval script
npx tsx docs/code-samples/setup-approvals.ts
import { ethers } from 'ethers';
import { MarketFetcher, getContractAddress } from '@limitless-exchange/sdk';
// 1. Fetch market to get venue addresses
const market = await marketFetcher.getMarket('market-slug');
// 2. Create contract instances
const usdc = new ethers.Contract(
getContractAddress('USDC'),
['function approve(address spender, uint256 amount) returns (bool)'],
wallet
);
const ctf = new ethers.Contract(
getContractAddress('CTF'),
['function setApprovalForAll(address operator, bool approved)'],
wallet
);
// 3. Approve USDC for BUY orders
await usdc.approve(market.venue.exchange, ethers.MaxUint256);
// 4. Approve CT for SELL orders
await ctf.setApprovalForAll(market.venue.exchange, true);
// 5. For NegRisk SELL orders, also approve adapter
if (market.negRiskRequestId) {
await ctf.setApprovalForAll(market.venue.adapter, true);
}
For complete examples, see docs/code-samples/setup-approvals.ts.
NegRisk markets are group markets with multiple related outcomes. Here's a quick example:
import { OrderClient, MarketFetcher, Side, OrderType } from '@limitless-exchange/sdk';
// 1. Fetch NegRisk group market
const marketFetcher = new MarketFetcher(httpClient);
const groupMarket = await marketFetcher.getMarket('largest-company-end-of-2025-1746118069282');
// 2. Select a submarket (e.g., Apple)
const appleMarket = groupMarket.markets[0];
const marketDetails = await marketFetcher.getMarket(appleMarket.slug);
// 3. Create order client (userData fetched automatically from profile)
const orderClient = new OrderClient({
httpClient,
wallet,
});
// 4. Place order on submarket (not group!)
const order = await orderClient.createOrder({
tokenId: marketDetails.tokens.yes,
price: 0.5,
size: 10,
side: Side.BUY,
orderType: OrderType.GTC,
marketSlug: appleMarket.slug, // Use submarket slug
});
Important: Always use the submarket slug for NegRisk orders, not the group market slug!
For more details, see the NegRisk Trading Guide.
GTC orders use price + size and can optionally set postOnly to reject an order that would immediately match.
import { OrderClient, Side, OrderType } from '@limitless-exchange/sdk';
const gtcOrder = await orderClient.createOrder({
tokenId: marketDetails.tokens.yes,
price: 0.42,
size: 10,
side: Side.BUY,
orderType: OrderType.GTC,
marketSlug: 'market-slug',
postOnly: true, // Supported only for GTC
});
console.log(gtcOrder.order.id);
Order creation can opt into receive-window freshness checks by passing recvWindow and, optionally, timestamp. These fields are top-level POST /orders request fields; they are not part of the EIP-712 signed order payload.
const order = await orderClient.createOrder({
tokenId: marketDetails.tokens.yes,
price: 0.42,
size: 10,
side: Side.BUY,
orderType: OrderType.GTC,
marketSlug: 'market-slug',
recvWindow: 1500, // ms; SDK stamps timestamp automatically when omitted
});
If omitted, no receive-window fields are sent and existing behavior is unchanged. Client-supplied recvWindow must be 1..10000; recvWindow=0 is reserved for server-side kill-switch behavior. API or OME receive-window rejections surface as APIError with status === 425.
For complete examples, see docs/code-samples/clob-gtc-order.ts.
FAK orders use the same price + size construction as GTC, but they only consume immediately available liquidity and cancel any remainder.
import { OrderClient, Side, OrderType } from '@limitless-exchange/sdk';
const fakOrder = await orderClient.createOrder({
tokenId: marketDetails.tokens.yes,
price: 0.45,
size: 10,
side: Side.BUY,
orderType: OrderType.FAK,
marketSlug: 'market-slug',
});
if (fakOrder.makerMatches && fakOrder.makerMatches.length > 0) {
console.log(`FAK matched immediately with ${fakOrder.makerMatches.length} fill(s)`);
} else {
console.log('FAK remainder was cancelled.');
}
Key Differences from GTC:
price + size like GTCpostOnly is not supported for FAKFor complete examples, see docs/code-samples/clob-fak-order.ts.
FOK orders execute immediately at the best available price or cancel entirely. Unlike GTC orders that use price + size, FOK orders use makerAmount.
Parameter Semantics:
makerAmount = total USDC to spendmakerAmount = number of shares to sellimport { OrderClient, Side, OrderType } from '@limitless-exchange/sdk';
// BUY FOK - spend 50 USDC at market price
const buyOrder = await orderClient.createOrder({
tokenId: marketDetails.tokens.yes,
makerAmount: 50, // 50 USDC to spend
side: Side.BUY,
orderType: OrderType.FOK,
marketSlug: 'market-slug',
});
// SELL FOK - sell 120 shares at market price
const sellOrder = await orderClient.createOrder({
tokenId: marketDetails.tokens.no,
makerAmount: 120, // 120 shares to sell
side: Side.SELL,
orderType: OrderType.FOK,
marketSlug: 'market-slug',
});
// Check execution
if (buyOrder.makerMatches && buyOrder.makerMatches.length > 0) {
console.log(`Order filled: ${buyOrder.makerMatches.length} matches`);
} else {
console.log('Order cancelled (no liquidity)');
}
Key Differences from GTC:
makerAmount (not price + size)For complete examples, see docs/code-samples/clob-fok-order.ts.
The SDK provides automatic retry logic for handling transient failures like rate limits and server errors:
import { withRetry, retryOnErrors } from '@limitless-exchange/sdk';
// Option 1: Wrapper function approach
const result = await withRetry(async () => await orderClient.createOrder(orderData), {
statusCodes: [429, 500, 503], // Retry on rate limits and server errors
maxRetries: 3,
delays: [2, 5, 10], // Wait 2s, then 5s, then 10s
onRetry: (attempt, error, delay) => {
console.log(`Retry ${attempt + 1} after ${delay}s: ${error.message}`);
},
});
// Option 2: Decorator approach (requires experimentalDecorators: true)
class TradingService {
@retryOnErrors({
statusCodes: [429, 500, 503],
maxRetries: 3,
exponentialBase: 2, // Exponential backoff: 1s, 2s, 4s
maxDelay: 30,
})
async placeOrder(orderData: any) {
return await this.orderClient.createOrder(orderData);
}
}
Key Features:
For detailed documentation, see the Error Handling & Retry Guide.
HttpClientHTTP client with API key authentication.
const httpClient = new HttpClient({
baseURL: 'https://api.limitless.exchange',
apiKey: process.env.LIMITLESS_API_KEY, // optional: can also be auto-loaded from LIMITLESS_API_KEY
timeout: 30000,
});
// Set or update API key
httpClient.setApiKey('sk_live_...');
// Make requests - X-API-Key header automatically included
const data = await httpClient.get('/endpoint');
await httpClient.post('/endpoint', { data });
For detailed documentation, see the docs directory:
Production-ready code samples are available in docs/code-samples:
basic-auth.ts - API key authentication setupwith-logging.ts - Authentication with custom loggingauth-retry.ts - Authentication with retry logicerror-handling.ts - Comprehensive error handlingCLOB Markets:
clob-fok-order.ts - Fill-or-Kill market ordersclob-gtc-order.ts - Good-Til-Cancelled limit orders with postOnlyclob-fak-order.ts - Fill-And-Kill limit ordersNegRisk Markets:
negrisk-fok-order.ts - FOK orders on group marketsnegrisk-gtc-order.ts - GTC orders on NegRisk submarketsget-active-markets.ts - Fetching active markets with sorting and paginationorderbook.ts - Fetching and analyzing orderbookspositions.ts - Portfolio and position trackingfluent-api-trading-workflow.ts - Complete trading workflowwebsocket-events.ts - Real-time trading events and subscriptionsapi-key-v3/partner-account-allowances.ts - Check and retry server-wallet allowance recovery with partner HMAC auth onlyapi-key-v3/server-wallet-redeem-withdraw.ts - Redeem resolved positions and optionally withdraw from a server walletpnpm install
pnpm build
pnpm test
pnpm test:coverage
pnpm lint
pnpm format
src/
├── types/ # TypeScript type definitions
│ ├── markets.ts # Market and active markets types
│ ├── orders.ts # Order types
│ ├── auth.ts # User profile types
│ └── ...
├── api/ # HTTP client and API utilities
│ ├── http.ts # HTTP client with API key auth
│ ├── errors.ts # Error handling
│ └── retry.ts # Retry logic
├── markets/ # Market data modules
│ ├── fetcher.ts # Market and orderbook fetching
│ └── index.ts
├── orders/ # Order management
│ └── client.ts # Order creation and management
├── portfolio/ # Portfolio and positions
│ └── fetcher.ts
├── websocket/ # Real-time data streaming
│ └── client.ts
├── api/ # HTTP client and API utilities
│ ├── http.ts # HTTP client
│ └── errors.ts # API error handling
└── utils/ # Shared utilities and constants
tests/
├── auth/ # Authentication tests
└── markets/ # Market fetcher tests
└── fetcher.test.ts //etc.
docs/
├── code-samples/ # Production-ready examples
│ ├── get-active-markets.ts
│ ├── clob-fok-order.ts
│ └── ...
└── */ # Documentation guides
Release Date: May 27, 2026
Latest release with optional receive-window controls for normal and delegated order creation.
FAK limit orders plus postOnly on GTCGET /profiles/metimestamp / recvWindow freshness controls for order creation/navigation, /market-pages/by-path, /market-pages/:id/markets, /property-keyspostOnly, optional receive-window controls, tick alignment, automatic signing, IEEE-safe create-order payload parsingtimestamp / recvWindow order creation optionsRelease Date: May 27, 2026
Release with authenticated profile reads and partner sub-account listing/recovery.
GET /profiles/meclient.portfolio.getProfile() without an addressRelease Date: May 4, 2026
Release with partner withdrawal-address allowlist helpers, server-wallet withdrawals to explicit whitelisted treasury destinations, and expanded WebSocket event coverage.
429, and 409For complete release notes, see CHANGELOG.md.
MIT - See LICENSE file for details
FAQs
TypeScript SDK for Limitless Exchange CLOB and NegRisk trading
We found that @limitless-exchange/sdk demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 3 open source maintainers collaborating on the project.
Did you know?

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Security News
RubyGems and Bundler 4.0.13 introduced an opt-in cooldown feature that delays newly published gems during dependency resolution.

Security News
pnpm 11.5 now recognizes npm staged publish approvals in release metadata, preventing those releases from being mistaken for lower-trust package publishes.

Security News
Federal audit finds NIST lacked a plan to clear the NVD backlog, wasted funds on duplicate work, and delayed use of CISA data.