Basic Information
This page collects the operational facts that apply to every Sparky Perpetual Contracts endpoint.
Base URLs
Sparky runs one backend per chain. Each deployment has its own database: balances, positions, orders, API keys and referral data are all per chain. Hostnames are placeholders until DNS is assigned.
| Chain | Chain ID | REST base URL | WebSocket |
|---|---|---|---|
| Avalanche C-Chain | 43114 | https://api-avax.sparkydex.app | wss://api-avax.sparkydex.app/ws |
| Arbitrum One | 42161 | https://api-arb.sparkydex.app | wss://api-arb.sparkydex.app/ws |
| BNB Chain | 56 | https://api-bnb.sparkydex.app | wss://api-bnb.sparkydex.app/ws |
| Avalanche Fuji (testnet) | 43113 | https://api-fuji.sparkydex.app | wss://api-fuji.sparkydex.app/ws |
| Arbitrum Sepolia (testnet) | 421614 | https://api-arb-sepolia.sparkydex.app | wss://api-arb-sepolia.sparkydex.app/ws |
/fapi/v1/*, /fapi/v2/* and /futures/data/* are mounted at the root of the host, exactly like Binance. The native API is under /api/v1/*.
Content type
- FAPI GET / DELETE: parameters are URL-encoded in the query string.
- FAPI POST / PUT: business parameters are sent as a JSON body (
Content-Type: application/json);timestampandsignaturestay in the query string. This differs from Binance's "everything in the query string" default, but every mainstream SDK supports both. - Native
/api/v1/*: JSON bodies. - Responses:
application/json. Prices, quantities and other decimals are returned as strings; timestamps are int64 Unix milliseconds on FAPI (the native API mixes seconds and milliseconds — each page says which).
Versioning
Path-versioned. Sparky implements /fapi/v1/* plus /fapi/v2/balance and /fapi/v2/positionRisk. There is no /fapi/v1/balance (404) — SDKs that default to v2 are fine.
Authentication
| Surface | Scheme |
|---|---|
/api/v1/* | Authorization: Bearer <JWT> from POST /api/v1/auth/login (EIP-712 login) |
/fapi/v1/*, /fapi/v2/* signed endpoints | X-MBX-APIKEY header + timestamp + signature query parameters (HMAC-SHA256) |
Public market endpoints (/fapi/v1/ping, time, exchangeInfo, depth, klines, tickers, premiumIndex, fundingRate, fundingInfo, openInterest, /futures/data/*) | none |
API keys
Created with a JWT via POST /api/v1/api-keys (see API Keys). Each key has:
| Field | Purpose |
|---|---|
api_key | 64 hex chars; send in X-MBX-APIKEY |
secret_key | 64 hex chars; returned once at creation, never sent to the server; used only for local HMAC |
ip_whitelist | optional, comma-separated; matched against the first X-Forwarded-For hop |
permissions | trading,deposit — API keys can never withdraw |
Max 30 keys per account per deployment. Disable with PUT /api/v1/api-keys/{id} (status: "disabled") or delete with DELETE.
Signing
Identical to Binance Futures, with the POST/PUT body rule made explicit:
- Collect every query parameter except
signature, URL-encode them and join with&→payload. - POST / PUT only: append the raw request body string (the exact JSON bytes you will send, not URL-encoded) to
payload. signature = hex(HMAC_SHA256(secret_key, payload)).- Send
?<query>&signature=<hex>withX-MBX-APIKEY.
GET: payload = "symbol=BTCUSDT×tamp=1714261234567"
POST: payload = "timestamp=1714261234567" + '{"symbol":"BTCUSDT","side":"BUY","type":"LIMIT","quantity":"0.01","price":"60000"}'
Sparky's verifier is tolerant of encoding style: it first checks the signature against the query string as received (URL-encoded); if that fails it URL-decodes every (k, v), re-joins, and checks again. Both branches use the same secret, so this does not weaken HMAC — it just lets clients that signed a raw orderIdList=[uuid1,uuid2] and let their HTTP library percent-encode it still verify. New code should follow the Binance convention (sign the URL-encoded form).
Failures return HTTP 401 with Sparky's envelope, not a Binance code:
{ "success": false, "error": { "code": "SIGNATURE_INVALID", "message": "..." } }
error.code | HTTP | Cause |
|---|---|---|
INVALID_API_KEY | 401 | Key not found on this deployment |
API_KEY_DISABLED | 401 | Key status != active |
IP_NOT_ALLOWED | 403 | Client IP not in ip_whitelist |
SIGNATURE_INVALID | 401 | HMAC mismatch, missing timestamp, or timestamp outside the window |
Time synchronization
timestampis Unix milliseconds.- The server enforces
|now − timestamp| ≤ 60 000 ms— bidirectional: timestamps in the future are rejected too (Binance historically only checked the past side). recvWindowis accepted but ignored: the 60 s window is fixed server-side and cannot be widened or narrowed.- Check drift with
GET /fapi/v1/timeat startup and periodically; applyserverTime − localTimeas an offset.
Rate limits
Enforced on every /fapi/* and /futures/data/* route, using fixed windows aligned to the wall clock (so a client may burst up to 2× a ceiling across a window boundary, exactly as on Binance):
| Bucket | Scope | Window | Ceiling | Header |
|---|---|---|---|---|
| Request weight | public endpoints, per client IP | 1 min | 6000 | X-MBX-USED-WEIGHT-1M |
| Request weight | signed endpoints, per API key | 1 min | standard 1200 / ext-mm 2400 / house 6000 | X-MBX-USED-WEIGHT-1M |
| Order count | POST/PUT /fapi/v1/order, POST/PUT /fapi/v1/batchOrders, POST /fapi/v1/algoOrder, per API key | 10 s | standard 50 / ext-mm 100 / house 200 | X-MBX-ORDER-COUNT-10S |
| Order count | same routes, per API key | 1 min | standard 200 / ext-mm 600 / house 1200 | X-MBX-ORDER-COUNT-1M |
- The tier is the key's
rate_tier(standardunless Sparky operations changed it; visible onGET /api/v1/api-keys). - Request weights:
exchangeInfo10;depth,klines,ticker/24hr,fundingRate,futures/data/*,allOrders,userTrades,positionRisk,balance,fundingFeeHistory,batchOrders5;forceOrders20;income30; every other route 1. Each page lists its weight. - Cancels (
DELETE) never count against the order buckets.batchOrderscounts as one order per request. - Signed endpoints additionally pass a pre-authentication per-IP gate of 24000/min; it only stamps headers on its own
429. GET /fapi/v1/exchangeInfoadvertises thestandardtier's ceilings (REQUEST_WEIGHT1m,ORDERS1m,ORDERS10s); keys on a higher tier learn their real headroom from the headers.- Over the limit → HTTP
429with{"code":-1003,"msg":"Too many requests."}and the counters in the headers above. A rejected request does not consume quota (a rejected order has already spent its request weight, as on Binance). Sparky does not escalate to418, keeps no IP-ban state, and sends noRetry-Afterheader. Pace yourself onX-MBX-USED-WEIGHT-1Mrather than waiting for the429.
Symbols
symbol is case-insensitive and is normalized to Binance form. The following inputs all resolve to BTCUSDT:
| Input | Normalized |
|---|---|
btcusdt, BTCUSDT | BTCUSDT |
BTC-USD, BTC-USDT | BTCUSDT |
BTC/USDT, BTC_USDT | BTCUSDT |
Only BTCUSDT-style symbols are canonical (Binance would reject the aliases). A symbol that is not in the deployment's market_configs → -1121 Invalid symbol. The live list comes from GET /fapi/v1/exchangeInfo; see Symbol aliases for the native-API form.
Sharded routing
Deployments may run several pods with each symbol owned by one pod. Write requests that land on a non-owner pod are forwarded transparently — same query string, same signature, one fixed base URL for the client. Details in Sharded routing.
Error response format
Business and signing errors on FAPI use the Binance envelope:
{ "code": -1021, "msg": "Timestamp outside recv window" }
| HTTP | Meaning | Typical codes |
|---|---|---|
400 | Parameter / business validation | -1013, -1100, -1102, -1106, -1120, -1121, -1130, -2010, -2011, -2014, -2019, -2021, -2022, -4028, -4046, -4059 |
401 | Auth failure | INVALID_API_KEY, API_KEY_DISABLED, SIGNATURE_INVALID (Sparky envelope) |
403 | IP not allowed | IP_NOT_ALLOWED (Sparky envelope) |
404 | Order / resource missing | -2013, -1125 |
500 | Server error | -1000, -1001 |
Full table at Error Codes.