Skip to main content

Error Codes

All error responses follow a consistent format:
{
  "success": false,
  "error": {
    "code": "ERROR_CODE",
    "message": "Human-readable error description."
  }
}

HTTP status codes

CodeMeaning
200Success
201Created
400Bad Request — invalid parameters
401Unauthorized — missing or invalid API key
404Not Found — resource doesn’t exist
409Conflict — duplicate reference or resource
422Unprocessable Entity — valid format but business logic rejected
429Too Many Requests — rate limit exceeded
500Internal Server Error

Error codes

Authentication

CodeHTTPDescription
UNAUTHORIZED401Missing or invalid x-api-key header.
API_KEY_REVOKED401The API key has been revoked. Contact zetdotmoney@gmail.com to generate a new one.
IP_NOT_ALLOWED401Request from an IP address not in the allowlist.

Validation

CodeHTTPDescription
INVALID_REQUEST400Request body failed validation. Check the message for specifics.
INVALID_AMOUNT400Amount must be a positive number.
INVALID_TOKEN400Token symbol not supported on the specified chain.
INVALID_CHAIN400Chain identifier not supported.
INVALID_ADDRESS400Wallet or contract address is not a valid Ethereum/EVM address.
INVALID_BANK_CODE400Bank code not recognized. Use /onramp/banks to get valid codes.
INVALID_ACCOUNT_NUMBER400Account number format is invalid. Must be 10 digits.

Quotes

CodeHTTPDescription
QUOTE_EXPIRED422The quote has expired. Request a new quote.
QUOTE_NOT_FOUND404Quote ID not found or belongs to another API key.
NO_ROUTE_FOUND422No swap/bridge route available for this token pair. Try a different pair or amount.
AMOUNT_TOO_LOW422Amount is below the minimum for this operation.
AMOUNT_TOO_HIGH422Amount exceeds the maximum for this operation.

Wallets

CodeHTTPDescription
WALLET_NOT_FOUND404Wallet ID not found or belongs to another API key.
DUPLICATE_EXTERNAL_USER409A wallet already exists for this externalUserId.
INSUFFICIENT_BALANCE422Wallet doesn’t have enough balance for this transaction.

Transactions

CodeHTTPDescription
TRANSACTION_NOT_FOUND404Transaction ID not found or belongs to another API key.
DUPLICATE_REFERENCE409A transaction with this reference already exists.
TRANSACTION_FAILED422The on-chain transaction reverted. Check errorMessage for details.

Ramp

CodeHTTPDescription
BANK_VERIFICATION_FAILED422Could not verify the bank account. Check the bank code and account number.
RAMP_PROVIDER_ERROR500The ramp provider (Flint) returned an error. Retry or contact support.
DEPOSIT_EXPIRED422The bank deposit window has expired. Initiate a new on-ramp.

Webhooks

CodeHTTPDescription
WEBHOOK_NOT_FOUND404Webhook ID not found.
INVALID_WEBHOOK_URL400URL must be HTTPS and reachable.
MAX_WEBHOOKS_REACHED422Maximum number of webhooks (10) reached. Delete unused ones.

Rate limiting

CodeHTTPDescription
RATE_LIMITED429Too many requests. Respect the X-RateLimit-Reset header.

Server

CodeHTTPDescription
INTERNAL_ERROR500Unexpected server error. Retry with exponential backoff. If persistent, contact support.
SERVICE_UNAVAILABLE503Temporary maintenance. Retry after a few minutes.

Handling errors

async function zetApiCall(endpoint, body) {
  const response = await fetch(`https://api.zetmoney.co/v1${endpoint}`, {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      'x-api-key': process.env.ZET_API_KEY,
    },
    body: JSON.stringify(body),
  });

  const result = await response.json();

  if (!result.success) {
    const { code, message } = result.error;

    switch (code) {
      case 'RATE_LIMITED':
        // Retry after delay
        const retryAfter = response.headers.get('X-RateLimit-Reset');
        await sleep(retryAfter * 1000 - Date.now());
        return zetApiCall(endpoint, body);

      case 'QUOTE_EXPIRED':
        // Get a new quote
        throw new Error('Quote expired, please request a new one');

      case 'INSUFFICIENT_BALANCE':
        // Show user-friendly message
        throw new Error('Not enough balance for this transaction');

      default:
        throw new Error(`Zet API Error [${code}]: ${message}`);
    }
  }

  return result.data;
}