Skip to content

Authentication and API Keys

The market and exchange catalog is public: GET /v1/markets, GET /v1/markets/{instrument}, and GET /v1/venues work without credentials. Other REST operations require an API key and an HMAC-SHA256 signature. Keep signing code in a trusted server process.

A key grants access to specific accounts and scopes. Give a market-data collector market:read, a reconciliation process read permissions, and an execution process the trading permissions it needs.

Scope Access
market:read Server time, order books, tickers, trades, and funding rates
accounts:read Accounts, balances, positions, fees, limits, and margin settings
accounts:write Change leverage and margin mode
orders:read Orders, child orders, and fills
orders:write Create, amend, cancel, batch, and dead-man-switch operations
custody:read Linked custodians, allocations, and settlements
custody:write Allocate eligible custody collateral
transfers:read Deposits and internal transfer history
transfers:write Transfer available funds between authorized subaccounts
keys:read Inspect key metadata and account grants

Provision keys through your approved account’s access process. Record the secret when issued, store it in a secret manager, and restrict the key to your servers’ outbound IP addresses. Use key metadata to check permissions and expiry. Contact Us for access changes.

Header Value
X-Trade8-API-Key Public key ID
X-Trade8-Timestamp UTC Unix time in whole seconds
X-Trade8-Signature Base64-encoded HMAC-SHA256 signature
Content-Type application/json when sending a JSON body
Idempotency-Key Required for POST, PUT, PATCH, and DELETE

The timestamp must be within 30 seconds of server time. Synchronize your host with NTP and check GET /v1/time when diagnosing clock errors. Every key is checked against its account grants, scopes, status, expiry, and IP allowlist.

Join these five strings with a single newline (\n), in this order:

  1. Timestamp exactly as sent in the header.
  2. Uppercase HTTP method.
  3. Encoded request path and query, including /v1/ and the leading /.
  4. Idempotency key, or an empty string for GET.
  5. Exact UTF-8 body, or an empty string when the request has no body.

Decode the base64 API secret into its 32 bytes. Compute HMAC-SHA256 over the joined string, then base64-encode the digest. Use standard base64 with padding.

For a GET request, the string ends with two newline bytes:

1788177600\nGET\n/v1/account/balances?account_id=acct_example_main\n\n

Serialize JSON once, sign that string, and send those same bytes. Preserve query order and percent encoding. Omit the hostname from the signed path. A change to the body, query, method, timestamp, or idempotency key requires a new signature.

import { createHmac } from 'node:crypto';
const body = JSON.stringify(order);
const timestamp = Math.floor(Date.now() / 1000).toString();
const path = '/v1/orders';
const idempotencyKey = 'order-request-0001';
const payload = [timestamp, 'POST', path, idempotencyKey, body].join('\n');
const signature = createHmac(
'sha256', Buffer.from(process.env.TRADE8_API_SECRET, 'base64')
).update(payload, 'utf8').digest('base64');

Download the Node.js Request Helper. It signs the serialized body, enforces the Trade8 origin, rejects redirects, and applies a 10-second request timeout. It leaves retry decisions to the caller.

import base64
import hashlib
import hmac
import json
import os
import time
body = json.dumps(order, separators=(",", ":"), ensure_ascii=False)
timestamp = str(int(time.time()))
path = "/v1/orders"
idempotency_key = "order-request-0001"
payload = "\n".join([timestamp, "POST", path, idempotency_key, body])
signature = base64.b64encode(hmac.new(
base64.b64decode(os.environ["TRADE8_API_SECRET"]),
payload.encode("utf-8"), hashlib.sha256
).digest()).decode()

Download the Python Request Helper.

Use the Node helper to calculate headers for this exact read-only request, then pass them to cURL. Keep shell tracing disabled and run this on a trusted host; command-line arguments can be visible to other local users.

Terminal window
# Supply TRADE8_API_KEY and TRADE8_API_SECRET from your secret manager.
request_path='/v1/account/balances?account_id=acct_example_main'
timestamp=$(date +%s)
signature=$(node --input-type=module - "$timestamp" "$request_path" <<'JS'
import { sign } from './rest-client.mjs';
console.log(sign(process.env.TRADE8_API_SECRET, process.argv[2], 'GET', process.argv[3]));
JS
)
curl --fail-with-body "https://api.trade8.xyz$request_path" \
-H "X-Trade8-API-Key: $TRADE8_API_KEY" \
-H "X-Trade8-Timestamp: $timestamp" \
-H "X-Trade8-Signature: $signature"
unset signature

Issue a replacement with the same minimum permissions, load it into the secret manager, and restart the relevant clients. Verify private reads, reconnect authenticated WebSocket sessions, then revoke the old key through your account’s access process. Separate signing credentials from application logs and support attachments.

WebSocket authentication uses the same HMAC function with a purpose-specific payload. See Connection and Authentication.

Account access

Jurisdiction Restriction

Trade8 is not available in your jurisdiction.

You can still explore the product and read the documentation.

Explore the Docs