GaiaExGaiaEx
API

General Info & Authentication

Base URL, HMAC-SHA256 authentication, rate limits, HTTP status codes, and request formatting for the GaiaEx API.

AI AGENT READYEquip Claude Code, OpenAI Codex & Antigravity (`agy`) with Official GaiaEx Skills
CLAUDE CODESKILL.md
Claude SKILL.md

Drop-in Claude Code skill with HMAC-SHA256 signing, /fapi/v1 & /fapi/v2 schemas, and secret-redaction guardrails.

Raw ↗
OPENAI CODEXAGENTS.md
Codex AGENTS.md

Drop-in repository AGENTS.md for OpenAI Codex CLI — tick-size rules, rate-limit budgets, and self-custody boundaries.

Raw ↗
ANTIGRAVITYAGY.md
Antigravity AGY.md

Google Antigravity (agy) & Gemini agent spec with verified async Python WebSocket + REST execution templates.

Raw ↗
UNIVERSAL LLMllms.txt
/llms.txt Index

Single-URL machine-readable index of all GaiaEx documentation pages, OpenAPI 3.0 schema, and Postman collection.

Raw ↗

General API Information

  • The base endpoint is: https://openapi.gaiaex.com
  • GaiaEx implements the GaiaEx Unified Futures standard under /fapi/v1/* and /fapi/v2/*.
  • All endpoints return either a JSON object or array.
  • All time and timestamp related fields are in milliseconds (Unix epoch).
  • Data is returned in ascending order unless otherwise specified.
  • For GET and DELETE endpoints, parameters are sent as query strings. For POST and PUT endpoints, parameters can be sent as query strings or application/x-www-form-urlencoded request bodies.
  • Your trading account is automatically resolved server-side from your X-MBX-APIKEY header — standard quantitative SDKs (ccxt, Python, TypeScript, Rust) and bots work as a 1:1 drop-in.

Endpoint Security Type

Each endpoint has a security type that determines how you interact with it.

Security TypeDescription
NONEPublic market data endpoint. Can be accessed freely with no authentication required.
TRADE / USER_DATA (SIGNED)Endpoint requires the X-MBX-APIKEY header, timestamp, and an HMAC-SHA256 signature parameter.
USER_STREAMListen-key lifecycle endpoints (/fapi/v1/listenKey) requiring the X-MBX-APIKEY header.
SESSIONMobile/web app endpoints requiring a valid session token (Authorization: Bearer <token>).
  • All trading and account endpoints under /fapi/v1/* and /fapi/v2/* accept SIGNED (X-MBX-APIKEY + HMAC-SHA256) authentication.
  • Market data endpoints (/fapi/v1/ping, /fapi/v1/time, /fapi/v1/exchangeInfo, /fapi/v1/depth, /fapi/v1/klines, /fapi/v1/ticker/24hr) are public (NONE).

Authentication

FROM ZERO TO FIRST TRADE

If you already have USDC or USDT deposited in the GaiaEx mobile app, you're four steps away from trading via API:

  1. Mobile app → Settings → API Keys → Create. Pick read + trade. Copy the api_key and api_secret (the secret is shown only once).
  2. Put them in environment variables next to your script (GAIAEX_API_KEY and GAIAEX_API_SECRET — never a project file; see Security Best Practices).
  3. Sign each request's query string (or form body) with HMAC-SHA256(api_secret, query_string), append &signature=<hex>, and send the X-MBX-APIKEY header.
  4. Call GET /fapi/v2/balance. If it returns your balances, you're trading-ready — call POST /fapi/v1/order to place an order.

That's the whole contract. The rest of this page is the precise definition of step 3.

GaiaEx supports two authentication methods:

Method 1: 1:1 GaiaEx API Key + HMAC-SHA256 (Recommended for programmatic trading)

What HMAC-SHA256 is, in one paragraph. Your API secret never leaves your machine. For each request, you build a query string (or form body) that includes timestamp=<ms> (and optional recvWindow=<ms>, default 5000), compute HMAC-SHA256(api_secret, query_string_or_form_body), and append the resulting lowercase hex digest as signature=<hex>. You send your public key in the X-MBX-APIKEY HTTP header. The server resolves your wallet address automatically from the API key, recomputes the HMAC digest, and verifies they match.

What EIP-712 is, and why you don't need it for API trading. EIP-712 is a structured-data wallet signature used for on-chain actions (deposits, withdrawals, swaps, handshake). It requires a wallet private key and, in GaiaEx, is only produced by the mobile app's embedded wallet. API-key integrations never produce EIP-712 signatures — HMAC-SHA256 is the only signing you need. If a doc page mentions EIP-712, it's describing an in-app flow, not something you'll implement in your bot.

API keys are created in the GaiaEx mobile app (Settings > API Keys). Existing keys can be listed programmatically via the GET /api-keys endpoint, but creating, updating, and revoking keys all require a passkey step-up and are app-only — they cannot be performed headlessly via API key alone (see the API Keys page). Each signed request requires:

ComponentLocationDescription
X-MBX-APIKEYHTTP HeaderYour public API key (32-character hex string)
timestampQuery / form paramCurrent Unix timestamp in milliseconds (e.g., 1712345678000)
recvWindowQuery / form param (optional)Validity window in milliseconds (default 5000, max 60000)
signatureQuery / form paramLowercase hex HMAC-SHA256(api_secret, query_string_or_form_body)

Signature construction:

payload = query_string_or_form_body   # includes timestamp=<ms> and optional recvWindow=<ms>, WITHOUT &signature=...
signature = HMAC-SHA256(api_secret, payload)
  • query_string_or_form_body — all request parameters formatted as key=value&key2=value2...&timestamp=1712345678000 in the exact order sent on the wire.
  • Append &signature=<hex> as the final parameter.

Example (Python):

import hmac
import hashlib
import time
import os
import requests

api_key = os.environ["GAIAEX_API_KEY"]
api_secret = os.environ["GAIAEX_API_SECRET"]

timestamp = int(time.time() * 1000)
query_string = f"timestamp={timestamp}&recvWindow=5000"

signature = hmac.new(
    api_secret.encode("utf-8"),
    query_string.encode("utf-8"),
    hashlib.sha256,
).hexdigest()

headers = {
    "X-MBX-APIKEY": api_key,
}

resp = requests.get(
    f"https://openapi.gaiaex.com/fapi/v2/balance?{query_string}&signature={signature}",
    headers=headers,
)
print(resp.json())

Example (curl):

TIMESTAMP=$(date +%s000)
QUERY="timestamp=${TIMESTAMP}&recvWindow=5000"
SIGNATURE=$(echo -n "$QUERY" | openssl dgst -sha256 -hmac "$GAIAEX_API_SECRET" | awk '{print $2}')

curl -s -H "X-MBX-APIKEY: $GAIAEX_API_KEY" \
     "https://openapi.gaiaex.com/fapi/v2/balance?${QUERY}&signature=${SIGNATURE}"

Method 2: Bearer Session Token (Web app / mobile only)

Session tokens are issued by the GaiaEx mobile app after the user completes an in-app wallet handshake. They are not obtainable from the public REST API and are not intended for programmatic integrations — use API keys instead.

Scope of the Public API

API keys can be used to trade, read account data (balances, positions, fills, order history, funding), manage orders and leverage, and query read-only wallet data (portfolio, swap quotes). They cannot:

  • Deposit or withdraw funds
  • Execute on-chain transfers or swaps
  • Register or authenticate passkeys
  • Complete the account handshake
  • Create, modify, or delete API keys

These actions require the user's embedded-wallet signature and, in several cases, a passkey step-up — both performed in the GaiaEx mobile app. Users fund and defund accounts from the app; API integrations operate within the deposited balance.

HMAC Signing Walkthrough — Real Values You Can Verify

This section uses concrete values at every step. If you plug the same secret and query string into your own code, you must get the exact same signature shown here. If you don't, your implementation has a bug — most likely in parameter ordering or URL encoding.

Example 1: GET Futures Balance (GET /fapi/v2/balance)

Goal: query your USDⓈ-M Futures account balances (wallet address is resolved automatically from X-MBX-APIKEY).

StepValue
1. EndpointGET /fapi/v2/balance
2. Timestamp (ms)1712345678000
3. RecvWindow (ms)5000
4. Query string to signtimestamp=1712345678000&recvWindow=5000
5. API Secret (example)my_secret_key_example_32chars_xx
6. HMAC-SHA256 hex digest70e828ec6d529dbbe924f63c84d33fa273a88c23f5674e62740e7f20efb6ab6d

Example response (HTTP 200):

[
  {
    "accountAlias": "gaiaex-usdc",
    "asset": "USDC",
    "balance": "1523.47",
    "crossWalletBalance": "1523.47",
    "crossUnPnl": "18.92",
    "availableBalance": "892.10",
    "updateTime": 1712345679123
  },
  {
    "accountAlias": "gaiaex-usdt",
    "asset": "USDT",
    "balance": "500.00",
    "crossWalletBalance": "500.00",
    "crossUnPnl": "0.00",
    "availableBalance": "500.00",
    "updateTime": 1712345679123
  }
]
import hmac, hashlib, time, os, requests

# Load from environment variables (recommended)
api_key    = os.environ["GAIAEX_API_KEY"]
api_secret = os.environ["GAIAEX_API_SECRET"]

# Step 1-4: Build query string with timestamp
timestamp    = int(time.time() * 1000)
query_string = f"timestamp={timestamp}&recvWindow=5000"
print(f"Query to sign: {query_string}")

# Step 5-6: Compute HMAC-SHA256
signature = hmac.new(
    api_secret.encode("utf-8"),
    query_string.encode("utf-8"),
    hashlib.sha256,
).hexdigest()
print(f"Signature: {signature}")

# Step 7: Send request with X-MBX-APIKEY
resp = requests.get(
    f"https://openapi.gaiaex.com/fapi/v2/balance?{query_string}&signature={signature}",
    headers={"X-MBX-APIKEY": api_key},
)
print(f"Status: {resp.status_code}")
print(f"Response: {resp.json()}")

Example 2: POST Place Order (POST /fapi/v1/order)

Goal: place a limit buy order for 0.1 ETHUSDC at $3,500.00.

THE PAYLOAD YOU SIGN MUST MATCH THE WIRE ORDER

The signature is computed over the exact query string (or form-encoded body) you send, excluding &signature=.... Do not reorder parameters or alter number formatting between signing and sending.

StepValue
1. EndpointPOST /fapi/v1/order
2. Timestamp (ms)1712345678000
3. Query / form payloadsymbol=ETHUSDC&side=BUY&type=LIMIT&timeInForce=GTC&quantity=0.1&price=3500.00&timestamp=1712345678000&recvWindow=5000
4. API Secret (example)my_secret_key_example_32chars_xx
5. HMAC-SHA256 hex digest0dbdfdba87e6d08bd8f2e5c1a6f131e4863e39eb1c11425cf98cb9eb036af18d

Example response (HTTP 200):

{
  "orderId": 41298374,
  "symbol": "ETHUSDC",
  "status": "NEW",
  "clientOrderId": "bot-a1b2c3",
  "price": "3500.00",
  "avgPrice": "0.00",
  "origQty": "0.1",
  "executedQty": "0.0",
  "cumQuote": "0.00",
  "timeInForce": "GTC",
  "type": "LIMIT",
  "reduceOnly": false,
  "side": "BUY",
  "PositionSide": "BOTH",
  "updateTime": 1712345679456
}

Common rejection (HTTP 401, signature mismatch):

{"code": -1022, "msg": "Signature for this request is not valid."}

If you see this, the server computed a different digest than yours. Check, in order: (1) the parameter string you signed matches the exact parameter order sent on the wire; (2) &signature=... was not included in the signed input; (3) timestamp is in milliseconds (not seconds) and within recvWindow of the server clock.

import hmac, hashlib, time, os, urllib.parse, requests

api_key    = os.environ["GAIAEX_API_KEY"]
api_secret = os.environ["GAIAEX_API_SECRET"]

params = {
    "symbol": "ETHUSDC",
    "side": "BUY",
    "type": "LIMIT",
    "timeInForce": "GTC",
    "quantity": "0.1",
    "price": "3500.00",
    "timestamp": int(time.time() * 1000),
    "recvWindow": 5000,
}
query_string = urllib.parse.urlencode(params)
signature = hmac.new(
    api_secret.encode("utf-8"),
    query_string.encode("utf-8"),
    hashlib.sha256,
).hexdigest()

resp = requests.post(
    f"https://openapi.gaiaex.com/fapi/v1/order?{query_string}&signature={signature}",
    headers={"X-MBX-APIKEY": api_key},
)
print(resp.json())

COMMON SIGNING MISTAKES

  1. Parameter reordering — Sign the exact URL-encoded query string you send on the wire; do not let your HTTP client re-sort parameters after signing.
  2. Signing the full URL or path — For /fapi/v1 & /fapi/v2, sign ONLY the query string / form body (e.g., symbol=ETHUSDC&...&timestamp=...), NOT the URL path.
  3. Clock skew — The server rejects requests outside recvWindow (default 5000ms). Call GET /fapi/v1/time to measure clock offset.
  4. Using seconds instead of milliseconds — timestamp must be 13-digit Unix epoch milliseconds.

Timeouts, Retries, Idempotency

Follow these rules to keep a production bot safe from the most common duplicate-order failure mode.

Recommended HTTP timeout

Set your client timeout to 20 seconds for POST /fapi/v1/order, PUT /fapi/v1/order, and DELETE /fapi/v1/order. Typical responses are under 2 seconds, but allow headroom for network spikes.

Never blind-retry a timeout

IF A CALL TIMES OUT, THE ORDER MAY HAVE PLACED

A client-side HTTP timeout does not mean the order failed. The server may have accepted and placed the order. Before placing a replacement, reconcile:

  1. Fetch GET /fapi/v1/openOrders — does the order already exist?
  2. Fetch GET /fapi/v1/allOrders — was it filled or canceled?
  3. Only if neither shows the order should you place a replacement.

Idempotency via newClientOrderId

Pass a unique ASCII string (max 64 chars) as newClientOrderId in POST /fapi/v1/order. The server treats duplicate client order IDs from the same account within a 10-minute window as idempotent — it returns the existing order instead of creating a second one. Use this as belt-and-suspenders on every order:

import uuid
order = fapi_request("POST", "/fapi/v1/order", {
    "symbol":           "BTCUSDC",
    "side":             "BUY",
    "type":             "LIMIT",
    "timeInForce":      "GTC",
    "quantity":         "0.001",
    "price":            "79000",
    "newClientOrderId": f"bot-{uuid.uuid4().hex[:24]}",   # safe to retry
})

Modify order (PUT /fapi/v1/order)

A successful PUT /fapi/v1/order modifies the existing order on the book and returns the active orderId in the response. Always read orderId from the modify response for any subsequent cancel or modify calls.

Exponential backoff for transient errors

HTTP 429 (rate limited) and 502 (upstream unavailable) are transient — retry with jittered exponential backoff (e.g., 1s, 2s, 4s, up to 30s). HTTP 400 and 401 are not retryable without fixing the request.

Timing Security

  • All SIGNED requests must include a timestamp parameter (Unix epoch in milliseconds).
  • An optional recvWindow parameter (default 5000 ms, maximum 60000 ms) specifies how many milliseconds after timestamp the request remains valid.
  • The server rejects requests where the timestamp is outside the recvWindow tolerance.
  • Use GET /fapi/v1/time to synchronize clocks if needed.

TIP

Network latency can cause clock drift. If you receive timestamp-related rejections, call GET /fapi/v1/time to measure the offset between your local clock and the server.

Rate Limits

API Key Rate Limits (Trading Actions)

Endpoints that place, cancel, or modify orders are subject to per-API-key rate limits:

LimitWindowDefault
BurstPer second10 requests/s
RollingPer minute600 requests/min

Rate-limited endpoints include: POST /fapi/v1/order, DELETE /fapi/v1/order, PUT /fapi/v1/order, DELETE /fapi/v1/allOpenOrders, POST /fapi/v1/leverage, POST /fapi/v1/marginType.

RATE LIMIT VALUES ARE SUBJECT TO CHANGE

The values above reflect current nginx-level configuration. They are not part of the versioned API contract and may be adjusted without notice. Always respect Retry-After headers on 429 responses rather than hard-coding these numbers into your bot logic.

IP-Level Rate Limits

The API gateway enforces IP-based rate limits in addition to per-API-key limits:

ZoneRateApplies To
General30 requests/sAll REST endpoints
Trading10 requests/sOrder placement, cancellation, modification

Session / Handshake

LimitWindow
Per IP10 requests/min
Per address5 requests/min

HTTP 429 Response

When a rate limit is exceeded, the server returns HTTP 429. The response includes a Retry-After header indicating the number of seconds to wait before retrying.

{
  "detail": "Rate limit exceeded. Try again in 5s.",
  "retry_after": 5
}
Response HeaderDescription
X-MBX-USED-WEIGHT-1MCumulative request weight used by your IP in the current 1-minute window
X-MBX-USED-WEIGHTAlias of X-MBX-USED-WEIGHT-1M for GaiaEx SDK compatibility
X-MBX-ORDER-COUNT-1MCumulative order mutations submitted in the current 1-minute window
Retry-AfterSeconds to wait before retrying (present on all 429 responses)

Ban Escalation

Clients that repeatedly violate rate limits or fail to back off after receiving 429 responses may be temporarily IP-banned. Ban durations escalate for repeat offenders.

USE WEBSOCKET STREAMS

For real-time data (order book, positions, balances), use WebSocket streams instead of polling REST endpoints. This reduces rate limit pressure and provides lower-latency updates. See the Servers & URLs page for connection details.

HTTP Return Codes

CodeDescription
200Request succeeded.
400Bad request — malformed parameters or validation failure.
401Unauthorized — missing or invalid authentication / signature.
403Forbidden — API key lacks required permissions or IP restriction violated.
404Endpoint not found, or resource does not exist.
409Conflict — duplicate order or idempotency violation.
422Unprocessable entity — valid syntax but semantic validation failed.
429Rate limit exceeded. Check Retry-After header.
500Internal server error. Retry with exponential backoff.
502Upstream exchange temporarily unreachable. See retry guidance below.
503Service temporarily unavailable. See retry guidance below.

Handling 502 / 503 on Order Endpoints

UNKNOWN EXECUTION STATUS

If you receive a 502 or 503 response on an order-related endpoint (POST /fapi/v1/order, DELETE /fapi/v1/order, PUT /fapi/v1/order), do NOT assume the order failed. The request may have been processed by the exchange before the timeout occurred.

Correct handling:

  1. Query your open orders via GET /fapi/v1/openOrders or the User Data WebSocket stream to verify whether the order was executed.
  2. Only re-submit the order if you have confirmed it was not placed.
  3. Retry with exponential backoff: 1s → 2s → 4s → 8s (max 30s).

For non-order endpoints (market data, account queries), a 502 or 503 is always safe to retry immediately with backoff.

Error Response Format

On /fapi/v1/* and /fapi/v2/* endpoints, errors return a GaiaEx-compatible JSON object with code and msg (or detail):

{
  "code": -1121,
  "msg": "Invalid symbol."
}

For schema validation errors (422):

{
  "detail": [
    {
      "loc": ["query", "symbol"],
      "msg": "field required",
      "type": "value_error.missing"
    }
  ]
}

API Key Permissions

Each API key has a set of permissions that control which endpoints it can access:

PermissionAllows
readAccount data, balances, positions, order history, market data
tradePlace, cancel, modify orders; set leverage; change margin type

By default, newly created API keys have read permission only. Add trade permission to enable order placement.

PERMISSIONS ARE NOT AUTOMATICALLY ASSIGNED

The server does not assign a default permission set on your behalf. You must explicitly select the desired permissions when creating a key in the app (read + trade). The recommended configuration for trading bots is read + trade.

IP Whitelist

API keys can optionally be restricted to specific IP addresses. When an IP whitelist is configured, requests from non-whitelisted IPs will be rejected with HTTP 403.

CORS

The API supports Cross-Origin Resource Sharing (CORS) with the following allowed headers:

  • X-MBX-APIKEY
  • Authorization
  • Content-Type
  • X-Requested-With