Server API Keys
Authenticate server-to-server calls with x-app-api-key and optionally lock your app to key-only access
This recipe is for partners calling the AGG API from their own backend (Node, Go, Python, anywhere that isn't the end-user's browser). Browser-based integrations do not need an API key — they're authenticated by their allowed origins and the end-user's session token.
An API key is a secret. Never embed it in browser bundles, source maps, public repositories, or logs. Treat it like a database password.
When You Need One
Send x-app-api-key from your backend when:
- You call any AGG endpoint from server code (no
Originheader, no user session). - You want per-key rate-limit quota instead of the shared browser-IP quota.
- You've enabled
requireApiKeyon your app.
The header is always accepted but never required by default — turning on requireApiKey
is how you make it mandatory for your app.
Creating a Key
Open the admin dashboard, select the target app, and go to Credentials → API Keys → Create new key. Pick a scope:
| Scope | Can do |
|---|---|
read | Read-only access to your app's public endpoints. |
read_write | Read + partner-admin write operations on your app (members, settings, etc., excluding security). |
The key value is shown once in the creation dialog. Copy it immediately and store it in your secret manager — AGG only stores a SHA-256 hash and cannot display the raw value again.
Keys have the format agg_<appId>_<64 hex>. The appId is embedded so the key
is bound to a specific app; using it against a different app's x-app-id is
rejected with 403.
Sending It
From @agg-build/sdk
import { createAggClient } from "@agg-build/sdk";
const client = createAggClient({
baseUrl: "https://api.agg.market",
appId: process.env.AGG_APP_ID!,
apiKey: process.env.AGG_API_KEY!, // server-side only
});
// Every call now includes x-app-api-key automatically.
await client.getOrderbooks({ venueMarketIds: ["..."] });
Raw HTTP
GET /app/config HTTP/1.1
Host: api.agg.market
x-app-id: your-app-id
x-app-api-key: agg_your-app-id_<64 hex>
A key that's missing, malformed, unknown, revoked, expired, or bound to a
different app is rejected with 401 / 403. The server returns identical
"Invalid API key" messages for unknown and wrong-app cases — don't rely on
the error text to distinguish them.
Locking Your App to Key-Only Access
By default the key is optional — requests without one proceed as before. If your integration is strictly server-side and you want AGG to hard-reject unauthenticated calls, flip Require API key in the admin dashboard's Settings view for that app.
Once enabled:
- Any request to your app without a valid
x-app-api-keyreturns 401 with"This app requires x-app-api-key for all requests." - Browser-SDK calls from your app will start failing — they don't carry a key. Only turn this on for server-only apps.
- The toggle can only be flipped by a signed-in admin. A
read_writeAPI key cannot call the endpoint that changes it (defense against a compromised key locking you out of your own app).
Enabling requireApiKey on a browser-facing app will break the integration.
If in doubt, leave it off — the origin allowlist already protects browser
callers from cross-site misuse.
Rate Limits
A validated x-app-api-key moves you into a dedicated per-key budget:
- Default — shared with your app's general quota (
RATE_LIMIT_APP_MAX, 18 000 requests/minute on the platform today). - Custom — AGG can raise or lower an individual key's budget on request; once set, that key no longer shares IP-level throttling.
Keyed requests respond with 429 Too Many Requests and a Retry-After: <seconds>
header when the per-key bucket is exhausted.
Rotating a Key
- Create a new key in the admin dashboard.
- Deploy your backend with the new key.
- Confirm traffic is using the new key (check
lastUsedAtin the admin). - Revoke the old key.
Revoked and expired keys fail fast with 401 — there is no grace period.