Skip to main content

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, referral data and points are all per chain. Hostnames are placeholders until DNS is assigned.

ChainChain IDREST base URLWebSocket
Avalanche C-Chain43114https://api-avax.<sparky-domain>wss://api-avax.<sparky-domain>/ws
Arbitrum One42161https://api-arb.<sparky-domain>wss://api-arb.<sparky-domain>/ws
BNB Chain56https://api-bnb.<sparky-domain>wss://api-bnb.<sparky-domain>/ws
Avalanche Fuji (testnet)43113https://api-fuji.<sparky-domain>wss://api-fuji.<sparky-domain>/ws
Arbitrum Sepolia (testnet)421614https://api-arb-sepolia.<sparky-domain>wss://api-arb-sepolia.<sparky-domain>/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); timestamp and signature stay 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

SurfaceScheme
/api/v1/*Authorization: Bearer <JWT> from POST /api/v1/auth/login (EIP-712 login)
/fapi/v1/*, /fapi/v2/* signed endpointsX-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:

FieldPurpose
api_key64 hex chars; send in X-MBX-APIKEY
secret_key64 hex chars; returned once at creation, never sent to the server; used only for local HMAC
ip_whitelistoptional, comma-separated; matched against the first X-Forwarded-For hop
permissionstrading,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:

  1. Collect every query parameter except signature, URL-encode them and join with &payload.
  2. POST / PUT only: append the raw request body string (the exact JSON bytes you will send, not URL-encoded) to payload.
  3. signature = hex(HMAC_SHA256(secret_key, payload)).
  4. Send ?<query>&signature=<hex> with X-MBX-APIKEY.
GET: payload = "symbol=BTCUSDT&timestamp=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.codeHTTPCause
INVALID_API_KEY401Key not found on this deployment
API_KEY_DISABLED401Key status != active
IP_NOT_ALLOWED403Client IP not in ip_whitelist
SIGNATURE_INVALID401HMAC mismatch, missing timestamp, or timestamp outside the window

Time synchronization

  • timestamp is Unix milliseconds.
  • The server enforces |now − timestamp| ≤ 60 000 msbidirectional: timestamps in the future are rejected too (Binance historically only checked the past side).
  • recvWindow is accepted but ignored: the 60 s window is fixed server-side and cannot be widened or narrowed.
  • Check drift with GET /fapi/v1/time at startup and periodically; apply serverTime − localTime as 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):

BucketScopeWindowCeilingHeader
Request weightpublic endpoints, per client IP1 min6000X-MBX-USED-WEIGHT-1M
Request weightsigned endpoints, per API key1 minstandard 1200 / ext-mm 2400 / house 6000X-MBX-USED-WEIGHT-1M
Order countPOST/PUT /fapi/v1/order, POST/PUT /fapi/v1/batchOrders, POST /fapi/v1/algoOrder, per API key10 sstandard 50 / ext-mm 100 / house 200X-MBX-ORDER-COUNT-10S
Order countsame routes, per API key1 minstandard 200 / ext-mm 600 / house 1200X-MBX-ORDER-COUNT-1M
  • The tier is the key's rate_tier (standard unless Sparky operations changed it; visible on GET /api/v1/api-keys).
  • Request weights: exchangeInfo 10; depth, klines, ticker/24hr, fundingRate, futures/data/*, allOrders, userTrades, positionRisk, balance, fundingFeeHistory, batchOrders 5; forceOrders 20; income 30; every other route 1. Each page lists its weight.
  • Cancels (DELETE) never count against the order buckets. batchOrders counts 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/exchangeInfo advertises the standard tier's ceilings (REQUEST_WEIGHT 1m, ORDERS 1m, ORDERS 10s); keys on a higher tier learn their real headroom from the headers.
  • Over the limit → HTTP 429 with {"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 to 418, keeps no IP-ban state, and sends no Retry-After header. Pace yourself on X-MBX-USED-WEIGHT-1M rather than waiting for the 429.

Symbols

symbol is case-insensitive and is normalized to Binance form. The following inputs all resolve to BTCUSDT:

InputNormalized
btcusdt, BTCUSDTBTCUSDT
BTC-USD, BTC-USDTBTCUSDT
BTC/USDT, BTC_USDTBTCUSDT

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" }
HTTPMeaningTypical codes
400Parameter / business validation-1013, -1100, -1102, -1106, -1120, -1121, -1130, -2010, -2011, -2014, -2019, -2021, -2022, -4028, -4046, -4059
401Auth failureINVALID_API_KEY, API_KEY_DISABLED, SIGNATURE_INVALID (Sparky envelope)
403IP not allowedIP_NOT_ALLOWED (Sparky envelope)
404Order / resource missing-2013, -1125
500Server error-1000, -1001

Full table at Error Codes.