Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.exorde.io/llms.txt

Use this file to discover all available pages before exploring further.

The header

Every authenticated request carries your key in the X-API-Key header.
curl https://intel-v1.exorde.io/v1/topics/global/trending \
  -H "X-API-Key: exd_trial_QLocUNNcjQ7TXxTgJZ2DWww4QxjlLBgc"
No cookies, no OAuth, no signed requests. Keys are secrets — treat them like passwords.
Never commit a key to git, embed it in a public SPA bundle, or email it in plaintext. If a key is exposed, rotate it immediately (see below) — rotation is atomic and the old key dies the same instant the new one is born.

Key tiers and prefixes

Each key carries a prefix that hints at its tier. The prefix is cosmetic — the real tier is stored server-side and returned by GET /v1/keys/current.
PrefixTierTypical source
exd_trial_WatchPOST /v1/keys/trial (free, 7 days)
exd_watch_WatchPaid Watch subscription
exd_see_SeePaid See subscription
exd_know_KnowPaid Know subscription
exd_test_TestInternal QA, integrator plumbing tests (not issued to customers)
Test-mode keys (exd_test_*) bypass the database and serve fixture data for deterministic plumbing tests. Production data requires a real key. See Test mode below.

Minting a trial key

Public endpoint, IP-rate-limited, idempotent per email within the key’s active window. Call it twice with the same email and you get the same key back.
curl
curl -X POST https://intel-v1.exorde.io/v1/keys/trial \
  -H "Content-Type: application/json" \
  -d '{"email": "[email protected]"}'
Python
import httpx

r = httpx.post(
    "https://intel-v1.exorde.io/v1/keys/trial",
    json={"email": "[email protected]"},
)
r.raise_for_status()
data = r.json()
print(data["api_key"], "reused:", data["reused"])
A successful response (HTTP 201 first time, HTTP 200 if replayed):
{
  "api_key": "exd_trial_QLocUNNcjQ7TXxTgJZ2DWww4QxjlLBgc",
  "client_id": "trial_3c91be7f",
  "tier": "watch",
  "topics": ["global"],
  "webhook_limit": 0,
  "rate_limit_rpm": 30,
  "monthly_call_quota": 5000,
  "active": true,
  "created_at": "2026-05-19T13:00:00Z",
  "expires_at": "2026-05-26T13:00:00Z",
  "reused": false
}
If you call this endpoint again with the same email while a valid key still exists, you get the same key back with reused: true and HTTP 200. No duplicate keys, no silent reissuance. If the previous key for that email has expired or been revoked, a fresh key is minted (HTTP 201, reused: false).

Inspecting the current key

Use this at runtime to discover what your key can do — tier, topics, expiry, rate limit, webhook quota.
curl https://intel-v1.exorde.io/v1/keys/current \
  -H "X-API-Key: $EXORDE_API_KEY"
{
  "api_key": "exd_trial_QLocUNNcjQ7TXxTgJZ2DWww4QxjlLBgc",
  "client_id": "trial_3c91be7f",
  "tier": "watch",
  "topics": ["global"],
  "webhook_limit": 0,
  "rate_limit_rpm": 30,
  "monthly_call_quota": 5000,
  "active": true,
  "expires_at": "2026-05-26T13:00:00Z"
}
Same shape as the trial response, without reused. Build tier-aware UIs from this — read once at app start, refresh on 401/403.

Rotating a key

Rotation issues a new key with the same tier, topics, limits, and expiry. The old key is deactivated atomically — switch your clients to the new key immediately.
curl -X POST https://intel-v1.exorde.io/v1/keys/rotate \
  -H "X-API-Key: $EXORDE_API_KEY"
{
  "old_api_key": "exd_trial_QLocUNNcjQ7TXxTgJZ2DWww4QxjlLBgc",
  "new_api_key": "exd_trial_r3qiDtHjRNXp8wEYCPRH8L9Wv2nCNkqA",
  "client_id": "trial_3c91be7f",
  "tier": "watch",
  "topics": ["global"],
  "webhook_limit": 0,
  "rate_limit_rpm": 30,
  "expires_at": "2026-05-26T13:00:00Z"
}
Expiry is not extended by rotation. Rotation is for credential hygiene, not lifetime extension. Replay protection: every subsequent call with the old key returns 401 invalid_api_key.

Revoking a key

Permanent. Idempotent. Once revoked, the key cannot be reactivated — a future POST to /v1/keys/trial with the same email will mint a brand-new key.
curl -X DELETE https://intel-v1.exorde.io/v1/keys/current \
  -H "X-API-Key: $EXORDE_API_KEY"
{
  "api_key": "exd_trial_r3qiDtHjRNXp8wEYCPRH8L9Wv2nCNkqA",
  "revoked": true,
  "already_inactive": false,
  "revoked_at": "2026-05-19T13:42:11Z"
}
A second call with the same key returns already_inactive: true and HTTP 200 (idempotent, not an error).

