For DevelopersAPIError Responses

Error Responses

This page documents all error codes, response formats, and recommended handling strategies for the GX Exchange API.

HTTP Status Codes

StatusErrorDescriptionAction
200SuccessProcess response body
400bad_requestMalformed request, missing fields, or invalid parametersFix request format
401unauthorizedInvalid signature, expired timestamp, or missing authenticationVerify signature and clock sync
403forbiddenAgent wallet not authorized for this accountCheck agent authorization
404not_foundUnknown endpoint or resourceVerify URL path
429rate_limit_exceededToo many requestsBack off per Retry-After header
500internal_errorServer-side errorRetry with exponential backoff
503service_unavailableTemporary maintenance or overloadRetry after short delay

Error Response Format

All HTTP errors follow this structure:

{
  "error": "error_key",
  "message": "Human-readable explanation of what went wrong.",
  "details": { }
}
FieldTypeDescription
errorstringMachine-readable error code
messagestringHuman-readable description
detailsObject?Optional additional context

Exchange Endpoint Errors

The POST /exchange endpoint returns errors within the response body rather than using HTTP status codes for business logic errors:

Success

{
  "status": "ok",
  "response": {
    "type": "order",
    "data": { "statuses": [{ "resting": { "oid": 12345 } }] }
  }
}

Business Logic Error

{ "status": "err", "response": "Insufficient margin" }

Per-Order Errors (Batch)

When submitting multiple orders, each order can succeed or fail independently:

{
  "status": "ok",
  "response": {
    "type": "order",
    "data": {
      "statuses": [
        { "resting": { "oid": 12345 } },
        { "error": "Price out of range" }
      ]
    }
  }
}

Common Exchange Errors

Error MessageCauseResolution
Insufficient marginNot enough collateral for the orderReduce order size or deposit more funds
Order not foundCancel/modify target does not exist or is already filledVerify the order ID is correct and still active
Invalid signatureEIP-712 signature verification failedCheck signing domain (GXExchange), chain ID (42069), and key
Price out of rangeLimit price too far from the oracle priceUse a price closer to the current market
Reduce only violatedReduce-only order would increase position sizeCheck position direction before placing
Self-trade preventedOrder would match against your own resting orderCancel the existing order first
Max open orders exceededToo many open orders for this marketCancel some existing orders
Account not activatedAddress has not been activated on GX ExchangeComplete the activation process first
Agent not authorizedAgent wallet has not been approved by this addressSubmit approveAgent from the main wallet
Nonce too oldThe nonce timestamp is outside the validity windowSynchronize your system clock via NTP

Validation Errors (400)

Missing Required Field

{
  "error": "bad_request",
  "message": "Missing required field: pair",
  "details": { "field": "pair" }
}

Invalid Order Size

{
  "error": "bad_request",
  "message": "Order size 0.00001 is below minimum 0.0001 for BTC-USDC",
  "details": { "field": "size", "min": "0.0001", "provided": "0.00001" }
}

Invalid Info Type

{
  "error": "bad_request",
  "message": "Unknown info type: invalidType"
}

Authentication Errors (401)

Invalid Signature

{
  "error": "unauthorized",
  "message": "Invalid signature. Verify your EIP-712 computation.",
  "details": { "hint": "Check domain name=GXExchange, chainId=42069, and verifyingContract=0x0" }
}

Expired Timestamp (HMAC)

{
  "error": "unauthorized",
  "message": "Timestamp is outside the 30-second validity window.",
  "details": { "server_time": 1700000030, "provided": 1700000000 }
}

Rate Limit Errors (429)

{
  "error": "rate_limit_exceeded",
  "message": "Rate limit exceeded. Retry after 0.3 seconds.",
  "retry_after": 0.3
}

The Retry-After header is also set on the HTTP response.

Error Handling Best Practices

Retry Strategy

Error TypeRetry?Strategy
400 Bad RequestNoFix the request; retrying will produce the same error
401 UnauthorizedNoCheck signature logic or clock sync
403 ForbiddenNoVerify agent authorization
404 Not FoundNoCheck endpoint URL
429 Rate LimitedYesWait for retry_after seconds
500 Internal ErrorYesExponential backoff: 1s, 2s, 4s, 8s, max 30s
503 Service UnavailableYesWait 5-10 seconds, check status page

TypeScript Error Handler

async function apiRequest(url: string, body: any, maxRetries = 3): Promise<any> {
  for (let attempt = 0; attempt < maxRetries; attempt++) {
    const response = await fetch(url, {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify(body),
    });
 
    if (response.ok) {
      return response.json();
    }
 
    if (response.status === 429) {
      const retryAfter = parseFloat(response.headers.get("Retry-After") || "1");
      await new Promise(r => setTimeout(r, retryAfter * 1000));
      continue;
    }
 
    if (response.status >= 500) {
      const delay = Math.min(1000 * Math.pow(2, attempt), 30000);
      await new Promise(r => setTimeout(r, delay));
      continue;
    }
 
    // Non-retryable error
    const error = await response.json();
    throw new Error(`API Error ${response.status}: ${error.message}`);
  }
 
  throw new Error("Max retries exceeded");
}

Python Error Handler

import time
import requests
 
def api_request(url: str, body: dict, max_retries: int = 3) -> dict:
    for attempt in range(max_retries):
        resp = requests.post(url, json=body)
 
        if resp.ok:
            return resp.json()
 
        if resp.status_code == 429:
            retry_after = float(resp.headers.get("Retry-After", "1"))
            time.sleep(retry_after)
            continue
 
        if resp.status_code >= 500:
            delay = min(2 ** attempt, 30)
            time.sleep(delay)
            continue
 
        # Non-retryable
        error = resp.json()
        raise Exception(f"API Error {resp.status_code}: {error.get('message')}")
 
    raise Exception("Max retries exceeded")