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

ChainChain IDREST base URLWebSocket
Avalanche C-Chain43114https://api-avax.sparkydex.appwss://api-avax.sparkydex.app/ws
Arbitrum One42161https://api-arb.sparkydex.appwss://api-arb.sparkydex.app/ws
BNB Chain56https://api-bnb.sparkydex.appwss://api-bnb.sparkydex.app/ws
Avalanche Fuji (testnet)43113https://api-fuji.sparkydex.appwss://api-fuji.sparkydex.app/ws
Arbitrum Sepolia (testnet)421614https://api-arb-sepolia.sparkydex.appwss://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); 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 ms — bidirectional: 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.