Who am I

GET /v1/me returns the caller’s identity and full entitlements — tier, topics, rate limit, webhook + watchlist quotas, monthly usage. Ideal for building a tier-aware dashboard header or settings page.
curl https://intel-v1.exorde.io/v1/me \
  -H "X-API-Key: $EXORDE_API_KEY"
{
  "client_id": "trial_3c91be7f",
  "tier": "watch",
  "topics": ["global"],
  "limits": {
    "rate_limit_rpm": 30,
    "monthly_call_quota": 5000,
    "webhook_limit": 0,
    "watchlist_limit": 0,
    "watchlist_term_limit": 0
  },
  "usage": {
    "api_calls_this_month": 127,
    "period": "2026-05",
    "webhooks_active": 0,
    "watchlists_active": 0
  },
  "expires_at": "2026-05-26T13:00:00Z",
  "trace_id": "8b3a47ce91d04f17"
}

The typed error envelope

Every non-2xx response follows the same shape. Match on the error enum, show message to users, log trace_id for support.
{
  "error": "upgrade_required",
  "message": "This endpoint requires the 'see' tier",
  "feature": "clusters",
  "current_tier": "watch",
  "required_tier": "see",
  "upgrade": true,
  "trace_id": "8b3a47ce91d04f17"
}
The four authentication-specific errors:
StatuserrorWhenAction
401missing_api_keyHeader not sentAdd X-API-Key
401invalid_api_keyUnknown / rotated / revoked keyMint or rotate
403key_expiredPast expires_atUpgrade or mint a new trial
403topic_deniedKey valid but not scoped to the requested topicUpgrade or change topic
Full list across all routers: Errors.
Trace IDs are 16-hex strings present on every response (success or error) in the X-Exorde-Trace-Id header and inside the JSON envelope on errors. Quote the trace_id when you email support — we resolve in one round trip.

Test mode

Keys with the exd_test_ prefix bypass the database, return a synthetic know-tier shape, and serve fixture data on every analytics endpoint. Use them to wire integrations (HTTP layer, JSON parsing, error handling) without burning real quota or polluting analytics.
# Always returns the same fixture alert envelope, with detected_at rotated to "now"
curl "https://intel-v1.exorde.io/v1/topics/cyber/alerts?hours=24" \
  -H "X-API-Key: exd_test_smoke"
How to tell apart a fixture alert from a live alert:
FieldTest fixtureLive data
alert_idstarts with sig_test_random UUID, no _test_ infix
detected_atrotated to “now-ish”actual detection time
keywordalways iran for cyber, bitcoin for finance, etc.whatever is actually spiking
sample_posts[].previewtemplated stringfirst ~160 chars of a real post
Test keys are not issued to customers. They exist for our internal QA suite and for integrator partners who request a deterministic fixture path during onboarding.

Operational guidance

  • Store keys server-side only. Never in a public SPA bundle, git history, or a client-side env var shipped to users.
  • Rotate on suspicion of leak. Rotation is free, atomic, and preserves all tier/topic/limit settings.
  • Use /v1/me at startup to detect tier changes (upgrades, downgrades, expiries) without polling the billing system.
  • Handle 401 as “get a new key” and 403 as “ask the user to upgrade or change topic” — the codes are distinct for a reason.
  • Always log trace_id alongside any user-facing error message. It is the single most useful piece of evidence in a support ticket.
  • Set a watchdog on expires_at. Trial keys expire silently after 7 days; surface a “renew/upgrade” prompt when within 24h of expiry.

Lifecycle example: full mint → rotate → revoke

import httpx, time

BASE = "https://intel-v1.exorde.io"
EMAIL = "[email protected]"

# 1. Mint
r = httpx.post(f"{BASE}/v1/keys/trial", json={"email": EMAIL}).raise_for_status()
key = r.json()["api_key"]
print("minted:", key)

# 2. Use it
me = httpx.get(f"{BASE}/v1/me", headers={"X-API-Key": key}).json()
print("tier:", me["tier"], "topics:", me["topics"])

# 3. Rotate (suspected leak)
r = httpx.post(f"{BASE}/v1/keys/rotate", headers={"X-API-Key": key}).json()
new_key = r["new_api_key"]
print("rotated:", key, "->", new_key)

# Old key now dead
assert httpx.get(f"{BASE}/v1/me", headers={"X-API-Key": key}).status_code == 401

# 4. Revoke (when done)
r = httpx.delete(f"{BASE}/v1/keys/current", headers={"X-API-Key": new_key}).json()
assert r["revoked"] is True
print("revoked:", new_key)
Every step in this lifecycle is exercised by our QA suite (205 scenarios, currently 100% PASS). See Changelog for release-by-release detail.
Last reviewed: 2026-05-19. API version 1.2.8.