PERPETUAL BINARY API
Build trading bots for 5-minute binary perpetual markets. Real-time WebSocket streams, 23,000+ assets, on-chain execution on Base.
// QUICK START
Get the current BTC perpetual market status:
curl https://cymetica.com/api/v1/perpetual/markets/BTC/status
// CONTENTS
// QUICK START
Get trading in 60 seconds. No API key required.
1. Check Market Status
curl https://cymetica.com/api/v1/perpetual/markets/ETH/status
Returns current epoch, prices, and time remaining.
2. Stream Real-Time Updates
// JavaScript - works in browser console
const ws = new WebSocket('wss://cymetica.com/api/v1/perpetual/ws/ETH');
ws.onmessage = (e) => console.log(JSON.parse(e.data));
Receive status updates every 30 seconds. Send "ping" for instant updates.
3. Build a Simple Bot
# Python - pip install aiohttp
import aiohttp, asyncio
async def check_signal():
async with aiohttp.ClientSession() as s:
async with s.get('https://cymetica.com/api/v1/perpetual/markets/ETH/status') as r:
data = await r.json()
price = float(data['yes_price'])
if price < 0.45:
print(f"BUY YES @ {price}")
elif price > 0.55:
print(f"BUY NO @ {price}")
else:
print(f"HOLD @ {price}")
asyncio.run(check_signal())
Active Markets
ETH, SOL, DOGE, AAPL_V2, NVDA_V2, TSLA_V2, LINK_V2
Epoch Duration
5 minutes (4.5 min trading window)
Chain
Base L2 (Chain ID: 8453)
// BASE URL
https://cymetica.com/api/v1/perpetual
All endpoints are public and do not require authentication for read operations.
// ASSETS
Query Parameters
Example Request
curl "https://cymetica.com/api/v1/perpetual/assets?asset_type=crypto&limit=10"
Response
[
{
"symbol": "BTC",
"name": "Bitcoin",
"asset_type": "crypto",
"has_market": true,
"oracle_tier": 1,
"current_price": "94250.50"
},
...
]
Response
{
"total_assets": 23779,
"cryptocurrencies": 4469,
"stocks": 19310,
"active_markets": 5
}
// MARKETS & STATUS
Path Parameters
Response Schema
Example
curl https://cymetica.com/api/v1/perpetual/markets/BTC/status
{
"epoch": 42,
"start_price": "94250.00",
"current_price": "94312.50",
"time_remaining": 180,
"trading_active": true,
"yes_price": "0.55",
"no_price": "0.45",
"yes_pool": "15000.00",
"no_pool": "12000.00"
}
Response
{
"epoch": 41,
"start_price": "94100.00",
"end_price": "94250.00",
"outcome": "YES",
"yes_pool": "18000.00",
"no_pool": "14000.00",
"resolved_at": "2026-01-12T01:00:00Z"
}
// ORDERBOOK & POSITIONS
{
"yes_bids": [
{"price": "0.54", "size": "500.00"},
{"price": "0.53", "size": "1200.00"}
],
"yes_asks": [
{"price": "0.56", "size": "800.00"},
{"price": "0.57", "size": "1500.00"}
],
"no_bids": [...],
"no_asks": [...],
"spread": "0.02"
}
Query Parameters
curl "https://cymetica.com/api/v1/perpetual/markets/BTC/position/0x1234...?epoch=42"
{
"yes_tokens": "100.00",
"no_tokens": "0.00",
"entry_price": "0.52",
"unrealized_pnl": "3.00"
}
Query Parameters
[
{
"timestamp": "2026-01-12T01:00:00Z",
"open": "94100.00",
"high": "94350.00",
"low": "94050.00",
"close": "94250.00",
"volume": "50000.00"
},
...
]
// WEBSOCKET STREAMING
Connection URL
wss://cymetica.com/api/v1/perpetual/ws/BTC
Message Types
Example Messages
// Status update (sent on connect and every 30s)
{
"type": "status",
"symbol": "ETH",
"data": {
"epoch": 2096,
"start_price": "3124.19",
"time_remaining": 180,
"trading_active": true,
"yes_price": "0.55",
"no_price": "0.45",
"yes_supply": "1000.00",
"no_supply": "800.00"
},
"timestamp": "2026-01-12T01:15:30Z"
}
// Error (if market not found)
{
"type": "error",
"message": "Market for XYZ not found",
"timestamp": "2026-01-12T01:15:30Z"
}
Note: Send "ping" to receive "pong" and trigger an immediate status update. Active markets: ETH, SOL, DOGE, AAPL_V2, NVDA_V2, TSLA_V2, LINK_V2
JavaScript Example
const ws = new WebSocket('wss://cymetica.com/api/v1/perpetual/ws/ETH');
ws.onopen = () => {
console.log('Connected');
// Send ping every 10s to get frequent updates
setInterval(() => ws.send('ping'), 10000);
};
ws.onmessage = (event) => {
const msg = JSON.parse(event.data);
if (msg.type === 'status') {
const d = msg.data;
console.log(`Epoch ${d.epoch}: ${d.time_remaining}s left`);
console.log(`YES: ${d.yes_price} | NO: ${d.no_price}`);
}
if (msg.type === 'error') {
console.error(`Error: ${msg.message}`);
}
};
ws.onerror = (error) => console.error('WebSocket error:', error);
ws.onclose = () => console.log('Disconnected');
// MRT TOKENS
Market Reward Tokens
Each perpetual market has its own MRT token that trades on Aerodrome DEX (Base). MRTs provide liquidity rewards and creator fees.
curl https://cymetica.com/api/v1/perpetual/markets/BTC/mrt
{
"token_address": "0x7Bf1CeF07ce0d5E005740a72768016c47c515bF6",
"token_symbol": "BTC-PERP-MRT",
"token_name": "BTC Perpetual MRT",
"pool_address": "0x2C38aA68b0885Bf67e9A7DeB6A965D1F6F09CE3c",
"is_graduated": true,
"total_raised": "100.00",
"graduation_progress": 10000,
"dex_links": {
"dexscreener": "https://dexscreener.com/base/0x2C38...",
"geckoterminal": "https://geckoterminal.com/base/pools/0x2C38...",
"basescan_token": "https://basescan.org/token/0x7Bf1...",
"aerodrome": "https://aerodrome.finance/swap?from=0x7Bf1..."
}
}
// BOT EXAMPLES
Python Trading Bot
import asyncio
import aiohttp
import json
API_BASE = "https://cymetica.com/api/v1/perpetual"
WS_URL = "wss://cymetica.com/api/v1/perpetual/ws"
class PerpetualBot:
def __init__(self, symbol: str):
self.symbol = symbol
self.epoch = None
self.trading_active = False
async def connect(self):
"""Connect to WebSocket and receive updates."""
async with aiohttp.ClientSession() as session:
async with session.ws_connect(f"{WS_URL}/{self.symbol}") as ws:
print(f"Connected to {self.symbol} stream")
# Send pings to receive frequent status updates
async def ping_loop():
while True:
await asyncio.sleep(10)
await ws.send_str("ping")
asyncio.create_task(ping_loop())
async for msg in ws:
if msg.type == aiohttp.WSMsgType.TEXT:
if msg.data != "pong":
await self.handle_message(json.loads(msg.data))
async def handle_message(self, message: dict):
"""Process incoming WebSocket messages."""
msg_type = message.get("type")
if msg_type == "status":
# Data is nested under 'data' key
data = message.get("data", {})
self.epoch = data.get("epoch")
self.trading_active = data.get("trading_active")
time_left = data.get("time_remaining")
yes_price = float(data.get("yes_price", 0.5))
if self.trading_active:
print(f"Epoch {self.epoch} | {time_left}s | YES: {yes_price:.2f}")
# Example strategy: Buy YES if undervalued
if yes_price < 0.45:
print("Signal: BUY YES (undervalued)")
elif msg_type == "error":
print(f"Error: {message.get('message')}")
async def get_status(self) -> dict:
"""Fetch current market status via REST."""
async with aiohttp.ClientSession() as session:
async with session.get(
f"{API_BASE}/markets/{self.symbol}/status"
) as resp:
return await resp.json()
async def main():
# Use ETH (active market) - see /markets for all available
bot = PerpetualBot("ETH")
# Get initial status via REST
status = await bot.get_status()
print(f"Starting epoch: {status['epoch']}")
print(f"YES price: {status['yes_price']}")
# Connect WebSocket for real-time updates
await bot.connect()
if __name__ == "__main__":
asyncio.run(main())
Multi-Asset Scanner
import asyncio
import aiohttp
API_BASE = "https://cymetica.com/api/v1/perpetual"
# Active markets - check /markets endpoint for current list
SYMBOLS = ["ETH", "SOL", "DOGE", "NVDA_V2", "AAPL_V2", "TSLA_V2"]
async def scan_markets():
"""Scan all markets for trading opportunities."""
async with aiohttp.ClientSession() as session:
tasks = [fetch_status(session, sym) for sym in SYMBOLS]
results = await asyncio.gather(*tasks)
print("\n=== MARKET SCANNER ===")
for symbol, status in zip(SYMBOLS, results):
if status and status.get("trading_active"):
yes = float(status["yes_price"])
time_left = status["time_remaining"]
# Flag opportunities
signal = ""
if yes < 0.40:
signal = " << BUY YES"
elif yes > 0.60:
signal = " << BUY NO"
print(f"{symbol}: YES={yes:.2f} ({time_left}s){signal}")
async def fetch_status(session, symbol):
try:
async with session.get(
f"{API_BASE}/markets/{symbol}/status"
) as resp:
if resp.status == 200:
return await resp.json()
except Exception as e:
print(f"Error fetching {symbol}: {e}")
return None
if __name__ == "__main__":
asyncio.run(scan_markets())
On-Chain Trading Bot
Place trades directly on Base chain using your own wallet. Requires: collateral tokens (CYM1). Gas is auto-swapped from your tokens when needed.
import asyncio
from web3 import Web3
from eth_account import Account
# Configuration
RPC_URL = "https://base.publicnode.com"
PRIVATE_KEY = "your_private_key_here" # Keep secret!
# Contract ABIs (minimal)
MARKET_ABI = [
{"inputs": [{"name": "amount", "type": "uint256"}], "name": "buyYes", "outputs": [], "stateMutability": "nonpayable", "type": "function"},
{"inputs": [{"name": "amount", "type": "uint256"}], "name": "buyNo", "outputs": [], "stateMutability": "nonpayable", "type": "function"},
{"inputs": [], "name": "currentEpoch", "outputs": [{"type": "uint256"}], "stateMutability": "view", "type": "function"},
{"inputs": [], "name": "collateralToken", "outputs": [{"type": "address"}], "stateMutability": "view", "type": "function"},
{"inputs": [], "name": "getCurrentStatus", "outputs": [{"type": "uint256"}, {"type": "uint256"}, {"type": "uint256"}, {"type": "bool"}, {"type": "uint256"}, {"type": "uint256"}, {"type": "uint256"}, {"type": "uint256"}], "stateMutability": "view", "type": "function"},
]
ERC20_ABI = [
{"inputs": [{"name": "spender", "type": "address"}, {"name": "amount", "type": "uint256"}], "name": "approve", "outputs": [{"type": "bool"}], "stateMutability": "nonpayable", "type": "function"},
{"inputs": [{"name": "owner", "type": "address"}, {"name": "spender", "type": "address"}], "name": "allowance", "outputs": [{"type": "uint256"}], "stateMutability": "view", "type": "function"},
{"inputs": [{"name": "account", "type": "address"}], "name": "balanceOf", "outputs": [{"type": "uint256"}], "stateMutability": "view", "type": "function"},
]
class TradingBot:
def __init__(self, market_address: str):
self.w3 = Web3(Web3.HTTPProvider(RPC_URL))
self.account = Account.from_key(PRIVATE_KEY)
self.market = self.w3.eth.contract(
address=Web3.to_checksum_address(market_address),
abi=MARKET_ABI
)
# Get collateral token
collateral_addr = self.market.functions.collateralToken().call()
self.collateral = self.w3.eth.contract(
address=collateral_addr,
abi=ERC20_ABI
)
def get_status(self) -> dict:
"""Get current market status."""
status = self.market.functions.getCurrentStatus().call()
return {
"epoch": status[0],
"start_price": status[1] / 1e18,
"time_remaining": status[2],
"trading_active": status[3],
"yes_price": status[4] / 1e18,
"no_price": status[5] / 1e18,
}
def approve_collateral(self, amount: float):
"""Approve market to spend collateral."""
amount_wei = self.w3.to_wei(amount, "ether")
tx = self.collateral.functions.approve(
self.market.address, amount_wei
).build_transaction({
"from": self.account.address,
"nonce": self.w3.eth.get_transaction_count(self.account.address),
"gas": 100000,
**self._get_gas_params(),
})
signed = self.account.sign_transaction(tx)
tx_hash = self.w3.eth.send_raw_transaction(signed.raw_transaction)
print(f"Approval tx: {tx_hash.hex()}")
return self.w3.eth.wait_for_transaction_receipt(tx_hash)
def _get_gas_params(self) -> dict:
"""Get live EIP-1559 gas parameters. NEVER hardcode gwei values."""
from src.utils.gas_price import get_gas_params_sync
max_fee, priority_fee = get_gas_params_sync(self.w3)
return {
"maxFeePerGas": max_fee,
"maxPriorityFeePerGas": priority_fee,
}
def buy_yes(self, amount: float) -> str:
"""Buy YES tokens (bet price goes UP)."""
return self._execute_trade("buyYes", amount)
def buy_no(self, amount: float) -> str:
"""Buy NO tokens (bet price goes DOWN)."""
return self._execute_trade("buyNo", amount)
def _execute_trade(self, method: str, amount: float) -> str:
amount_wei = self.w3.to_wei(amount, "ether")
# Check allowance
allowance = self.collateral.functions.allowance(
self.account.address, self.market.address
).call()
if allowance < amount_wei:
print("Approving collateral...")
self.approve_collateral(amount * 2) # Approve extra
# Build trade tx
func = getattr(self.market.functions, method)
tx = func(amount_wei).build_transaction({
"from": self.account.address,
"nonce": self.w3.eth.get_transaction_count(self.account.address),
"gas": 300000,
**self._get_gas_params(),
})
signed = self.account.sign_transaction(tx)
tx_hash = self.w3.eth.send_raw_transaction(signed.raw_transaction)
print(f"Trade tx: {tx_hash.hex()}")
receipt = self.w3.eth.wait_for_transaction_receipt(tx_hash)
if receipt.status == 1:
print(f"Trade successful! Gas used: {receipt.gasUsed}")
else:
print("Trade failed!")
return tx_hash.hex()
# Example usage
if __name__ == "__main__":
# ETH market address (get from /markets endpoint)
MARKET = "0x54F033598D130f374C62DB5313816f7a83b7Bb2b"
bot = TradingBot(MARKET)
# Check status
status = bot.get_status()
print(f"Epoch {status['epoch']} | Trading: {status['trading_active']}")
print(f"YES: {status['yes_price']:.2f} | NO: {status['no_price']:.2f}")
# Place trade if active
if status["trading_active"] and status["yes_price"] < 0.45:
bot.buy_yes(1.0) # Buy 1 CYM1 worth of YES
elif status["trading_active"] and status["yes_price"] > 0.55:
bot.buy_no(1.0) # Buy 1 CYM1 worth of NO
JavaScript Trading Bot (ethers.js)
Browser or Node.js trading bot using ethers.js. Install: npm install ethers
import { ethers } from 'ethers';
// Configuration
const RPC_URL = 'https://base.publicnode.com';
const PRIVATE_KEY = 'your_private_key_here'; // Keep secret!
// Contract ABIs (minimal)
const MARKET_ABI = [
'function buyYes(uint256 amount) external',
'function buyNo(uint256 amount) external',
'function currentEpoch() view returns (uint256)',
'function collateralToken() view returns (address)',
'function getCurrentStatus() view returns (uint256, uint256, uint256, bool, uint256, uint256, uint256, uint256)',
];
const ERC20_ABI = [
'function approve(address spender, uint256 amount) returns (bool)',
'function allowance(address owner, address spender) view returns (uint256)',
'function balanceOf(address account) view returns (uint256)',
'function symbol() view returns (string)',
];
class TradingBot {
constructor(marketAddress) {
this.provider = new ethers.JsonRpcProvider(RPC_URL);
this.wallet = new ethers.Wallet(PRIVATE_KEY, this.provider);
this.market = new ethers.Contract(marketAddress, MARKET_ABI, this.wallet);
this.collateral = null; // Set in init()
}
async init() {
const collateralAddr = await this.market.collateralToken();
this.collateral = new ethers.Contract(collateralAddr, ERC20_ABI, this.wallet);
console.log(`Collateral: ${await this.collateral.symbol()}`);
}
async getStatus() {
const status = await this.market.getCurrentStatus();
return {
epoch: Number(status[0]),
startPrice: Number(status[1]) / 1e18,
timeRemaining: Number(status[2]),
tradingActive: status[3],
yesPrice: Number(status[4]) / 1e18,
noPrice: Number(status[5]) / 1e18,
};
}
async approveCollateral(amount) {
const amountWei = ethers.parseEther(amount.toString());
const tx = await this.collateral.approve(this.market.target, amountWei);
console.log(`Approval tx: ${tx.hash}`);
await tx.wait();
return tx.hash;
}
async buyYes(amount) {
return this._executeTrade('buyYes', amount);
}
async buyNo(amount) {
return this._executeTrade('buyNo', amount);
}
async _executeTrade(method, amount) {
const amountWei = ethers.parseEther(amount.toString());
// Check allowance
const allowance = await this.collateral.allowance(
this.wallet.address,
this.market.target
);
if (allowance < amountWei) {
console.log('Approving collateral...');
await this.approveCollateral(amount * 2);
}
// Execute trade
const tx = await this.market[method](amountWei, {
gasLimit: 300000n,
});
console.log(`Trade tx: ${tx.hash}`);
const receipt = await tx.wait();
if (receipt.status === 1) {
console.log(`Trade successful! Gas: ${receipt.gasUsed}`);
} else {
console.log('Trade failed!');
}
return tx.hash;
}
}
// Example usage
async function main() {
// ETH market address (get from /markets endpoint)
const MARKET = '0x54F033598D130f374C62DB5313816f7a83b7Bb2b';
const bot = new TradingBot(MARKET);
await bot.init();
// Check status
const status = await bot.getStatus();
console.log(`Epoch ${status.epoch} | Trading: ${status.tradingActive}`);
console.log(`YES: ${status.yesPrice.toFixed(2)} | NO: ${status.noPrice.toFixed(2)}`);
// Place trade if active
if (status.tradingActive && status.yesPrice < 0.45) {
await bot.buyYes(1.0); // Buy 1 CYM1 worth of YES
} else if (status.tradingActive && status.yesPrice > 0.55) {
await bot.buyNo(1.0); // Buy 1 CYM1 worth of NO
}
}
main().catch(console.error);
Trading Requirements
- Collateral: CYM1 tokens (get from market's collateralToken())
- Gas: Auto-swapped from your tokens when needed
- Timing: Trades only accepted during trading window (first 4.5 min of epoch)
- Chain: Base L2 (Chain ID: 8453)
// TYPESCRIPT TYPES
Full type definitions for TypeScript/JavaScript projects.
Download perpetual-types.ts// ============================================
// PERPETUAL BINARY API - TypeScript Types
// ============================================
// --- Assets ---
interface Asset {
symbol: string;
name: string;
asset_type: 'crypto' | 'stock' | 'forex' | 'commodity';
sector: string | null;
market_cap: number;
oracle_tier: number;
has_market: boolean;
market_address: string | null;
logo_url: string | null;
coingecko_id: string | null;
}
// Note: GET /assets returns Asset[] directly (not wrapped)
// --- Markets ---
interface Market {
symbol: string;
name: string;
asset_type: string;
market_address: string;
yes_token: string;
no_token: string;
collateral_token: string;
oracle_tier: number;
epoch_duration: number;
chain_id: number;
}
interface MarketStatus {
symbol: string;
market_address: string;
epoch: number;
start_price: string;
time_remaining: number;
trading_active: boolean;
yes_price: string;
no_price: string;
yes_supply: string;
no_supply: string;
timestamp: string;
}
// --- Orderbook ---
interface OrderbookLevel {
price: string;
size: string;
total: string;
}
interface Orderbook {
symbol: string;
market_address: string;
bids: OrderbookLevel[]; // YES buy orders
asks: OrderbookLevel[]; // NO buy orders (inverse)
timestamp: string;
}
// --- MRT Tokens ---
interface MRTInfo {
token_address: string;
token_symbol: string;
token_name: string;
pool_address: string;
is_graduated: boolean;
total_raised: string;
graduation_progress: number;
dex_links: {
dexscreener: string;
geckoterminal: string;
basescan_token: string;
aerodrome: string;
};
}
// --- WebSocket Messages ---
interface WSStatusMessage {
type: 'status';
symbol: string;
data: {
epoch: number;
start_price: string;
time_remaining: number;
trading_active: boolean;
yes_price: string;
no_price: string;
yes_supply: string;
no_supply: string;
};
timestamp: string;
}
interface WSPriceMessage {
type: 'price';
symbol: string;
data: {
price: string;
change_24h: string;
};
timestamp: string;
}
interface WSOrderbookMessage {
type: 'orderbook';
symbol: string;
data: Orderbook;
timestamp: string;
}
interface WSEpochResolvedMessage {
type: 'epoch_resolved';
symbol: string;
data: {
epoch: number;
outcome: 'YES' | 'NO';
start_price: string;
end_price: string;
yes_payout: string;
no_payout: string;
};
timestamp: string;
}
interface WSErrorMessage {
type: 'error';
message: string;
timestamp: string;
}
type WSMessage =
| WSStatusMessage
| WSPriceMessage
| WSOrderbookMessage
| WSEpochResolvedMessage
| WSErrorMessage;
// --- On-Chain Contract Types ---
interface ContractStatus {
epoch: bigint;
startPrice: bigint;
timeRemaining: bigint;
tradingActive: boolean;
yesPrice: bigint;
noPrice: bigint;
yesSupply: bigint;
noSupply: bigint;
}
// --- API Client Helper ---
class PerpetualAPI {
constructor(private baseUrl = 'https://cymetica.com/api/v1/perpetual') {}
async getAssets(params?: {
asset_type?: string;
search?: string;
page?: number;
page_size?: number;
}): Promise<Asset[]> {
const url = new URL(`${this.baseUrl}/assets`);
if (params) {
Object.entries(params).forEach(([k, v]) =>
v !== undefined && url.searchParams.set(k, String(v))
);
}
const res = await fetch(url);
return res.json();
}
async getMarkets(): Promise<Market[]> {
const res = await fetch(`${this.baseUrl}/markets`);
return res.json();
}
async getMarketStatus(symbol: string): Promise<MarketStatus> {
const res = await fetch(`${this.baseUrl}/markets/${symbol}/status`);
return res.json();
}
async getOrderbook(symbol: string): Promise<Orderbook> {
const res = await fetch(`${this.baseUrl}/markets/${symbol}/orderbook`);
return res.json();
}
async getMRT(symbol: string): Promise<MRTInfo> {
const res = await fetch(`${this.baseUrl}/markets/${symbol}/mrt`);
return res.json();
}
connectWebSocket(symbol: string, onMessage: (msg: WSMessage) => void): WebSocket {
const ws = new WebSocket(`wss://cymetica.com/api/v1/perpetual/ws/${symbol}`);
ws.onmessage = (e) => {
if (e.data !== 'pong') {
onMessage(JSON.parse(e.data));
}
};
// Ping every 10s for frequent updates
ws.onopen = () => setInterval(() => ws.send('ping'), 10000);
return ws;
}
}
// --- Usage Example ---
const api = new PerpetualAPI();
// Fetch market status
const status = await api.getMarketStatus('ETH');
console.log(`Epoch ${status.epoch}: YES=${status.yes_price}`);
// Stream updates
api.connectWebSocket('ETH', (msg) => {
if (msg.type === 'status') {
console.log(`Time left: ${msg.data.time_remaining}s`);
}
});
Installation
# Download and add to your project
curl -O https://cymetica.com/static/sdk/perpetual-types.ts
# Or copy the download URL
wget https://cymetica.com/static/sdk/perpetual-types.ts
Then import in your code: import { PerpetualAPI, MarketStatus } from './perpetual-types';
// ERRORS & RATE LIMITS
Rate Limits
Public endpoints are rate limited by IP address. Authenticated endpoints use API key limits.
| Endpoint Type | Limit | Window |
|---|---|---|
| Public REST endpoints | 100 requests | per minute |
| WebSocket connections | 10 connections | per IP |
| WebSocket messages | 60 messages | per minute |
Rate limit headers are included in all responses:
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 87
X-RateLimit-Reset: 1700000060
HTTP Status Codes
| Code | Meaning |
|---|---|
| 200 | Success |
| 400 | Bad Request - Invalid parameters |
| 404 | Not Found - Market or resource doesn't exist |
| 429 | Rate Limit Exceeded - Wait and retry |
| 500 | Server Error - Try again later |
Error Response Format
{
"success": false,
"error": {
"code": "MARKET_NOT_FOUND",
"message": "Market 'XYZ' not found",
"details": {
"symbol": "XYZ"
}
}
}
Error Codes
| Code | HTTP | Description |
|---|---|---|
MARKET_NOT_FOUND |
404 | Market symbol doesn't exist or has no active market |
MARKET_CLOSED |
400 | Trading window has ended for current epoch |
INVALID_PARAMETER |
400 | Request parameter is invalid or out of range |
INVALID_AMOUNT |
400 | Trade amount is invalid (must be > 0) |
INSUFFICIENT_BALANCE |
400 | Not enough collateral tokens for trade |
RATE_LIMIT_EXCEEDED |
429 | Too many requests - check Retry-After header |
INTERNAL_SERVER_ERROR |
500 | Server error - try again later |
Handling Rate Limits
async function fetchWithRetry(url, maxRetries = 3) {
for (let i = 0; i < maxRetries; i++) {
const res = await fetch(url);
if (res.status === 429) {
const retryAfter = res.headers.get('Retry-After') || 60;
console.log(`Rate limited. Waiting ${retryAfter}s...`);
await new Promise(r => setTimeout(r, retryAfter * 1000));
continue;
}
return res.json();
}
throw new Error('Max retries exceeded');
}
Best Practices
- Use WebSocket for real-time data instead of polling REST endpoints
- Cache responses when possible (market list changes rarely)
- Implement exponential backoff for retries
- Check
X-RateLimit-Remainingheader before making requests - Send "ping" on WebSocket every 10s for frequent status updates