Integrate Bolt.new with Binance API for crypto market data and trading. Public endpoints (prices, order books, candlestick charts) require no authentication and work in Bolt's WebContainer. Private endpoints (trading, account balances) need API keys with HMAC-SHA256 signature and must be called from server-side API routes. WebSocket streaming for real-time prices also works in the WebContainer. Always use Binance Testnet before live trading.
Build a Crypto Trading Dashboard with Binance API in Bolt.new
Binance is the world's largest cryptocurrency exchange by trading volume, and its API provides comprehensive market data and trading capabilities. For Bolt developers, the API divides cleanly into two categories that have very different integration requirements. The public tier — market data including ticker prices, 24-hour statistics, candlestick (OHLCV) data, and order books — requires no authentication whatsoever. Any fetch() call to Binance's public endpoints returns live market data, and these calls work directly in Bolt's WebContainer during development.
The private tier — account balances, order placement, trade history, and portfolio data — requires authentication with an API key and a digitally signed request. The signature uses HMAC-SHA256 with your secret key, applied to the query parameters and timestamp. Because the secret key must never be exposed in client-side code, all private API calls must route through Next.js API routes where the key stays server-side. These API routes work in the deployed environment and can also be called from the WebContainer for testing with the Testnet.
Binance provides a Test Network (Testnet) at `https://testnet.binance.vision` that mirrors the production API exactly but uses simulated funds. Always build and test trading functionality against the Testnet first. Getting Testnet credentials is free: go to testnet.binance.vision, log in with GitHub, and generate API keys. Replace the base URL and keys to switch to production. Testnet API calls work in Bolt's WebContainer via your API routes, so you can test the full trading flow before deploying.
Integration method
Binance has two API tiers. Public market data endpoints (prices, candlesticks, order books) need no authentication and can be called directly from React components or API routes — both work in Bolt's WebContainer. Private endpoints for trading and account data require HMAC-SHA256 signed requests with API keys and must go through Next.js API routes to keep the secret key server-side. WebSocket connections for real-time price streaming work in the WebContainer. Always test with Binance Testnet before connecting to production.
Prerequisites
- A Bolt.new account with a Next.js project
- For market data only: no credentials required — Binance public endpoints are free
- For trading and account data: a Binance Testnet account at testnet.binance.vision (recommended for development)
- For production: a Binance account with API key and secret key at binance.com/en/my/settings/api-management
- Understanding that API keys with trading permissions must be stored server-side only
Step-by-step guide
Fetch Public Market Data (No API Key Required)
Fetch Public Market Data (No API Key Required)
Binance's public REST API endpoints require no authentication. The market data tier includes current prices, 24-hour ticker statistics, order books, recent trades, and candlestick data. These endpoints have generous rate limits (typically 6000 weight per minute) and work directly in Bolt's WebContainer. The primary public endpoints are: - `GET /api/v3/ticker/24hr?symbol=BTCUSDT` — 24-hour rolling window stats for one symbol (or omit symbol for all) - `GET /api/v3/ticker/price?symbol=BTCUSDT` — just the current price, lightest endpoint - `GET /api/v3/klines?symbol=BTCUSDT&interval=1h&limit=100` — candlestick/OHLCV data - `GET /api/v3/depth?symbol=BTCUSDT&limit=20` — order book depth - `GET /api/v3/trades?symbol=BTCUSDT&limit=50` — recent trades Binance rate limiting uses a weight system. Each endpoint has a weight value (e.g., price ticker = 2 weight, 24hr ticker for all symbols = 80 weight, klines = 2 weight). Your limit is 6000 weight per minute on a rolling basis. The response headers include `X-MBX-USED-WEIGHT-1M` showing current usage. For a price dashboard polling several symbols every 30 seconds, you will use only a fraction of the limit. For fetching multiple symbols efficiently, use the array parameter format: `GET /api/v3/ticker/24hr?symbols=["BTCUSDT","ETHUSDT","BNBUSDT"]`. This returns all requested symbols in one response with a single weight hit rather than separate calls per symbol. All price values return as strings in Binance's API (e.g., price: "67432.50000000"). Parse them to float before displaying or calculating. Volume values can be very large — format them appropriately (billions of dollars in volume for BTC).
Create a lib/binance.ts file that exports a binancePublicFetch helper for unauthenticated Binance API calls. Use BINANCE_BASE_URL from process.env (defaulting to https://api.binance.com) for easy switching between testnet and production. Also export a parseBinanceTicker helper that converts the raw ticker response strings to typed numbers. Create a Next.js API route at app/api/binance/prices/route.ts that fetches 24hr stats for BTCUSDT, ETHUSDT, BNBUSDT, SOLUSDT, ADAUSDT using the array symbols param and returns formatted price objects.
Paste this in Bolt.new chat
1// lib/binance.ts2export const BINANCE_BASE_URL =3 process.env.BINANCE_BASE_URL || 'https://api.binance.com';45export const BINANCE_WS_URL =6 process.env.BINANCE_WS_URL || 'wss://stream.binance.com:9443';78// For Testnet, use:9// BINANCE_BASE_URL=https://testnet.binance.vision10// BINANCE_WS_URL=wss://testnet.binance.vision/ws1112export async function binancePublicFetch<T = unknown>(13 endpoint: string,14 params?: Record<string, string>15): Promise<T> {16 const url = new URL(`${BINANCE_BASE_URL}${endpoint}`);17 if (params) {18 Object.entries(params).forEach(([key, value]) => {19 url.searchParams.set(key, value);20 });21 }2223 const response = await fetch(url.toString());24 if (!response.ok) {25 const error = await response.json().catch(() => ({})) as { msg?: string; code?: number };26 throw new Error(error.msg || `Binance API error ${response.status}`);27 }2829 return response.json() as Promise<T>;30}3132export interface BinanceTicker {33 symbol: string;34 price: number;35 priceChange: number;36 priceChangePercent: number;37 volume: number;38 quoteVolume: number;39 high: number;40 low: number;41 count: number;42}4344export function parseTicker(raw: Record<string, string>): BinanceTicker {45 return {46 symbol: raw.symbol,47 price: parseFloat(raw.lastPrice || raw.price),48 priceChange: parseFloat(raw.priceChange || '0'),49 priceChangePercent: parseFloat(raw.priceChangePercent || '0'),50 volume: parseFloat(raw.volume || '0'),51 quoteVolume: parseFloat(raw.quoteVolume || '0'),52 high: parseFloat(raw.highPrice || '0'),53 low: parseFloat(raw.lowPrice || '0'),54 count: parseInt(raw.count || '0', 10),55 };56}Pro tip: Set BINANCE_BASE_URL=https://testnet.binance.vision in .env.development and BINANCE_BASE_URL=https://api.binance.com in production. This single environment variable toggles the entire integration between testnet and production, making it safe to develop without any risk to real funds.
Expected result: The public prices API route returns live BTC, ETH, BNB, SOL, and ADA price data. You can verify the data by comparing prices to the Binance app. The response shows in Bolt's preview without any API key.
Build the Market Dashboard with Candlestick Charts
Build the Market Dashboard with Candlestick Charts
With public market data flowing, build the dashboard components. The candlestick chart is the most technically complex part — it requires transforming Binance's klines array format into chart-library-friendly data and rendering interactive OHLCV candles. Binance klines return as arrays: each candle is `[openTime, open, high, low, close, volume, closeTime, quoteVolume, tradeCount, takerBuyBaseVolume, takerBuyQuoteVolume, ignore]`. Index 0 is the open timestamp in milliseconds, indices 1-5 are the price and volume data as strings. Transform these arrays into objects before passing to a chart library. For the React chart component, Recharts works well with Bolt-generated projects. However, Recharts does not have a native candlestick chart type — you would need to build one using the ComposedChart with custom Bar shapes. Alternatively, use a financial charting library like lightweight-charts (TradingView's open source library) which has native candlestick support. Install with `npm install lightweight-charts`. This library is pure JavaScript with no native bindings and works in the WebContainer. For the price ticker component, consider using Binance's WebSocket streaming for real-time updates instead of polling. The WebSocket stream at `wss://stream.binance.com:9443/ws/btcusdt@ticker` delivers price updates every second. In a React component, open the WebSocket in useEffect and update state on each message. Close the WebSocket in the cleanup function to prevent memory leaks. WebSocket connections work in Bolt's WebContainer — they use the browser's native WebSocket API, not Node.js's net module. The 24hr ticker streams deliver: symbol, close price (current), price change, percent change, best bid, best ask, open price, high, low, volume, and trade count — everything needed for a comprehensive market overview widget.
Build a crypto market dashboard page at /market. Create /api/binance/klines route that fetches kline data for a symbol and interval from Binance and transforms the array format to { time, open, high, low, close, volume } objects. Build a React MarketDashboard with: (1) a price header showing BTC price with 24h change; (2) a coin selector row for BTC, ETH, BNB, SOL; (3) a lightweight-charts CandlestickChart component showing the last 100 candles; (4) a volume bar chart below the candlestick. Add interval buttons (1H, 4H, 1D). Fetch new klines data when symbol or interval changes.
Paste this in Bolt.new chat
1// app/api/binance/klines/route.ts2import { NextResponse } from 'next/server';3import { binancePublicFetch } from '@/lib/binance';45type RawKline = [6 number, // openTime7 string, // open8 string, // high9 string, // low10 string, // close11 string, // volume12 number, // closeTime13 string, // quoteVolume14 number, // tradeCount15 string, // takerBuyBaseVolume16 string, // takerBuyQuoteVolume17 string // ignore18];1920export async function GET(request: Request) {21 const { searchParams } = new URL(request.url);22 const symbol = (searchParams.get('symbol') || 'BTCUSDT').toUpperCase();23 const interval = searchParams.get('interval') || '1d';24 const limit = searchParams.get('limit') || '100';2526 // Validate interval to prevent injection27 const validIntervals = ['1m','3m','5m','15m','30m','1h','2h','4h','6h','8h','12h','1d','3d','1w','1M'];28 if (!validIntervals.includes(interval)) {29 return NextResponse.json({ error: 'Invalid interval' }, { status: 400 });30 }3132 try {33 const klines = await binancePublicFetch<RawKline[]>('/api/v3/klines', {34 symbol,35 interval,36 limit,37 });3839 const candles = klines.map((k) => ({40 time: Math.floor(k[0] / 1000), // Convert ms to seconds for lightweight-charts41 open: parseFloat(k[1]),42 high: parseFloat(k[2]),43 low: parseFloat(k[3]),44 close: parseFloat(k[4]),45 volume: parseFloat(k[5]),46 }));4748 return NextResponse.json({ symbol, interval, candles });49 } catch (err) {50 const message = err instanceof Error ? err.message : 'Failed to fetch klines';51 return NextResponse.json({ error: message }, { status: 500 });52 }53}Pro tip: lightweight-charts from TradingView expects timestamps in seconds (Unix timestamp), not milliseconds. Binance returns timestamps in milliseconds. Divide by 1000 when transforming klines. Also note that lightweight-charts requires time values to be in ascending order — Binance klines already come in ascending order by default.
Expected result: The market dashboard shows a live candlestick chart for BTCUSDT. The interval buttons switch between hourly, 4-hour, and daily candles. The chart updates when switching between BTC, ETH, BNB, and SOL.
Set Up Private API Authentication with HMAC Signatures
Set Up Private API Authentication with HMAC Signatures
Private Binance API endpoints (account balance, order placement, trade history) require signed requests. The signature uses HMAC-SHA256 applied to the query string including a timestamp parameter. The API key is sent in the `X-MBX-APIKEY` header; the signature is added as a query parameter. The signing process: (1) Build the query string with all parameters including a `timestamp` of the current time in milliseconds. (2) Compute HMAC-SHA256 of the full query string using your secret key as the HMAC key. (3) Append `&signature={hex_signature}` to the query string. The timestamp must be within 5000 milliseconds of Binance's server time to prevent replay attacks — use `Date.now()` which is accurate enough for most environments. In Node.js (your API route), compute the HMAC using the built-in `crypto` module: `crypto.createHmac('sha256', secretKey).update(queryString).digest('hex')`. This is a standard Node.js module, no installation required. Get your API keys from Binance Account → API Management (for production) or testnet.binance.vision (for Testnet). When creating a production API key, configure IP restrictions to your Netlify or Bolt Cloud function's IP range for security. Enable only the permissions your application needs: 'Enable Reading' for account data, 'Enable Spot & Margin Trading' for order placement. Never enable withdrawal permissions for application API keys. Store both keys server-side. BINANCE_API_KEY goes in the request header — it is not technically secret (it is visible in network requests) but should still be kept server-side. BINANCE_SECRET_KEY is the actual secret and must never appear in any client-side code.
Add HMAC signing to lib/binance.ts. Export a binancePrivateFetch function that accepts an endpoint, HTTP method, and params object. It should: (1) get BINANCE_API_KEY and BINANCE_SECRET_KEY from process.env; (2) add timestamp: Date.now() to params; (3) build the query string from params; (4) compute HMAC-SHA256 of the query string using crypto.createHmac; (5) append signature to the query string; (6) make the request with X-MBX-APIKEY header. Create /api/binance/account route that calls /api/v3/account and returns non-zero balances.
Paste this in Bolt.new chat
1// Addition to lib/binance.ts — private signed requests2import crypto from 'crypto';34export async function binancePrivateFetch<T = unknown>(5 endpoint: string,6 method: 'GET' | 'POST' | 'DELETE' = 'GET',7 params: Record<string, string | number | boolean> = {}8): Promise<T> {9 const apiKey = process.env.BINANCE_API_KEY;10 const secretKey = process.env.BINANCE_SECRET_KEY;11 const baseUrl = process.env.BINANCE_BASE_URL || 'https://api.binance.com';1213 if (!apiKey || !secretKey) {14 throw new Error('BINANCE_API_KEY and BINANCE_SECRET_KEY must be configured in .env. Use Testnet keys from testnet.binance.vision for development.');15 }1617 // Add required timestamp18 const allParams = { ...params, timestamp: Date.now() };1920 // Build query string21 const queryString = Object.entries(allParams)22 .map(([key, value]) => `${key}=${encodeURIComponent(String(value))}`)23 .join('&');2425 // Compute HMAC-SHA256 signature26 const signature = crypto27 .createHmac('sha256', secretKey)28 .update(queryString)29 .digest('hex');3031 const signedQuery = `${queryString}&signature=${signature}`;32 const url = `${baseUrl}${endpoint}?${signedQuery}`;3334 const response = await fetch(url, {35 method,36 headers: {37 'X-MBX-APIKEY': apiKey,38 'Content-Type': 'application/json',39 },40 });4142 if (!response.ok) {43 const error = await response.json().catch(() => ({})) as { msg?: string; code?: number };44 throw new Error(error.msg || `Binance API error ${response.status} (code: ${error.code})`);45 }4647 return response.json() as Promise<T>;48}Pro tip: Always start with Binance Testnet credentials (from testnet.binance.vision) before connecting production API keys. The Testnet environment is identical to production for API calls but uses simulated funds. Set BINANCE_BASE_URL=https://testnet.binance.vision in .env to use Testnet.
Expected result: The private API helper generates correct HMAC signatures. Calling /api/binance/account returns real account balance data from Binance (or simulated balances from the Testnet). The signature verification succeeds.
Build Order Placement and Deploy
Build Order Placement and Deploy
With the signing infrastructure in place, build the order placement route. Binance order creation uses `POST /api/v3/order` with parameters: `symbol` (trading pair), `side` (BUY or SELL), `type` (MARKET, LIMIT, STOP_LOSS_LIMIT, etc.), `quantity`, and for LIMIT orders, `price` and `timeInForce` (GTC, IOC, FOK). For a MARKET order, you specify `quantity` (the amount of the base asset to buy/sell). The order fills immediately at the best available price. Market orders are simpler to implement but offer no price control. For a LIMIT order, you specify both `quantity` and `price`. The order rests in the order book until the market price reaches your limit price. Include `timeInForce=GTC` (Good Till Canceled) for orders that should remain until filled or manually canceled. Safety measures to implement in the order route: (1) Validate all parameters server-side before sending to Binance — never trust client-provided values. (2) Implement minimum notional value checks (most pairs have a $10 minimum order). (3) Add a testMode flag that calls `POST /api/v3/order/test` instead of the real endpoint — this validates the order without placing it. (4) Log all orders with timestamps for auditing. For development in Bolt's WebContainer, all private API calls to the Testnet work through your API routes since they are outbound HTTPS calls. You can test the full order flow — checking balances, placing test orders, seeing them in your Testnet account — without deploying. Switch to production credentials only after thorough Testnet testing. When deploying to Netlify or Bolt Cloud for production, add IP restrictions on your Binance API key to the hosting provider's outbound IP range for an additional layer of security.
Create a Next.js API route at app/api/binance/order/route.ts. Handle POST to place an order: validate required params (symbol, side, type, quantity), call POST /api/v3/order/test first to validate the order, then call POST /api/v3/order to place it. Handle GET to fetch open orders for a symbol. Add request validation that checks quantity is positive and symbol matches expected format (all uppercase, ends in USDT/BTC/ETH). Return { orderId, status, executedQty, price } on success. Include error mapping for common Binance error codes (code -2010 insufficient funds, -1121 invalid symbol).
Paste this in Bolt.new chat
1// app/api/binance/order/route.ts2import { NextResponse } from 'next/server';3import { binancePrivateFetch } from '@/lib/binance-private';45const BINANCE_ERROR_MESSAGES: Record<number, string> = {6 '-2010': 'Insufficient balance to place this order',7 '-1121': 'Invalid trading symbol — check the symbol format (e.g., BTCUSDT)',8 '-1013': 'Order quantity below minimum lot size for this trading pair',9 '-1100': 'Invalid parameter value — check quantity and price format',10 '-2011': 'Order does not exist or already canceled',11};1213interface BinanceOrderResult {14 orderId: number;15 symbol: string;16 status: string;17 executedQty: string;18 cummulativeQuoteQty: string;19 price: string;20 side: string;21 type: string;22}2324export async function POST(request: Request) {25 const { symbol, side, type, quantity, price, timeInForce, testMode = true } = await request.json();2627 // Validate required fields28 if (!symbol || !side || !type || !quantity) {29 return NextResponse.json({ error: 'symbol, side, type, and quantity are required' }, { status: 400 });30 }3132 // Basic validation33 if (!['BUY', 'SELL'].includes(side.toUpperCase())) {34 return NextResponse.json({ error: 'side must be BUY or SELL' }, { status: 400 });35 }3637 if (parseFloat(quantity) <= 0) {38 return NextResponse.json({ error: 'quantity must be positive' }, { status: 400 });39 }4041 const orderParams: Record<string, string | number> = {42 symbol: symbol.toUpperCase(),43 side: side.toUpperCase(),44 type: type.toUpperCase(),45 quantity: String(quantity),46 };4748 if (type.toUpperCase() === 'LIMIT') {49 if (!price) return NextResponse.json({ error: 'price required for LIMIT orders' }, { status: 400 });50 orderParams.price = String(price);51 orderParams.timeInForce = timeInForce || 'GTC';52 }5354 try {55 // Always test order first56 await binancePrivateFetch('/api/v3/order/test', 'POST', orderParams);5758 if (testMode) {59 return NextResponse.json({60 testMode: true,61 message: 'Order validation passed. Set testMode: false to place the real order.',62 params: orderParams,63 });64 }6566 const result = await binancePrivateFetch<BinanceOrderResult>(67 '/api/v3/order',68 'POST',69 orderParams70 );7172 return NextResponse.json({73 orderId: result.orderId,74 symbol: result.symbol,75 status: result.status,76 executedQty: parseFloat(result.executedQty),77 price: parseFloat(result.price) || null,78 side: result.side,79 type: result.type,80 });81 } catch (err) {82 const message = err instanceof Error ? err.message : 'Order placement failed';83 // Extract Binance error code from message84 const codeMatch = message.match(/code: (-?\d+)/);85 const friendlyMessage = codeMatch86 ? BINANCE_ERROR_MESSAGES[parseInt(codeMatch[1])] || message87 : message;88 return NextResponse.json({ error: friendlyMessage }, { status: 400 });89 }90}Pro tip: Default testMode to true in your order placement UI and require explicit user confirmation to switch to live trading. This prevents accidental order placement during development and testing. Add a prominent 'Live Trading' toggle that must be switched on and confirmed before real orders can execute.
Expected result: The order placement route validates orders and places them on the Testnet successfully. Test orders appear in your Binance Testnet account. The testMode flag prevents real orders during development.
Common use cases
Real-Time Cryptocurrency Price Dashboard
Build a live price ticker displaying BTC, ETH, BNB, SOL, and other cryptocurrencies with real-time price updates, 24-hour volume, price change percentage, and market cap. Use Binance's public WebSocket streams for sub-second price updates. No API key required — suitable for any public-facing application.
Build a real-time crypto price dashboard using Binance API. Create a Next.js API route at /api/binance/prices that fetches 24hr ticker statistics for BTC, ETH, BNB, SOL, and ADA from Binance's public endpoint https://api.binance.com/api/v3/ticker/24hr?symbols=[...]. Return symbol, price, priceChangePercent, volume, high, low for each. Also create a React component that opens a WebSocket to wss://stream.binance.com:9443/stream?streams=btcusdt@ticker/ethusdt@ticker for real-time updates, updating prices every second. Show a color-coded price grid with green for positive change and red for negative.
Copy this prompt to try it in Bolt.new
Candlestick Chart with Historical Data
Display OHLCV candlestick charts for any trading pair across multiple timeframes (1m, 5m, 1h, 4h, 1d). Binance klines data is free and unauthenticated. Build interactive charts with volume bars and basic technical indicators. Perfect for building trading tools, portfolio analyzers, or price history components.
Build a candlestick chart component using Binance klines API. Create a Next.js API route at /api/binance/klines that fetches OHLCV data from https://api.binance.com/api/v3/klines?symbol={symbol}&interval={interval}&limit=100. Return the data in recharts-friendly format with open, high, low, close, volume, and timestamp. Build a React chart component using Recharts ComposedChart with CandlestickChart and BarChart for volume. Add interval selector buttons (1H, 4H, 1D, 1W) and a symbol input. Default to BTCUSDT on 1D interval.
Copy this prompt to try it in Bolt.new
Authenticated Portfolio Tracker and Order Manager
Build a personal portfolio tracker that shows your Binance account balances, open orders, and recent trades. Add an order placement form for market and limit orders. All account and order operations require HMAC-signed API requests through a server-side API route to keep your secret key protected. Test with Binance Testnet before connecting production credentials.
Build a Binance portfolio tracker with order management. Create a Next.js API route at /api/binance/account that calls Binance's GET /api/v3/account endpoint with HMAC-SHA256 signature using BINANCE_API_KEY and BINANCE_SECRET_KEY from process.env and the current timestamp. Return non-zero balances with current USD value (by fetching prices for each coin). Add /api/binance/order for placing and canceling orders with signature. Build a React portfolio page with balance cards and a simple order form. Start with BINANCE_BASE_URL pointing to https://testnet.binance.vision for safety.
Copy this prompt to try it in Bolt.new
Troubleshooting
Timestamp for this request is outside of the recvWindow — error code -1021
Cause: The local machine's clock is more than 5000ms ahead of or behind Binance's server time. This is common in development environments and VMs with unsynchronized clocks.
Solution: Fetch Binance's server time and use it instead of Date.now(). Call GET /api/v3/time to get the server timestamp, then use that value in subsequent requests. Alternatively, increase recvWindow parameter to 10000 (10 seconds) to give more clock tolerance.
1// Sync with Binance server time:2const timeResponse = await binancePublicFetch<{ serverTime: number }>('/api/v3/time');3const serverTime = timeResponse.serverTime;4// Use serverTime instead of Date.now() for the timestamp parameterSignature for this request is not valid — error code -1022
Cause: The HMAC-SHA256 signature computation is incorrect. Common causes: the query string parameters are not in the exact same order when signing vs when sending, special characters are not encoded consistently, or the secret key has extra whitespace.
Solution: Ensure the query string used for signing is identical to the query string sent in the URL. Do not sort or reorder parameters after signing. Trim the BINANCE_SECRET_KEY value to remove any accidental whitespace in the .env file.
1// Verify signature generation matches exactly:2const queryString = Object.entries(allParams)3 .map(([key, value]) => `${key}=${encodeURIComponent(String(value))}`)4 .join('&');5// Sign EXACTLY this string:6const signature = crypto.createHmac('sha256', secretKey.trim()) // .trim() removes whitespace7 .update(queryString)8 .digest('hex');CORS error when calling Binance API directly from a React component
Cause: Binance's API does not allow direct browser (CORS) requests from non-Binance origins. All Binance API calls must go through a server-side route.
Solution: Route all Binance API calls through Next.js API routes. Public market data calls are the exception — Binance's public endpoints do support CORS for browser requests. But for consistency and to keep keys server-side, always use API routes.
1// Wrong — calling private Binance API from React component:2const account = await fetch('https://api.binance.com/api/v3/account?...'); // CORS error34// Correct — call your API route which calls Binance server-side:5const account = await fetch('/api/binance/account');WebSocket connection to Binance stream disconnects after 24 hours
Cause: Binance WebSocket streams have a 24-hour maximum connection lifetime. After 24 hours, the server sends a close frame and the connection terminates.
Solution: Implement automatic WebSocket reconnection in your React component. When the onclose event fires, check if the closure was intentional (component unmounting) or unexpected (timeout), and reconnect after a short delay if unexpected.
1// Auto-reconnect pattern for Binance WebSocket:2useEffect(() => {3 let ws: WebSocket;4 let reconnectTimeout: ReturnType<typeof setTimeout>;5 let intentionalClose = false;6 const connect = () => {7 ws = new WebSocket(`${BINANCE_WS_URL}/ws/btcusdt@ticker`);8 ws.onmessage = (e) => setTicker(JSON.parse(e.data));9 ws.onclose = () => {10 if (!intentionalClose) reconnectTimeout = setTimeout(connect, 3000);11 };12 };13 connect();14 return () => { intentionalClose = true; ws?.close(); clearTimeout(reconnectTimeout); };15}, []);Best practices
- Always test with Binance Testnet credentials before connecting production API keys — use testnet.binance.vision and set BINANCE_BASE_URL accordingly.
- Store BINANCE_API_KEY and BINANCE_SECRET_KEY as server-side environment variables without NEXT_PUBLIC_ prefix — the secret key signs all requests and must never appear in client bundles.
- Implement IP restrictions on production Binance API keys to your hosting provider's outbound IP range — this prevents unauthorized use if the key is somehow compromised.
- Default order placement to testMode: true and require explicit user action to enable real trading — this prevents accidental order execution during development and testing.
- Cache public market data (prices, 24hr stats) for 10-30 seconds in your API routes to reduce weight consumption and improve dashboard load times for concurrent users.
- Use WebSocket streams for real-time price updates in the UI instead of polling REST endpoints — WebSockets are more efficient and provide sub-second updates without rate limit concerns.
- Add BINANCE_ERROR_MESSAGES mapping in your API routes to convert Binance's numeric error codes into user-friendly messages — code -2010 is 'insufficient balance', not 'Account has insufficient balance for requested action.'
- Validate all order parameters server-side before sending to Binance — minimum quantity (LOT_SIZE filter), minimum notional value, and valid symbol format should all be checked in your API route.
Alternatives
Coinbase Advanced Trade API is better for US-based applications and offers Coinbase Commerce for crypto merchant payments alongside trading, while Binance has greater global market liquidity.
Robinhood API covers both crypto and stock trading in a single platform and is US-focused, while Binance is global and crypto-only with significantly more trading pairs.
Plaid connects to traditional banking and brokerage accounts rather than crypto exchanges, making it complementary to Binance for apps that handle both fiat and crypto portfolios.
Stripe handles fiat currency payments for standard e-commerce, while Binance API is the right choice specifically for crypto-native applications and trading features.
Frequently asked questions
Can I access Binance market data without any API key in Bolt?
Yes — Binance's public market data endpoints (ticker prices, 24hr stats, klines, order books) require no authentication. You can call them from API routes in Bolt's WebContainer during development and return the data to React components without any configuration. Public WebSocket streams also work without authentication.
Does Bolt.new have a native Binance integration?
No — Bolt.new does not have a native Binance connector. The integration requires building Next.js API routes that handle HMAC-SHA256 request signing for private endpoints. Bolt's AI can generate the signing logic and API routes from a prompt, making setup manageable even without a native connector.
Are WebSocket price streams compatible with Bolt's WebContainer?
Yes — WebSocket connections use the browser's native WebSocket API, which is fully supported in Bolt's WebContainer. You can connect to Binance's streaming WebSocket endpoints (stream.binance.com) from React components and receive real-time price updates in the preview. This is different from the TCP-based database drivers that fail in the WebContainer.
How do I switch between Binance Testnet and production?
Set BINANCE_BASE_URL=https://testnet.binance.vision in your .env for Testnet and BINANCE_BASE_URL=https://api.binance.com for production. The Testnet has a different API key — generate Testnet keys at testnet.binance.vision (sign in with GitHub). Swap both the base URL and API keys to switch environments.
Why must Binance private API calls go through a Next.js API route rather than directly from React?
Two reasons: security and CORS. The BINANCE_SECRET_KEY is used to sign every request — exposing it in client-side code means anyone can execute trades as you. Additionally, Binance's private endpoints do not allow CORS requests from browser origins, so direct fetch() calls from React components would fail. All signing and private API calls must happen server-side in API routes.
Talk to an Expert
Our team has built 600+ apps. Get personalized help with your project.
Book a free consultation