# Frontrun Trading API > Programmatic REST API for executing Solana token trades with KMS-managed custodial wallets. Supports BUY/SELL via auto-routed AMM or Jupiter aggregator, ASYNC/SYNC confirmation modes, and real-time WebSocket balance push. ## Authentication All endpoints require `Authorization: Bearer ` header. API key format: `fr_sk_` prefix + 64-character suffix (70 characters total). Obtained via portal at https://frontrun.pro/api. Max 12 active keys per account. ## Base URLs REST: `https://solana.frontrun.pro/api/v1/trading-api` WebSocket: `wss://solana.frontrun.pro/trading` ## Endpoints ### List Wallets ``` GET /wallets Authorization: Bearer ``` Response: ```json { "status": true, "data": [ { "address": "29wLeR2adFT4awKwzUtYJyRraxoNf9VzJhQpE2Czx1Y8", "chain": "SOLANA", "name": "my-wallet", "status": "ACTIVE", "balance": "1000000000" } ] } ``` Note: `balance` is in lamports (1 SOL = 1,000,000,000 lamports). Value is cached, not real-time. --- ### Create Wallet ``` POST /wallets/create Authorization: Bearer Content-Type: application/json ``` Body (all optional): ```json { "name": "string", "chain": "SOLANA" } ``` Response: ```json { "status": true, "data": { "address": "29wLeR2adFT4awKwzUtYJyRraxoNf9VzJhQpE2Czx1Y8", "chain": "SOLANA", "name": "my-wallet", "status": "ACTIVE", "balance": "0", "createdAt": "2026-02-18T10:00:00.000Z" } } ``` --- ### Import Wallet ``` POST /wallets/import Authorization: Bearer Content-Type: application/json ``` Body: ```json { "privateKey": "Base58-encoded private key", "name": "string (optional)" } ``` Idempotency: if the address already exists under the same API key, returns existing wallet. If address belongs to a different API key, returns 400. --- ### Execute Trade ``` POST /trade Authorization: Bearer Content-Type: application/json ``` Body parameters: | Field | Type | Required | Constraints | |-------|------|----------|-------------| | `tokenMint` | string | yes | Valid Solana mint address | | `side` | `"BUY"` \| `"SELL"` | yes | | | `uiInputAmount` | string | conditional | Required for BUY (SOL amount to spend). Required for SELL when `sellPercent` omitted (token amount to sell). Positive decimal string, e.g. `"0.05"` | | `sellPercent` | number | no | SELL only. Sells this percentage of current wallet token balance. Range: 0.01–100. When provided, `uiInputAmount` is ignored | | `slippageBasisPoint` | integer | yes | Slippage tolerance in basis points. Range: 0–10000 (100 = 1%) | | `walletAddress` | string | no | Specific wallet to use. Defaults to earliest created wallet under the API key | | `confirmationMode` | `"ASYNC"` \| `"SYNC"` | no | Default: `"ASYNC"` | | `commitment` | `"processed"` \| `"confirmed"` | no | SYNC mode only. Default: `"processed"` | BUY example — spend 0.05 SOL: ```json { "tokenMint": "J7BLM8GpZd7vGjR9zBYZQQVeRoVc4dNfcfLpyCEEpump", "side": "BUY", "uiInputAmount": "0.05", "slippageBasisPoint": 500 } ``` SELL by amount — sell exact token quantity: ```json { "tokenMint": "J7BLM8GpZd7vGjR9zBYZQQVeRoVc4dNfcfLpyCEEpump", "side": "SELL", "uiInputAmount": "1000000", "slippageBasisPoint": 500 } ``` SELL by percent — sell 100% of holdings (no need to know exact balance): ```json { "tokenMint": "J7BLM8GpZd7vGjR9zBYZQQVeRoVc4dNfcfLpyCEEpump", "side": "SELL", "sellPercent": 100, "slippageBasisPoint": 500 } ``` ASYNC response (`confirmed: false`, returns immediately): ```json { "status": true, "data": { "signature": "5eykt4UsFv8P8NJdTREpY1vzqKqZKvdpKuc147dw2N9d4...", "walletAddress": "29wLeR2adFT4awKwzUtYJyRraxoNf9VzJhQpE2Czx1Y8", "tokenMint": "J7BLM8GpZd7vGjR9zBYZQQVeRoVc4dNfcfLpyCEEpump", "side": "BUY", "status": "SUCCESS", "confirmed": false } } ``` SYNC response (waits up to 10s for on-chain confirmation): ```json { "status": true, "data": { "signature": "5eykt4UsFv8P8NJdTREpY1vzqKqZKvdpKuc147dw2N9d4...", "status": "SUCCESS", "confirmed": true } } ``` Per-wallet concurrency: only one trade can be in-flight per wallet at a time (30-second distributed lock). Concurrent requests to the same wallet return 409. SYNC 504 does NOT mean the trade failed — the transaction may have landed on-chain. Always verify via signature before retrying to avoid duplicate orders. --- ## Error Response Format All errors follow this structure: ```json { "status": false, "code": 400, "message": "descriptive error message", "data": null } ``` ## Error Codes | HTTP Status | Cause | Action | |-------------|-------|--------| | 400 | Invalid params (bad mint address, malformed uiInputAmount, no wallet available under API key) | Fix request params | | 403 | Wallet not owned by this API key, or key revoked | Check API key and wallet ownership | | 404 | Wallet address not found | Verify walletAddress exists | | 409 | Wallet locked by in-flight trade (30s TTL) | Wait and retry, or use a different wallet | | 422 | Trade rejected: insufficient SOL balance, slippage exceeded, on-chain rejection | Check balance, increase slippageBasisPoint | | 503 | Trade engine not ready | Retry after brief delay | | 504 | SYNC mode confirmation timeout (10s exceeded) | Verify signature on-chain before retrying | | 500 | Internal error | Check `message` field for details | --- ## WebSocket — Real-Time Trade Confirmation (ASYNC mode) Use WebSocket to receive on-chain confirmation without polling. The server pushes `trading.balanceUpdated` events when a subscribed wallet's balance changes on-chain. Connection: `wss://solana.frontrun.pro/trading` (Socket.IO protocol) ### Flow 1. Connect and wait for `trading.connected` 2. Emit `trading.batchSubscribeWallets` to subscribe wallets 3. Wait for `trading.batchSubscribed` confirmation 4. Call `POST /trade` (ASYNC mode), store returned `signature` 5. On each `trading.balanceUpdated` event, compare `data.signature` to your stored signature — match means the trade is confirmed ### Events Client → Server: `trading.batchSubscribeWallets` ```json { "wallets": [ { "chain": "SOLANA", "address": "29wLeR2adFT4awKwzUtYJyRraxoNf9VzJhQpE2Czx1Y8" } ] } ``` Max 50 wallets per client. Server → Client: `trading.connected` — emitted on successful connection, no payload. `trading.batchSubscribed` ```json { "v": 1, "ts": "2026-02-18T10:00:00.000Z", "type": "trading.batchSubscribed", "ok": true, "data": { "success": ["SOLANA:29wLeR2adFT4awKwzUtYJyRraxoNf9VzJhQpE2Czx1Y8"], "failed": [], "totalRequested": 1, "totalSuccess": 1, "totalFailed": 0 } } ``` `trading.balanceUpdated` ```json { "v": 1, "ts": "2026-02-18T10:00:01.200Z", "type": "trading.balanceUpdated", "ok": true, "data": { "signature": "5eykt4UsFv8P8NJdTREpY1vzqKqZKvdpKuc147dw2N9d4...", "commitment": "processed", "chain": "SOLANA", "address": "29wLeR2adFT4awKwzUtYJyRraxoNf9VzJhQpE2Czx1Y8", "balance": "950000000", "tokenBalances": [ { "mint": "J7BLM8GpZd7vGjR9zBYZQQVeRoVc4dNfcfLpyCEEpump", "decimals": 6, "uiAmount": "1500000" } ] } } ``` `commitment` values: `"processed"` (faster, ~400ms) or `"confirmed"` (safer, ~1–2s). The same trade emits two events, one per commitment level. Receiving either means the trade landed on-chain. ### Minimal JavaScript Example ```javascript import { io } from "socket.io-client"; const socket = io("wss://solana.frontrun.pro", { path: "/trading/socket.io", transports: ["websocket"], }); let pendingSignature = null; socket.on("trading.connected", () => { socket.emit("trading.batchSubscribeWallets", { wallets: [{ chain: "SOLANA", address: WALLET_ADDRESS }], }); }); socket.on("trading.batchSubscribed", async () => { const res = await fetch("https://solana.frontrun.pro/api/v1/trading-api/trade", { method: "POST", headers: { Authorization: `Bearer ${API_KEY}`, "Content-Type": "application/json" }, body: JSON.stringify({ tokenMint: TOKEN_MINT, side: "BUY", uiInputAmount: "0.01", slippageBasisPoint: 500 }), }); const { data } = await res.json(); pendingSignature = data.signature; }); socket.on("trading.balanceUpdated", ({ data }) => { if (data.signature !== pendingSignature) return; console.log(`confirmed [${data.commitment}]`, data.tokenBalances); if (data.commitment === "confirmed") socket.disconnect(); }); ``` --- ## Key Constraints Summary - API key format: `fr_sk_` + 64 chars; max 12 per account - `uiInputAmount`: positive decimal string; BUY = SOL spent; SELL = token amount - `sellPercent`: SELL only; float 0.01–100; overrides `uiInputAmount` - `slippageBasisPoint`: integer 0–10000 - Per-wallet trade lock: 30 seconds; use separate wallets for concurrent strategies - SYNC timeout: 10 seconds; 504 ≠ trade failed - WebSocket max subscriptions: 50 wallets per client connection - `balance` in REST responses is in lamports (÷ 1,000,000,000 = SOL)