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.

Each recipe below is a working integration, not a sketch. Set EXORDE_API_KEY, paste the snippet, run. The recipes lean on three core signal endpoints — see Trending, Narrative, and Alerts for the full envelopes powering each tile and event.

1. Newsroom — catch breaking stories before the wires

Goal: alert the news desk when a topic spikes outside its normal pattern. Tier: Watch trial sufficient for global. See/Know required for cyber/finance/disinfo. Endpoints: GET /v1/topics/{topic}/alerts, optional webhook subscription. See Alerts for the full alert envelope, signal types, IOC schema, and webhook delivery details. A real signal from this pipeline (2026-05-18, 6.67σ above 14-day baseline, validated by our LLM gate):
Multiple credible data breach disclosures (Turkish breach, FoxIT/Foxit software, gaming accounts) surfacing on dark web with fact-checker verification signals genuine cybersecurity incidents being reported and discussed across platforms.
import os, time, httpx
from datetime import datetime, timezone

BASE = "https://intel-v1.exorde.io"
HEADERS = {"X-API-Key": os.environ["EXORDE_API_KEY"]}
SEEN: set[str] = set()


def poll(topic: str = "global", hours: int = 168) -> list[dict]:
    r = httpx.get(
        f"{BASE}/v1/topics/{topic}/alerts",
        params={"hours": hours, "limit": 50, "llm_validated": True},
        headers=HEADERS,
        timeout=10,
    )
    if r.status_code == 429:
        time.sleep(int(r.headers.get("Retry-After", 5)))
        return []
    r.raise_for_status()
    fresh = [a for a in r.json()["alerts"] if a["alert_id"] not in SEEN]
    SEEN.update(a["alert_id"] for a in fresh)
    return fresh


def slack_card(a: dict) -> dict:
    sev = a["severity"]
    spread = a["spread"]
    text = (
        f"*🚨 Volume spike on `{a['topic']}` — keyword `{a['keyword']}`*\n"
        f"σ={sev['deviation_sigma']:.2f}  "
        f"current={sev['current_value']:.0f}  "
        f"baseline={sev['baseline_value']:.1f}\n"
        f"Spread: {spread['domain_count']} domains, "
        f"{spread['language_count']} languages\n\n"
        f"_{a['description']}_"
    )
    return {
        "text": f"*{a['keyword']}* — {a['description']}",
        "blocks": [
            {"type": "section", "text": {"type": "mrkdwn", "text": text}}
        ],
    }


while True:
    for a in poll("global"):
        ts = datetime.now(timezone.utc).strftime("%H:%M:%S")
        print(f"[{ts}] {a['keyword']:<25} σ={a['severity']['deviation_sigma']:.2f}")
        # httpx.post(SLACK_WEBHOOK_URL, json=slack_card(a))
    time.sleep(60)
Cost: 1,440 calls/day per topic. Comfortably inside Watch’s 5,000-call monthly budget for one topic; trivial on See’s 250,000 monthly budget across multiple topics. Upgrade path: swap polling for a webhook subscription (See/Know) to get sub-second push delivery into Slack/Teams/PagerDuty without burning RPM.

2. Brand monitoring with watchlists

Goal: track every public mention of your brand, your domains, and your executives — across the whole conversation, not just one platform. Tier: See or Know. Endpoints: POST /v1/watchlists, GET /v1/watchlists/{id}/(trending|entities|platforms|posts|alerts). See Topics and watchlists for the full term-type reference.
import os, httpx

BASE = "https://intel-v1.exorde.io"
KEY = os.environ["EXORDE_API_KEY"]
HEADERS_JSON = {"X-API-Key": KEY, "Content-Type": "application/json"}
HEADERS_GET = {"X-API-Key": KEY}


# 1. Create the watchlist (one-time)
r = httpx.post(
    f"{BASE}/v1/watchlists",
    headers=HEADERS_JSON,
    json={
        "name": "acme-brand",
        "base_topic": "global",
        "terms": [
            {"type": "keyword", "value": "acme"},
            {"type": "keyword", "value": "acme corp"},
            {"type": "domain",  "value": "acme.com"},
            {"type": "entity",  "value": "jane doe"},   # CEO
            {"type": "entity",  "value": "john roe"},   # CFO
        ],
    },
    timeout=10,
)
r.raise_for_status()
wl_id = r.json()["id"]
print("created:", wl_id)


# 2. Daily snapshot — what's spiking, who's mentioned, where
def daily_snapshot(wl_id: str) -> None:
    def g(path: str) -> dict:
        return httpx.get(
            f"{BASE}/v1/watchlists/{wl_id}{path}",
            headers=HEADERS_GET,
            timeout=10,
        ).json()

    print("Top terms:    ", [t["term"] for t in g("/trending")["terms"][:5]])
    print("Top entities: ", [e["entity"] for e in g("/entities")["entities"][:5]])
    print("Top platforms:", [p["platform"] for p in g("/platforms")["platforms"][:5]])

    alerts = g("/alerts?hours=24")["alerts"]
    if alerts:
        print(f"!! {len(alerts)} brand alerts in last 24h")


daily_snapshot(wl_id)
Why watchlists beat keyword-only search: the Exorde pipeline already classifies entities and clusters posts by narrative. A watchlist runs the same pipeline filtered to your terms, so you get structured signal — entity leaderboards, platform breakdowns, narrative summaries — not a keyword feed.

3. Threat-intel desk — daily cyber alert digest

Goal: every morning, the analyst sees yesterday’s high-σ cyber events with IOCs extracted, sample posts, and matched cluster context. Tier: See. Endpoints: GET /v1/topics/cyber/alerts, GET /v1/topics/cyber/clusters/{id} for drill-down. Full alert envelope including the IOC schema lives in Alerts.
import os, httpx
from datetime import datetime, timezone

BASE = "https://intel-v1.exorde.io"
H = {"X-API-Key": os.environ["EXORDE_API_KEY"]}


def cyber_morning_digest() -> None:
    r = httpx.get(
        f"{BASE}/v1/topics/cyber/alerts",
        params={"hours": 24, "limit": 50, "llm_validated": True},
        headers=H,
        timeout=10,
    ).json()

    if not r["alerts"]:
        print("no qualifying cyber alerts in last 24h")
        return

    today = datetime.now(timezone.utc).strftime("%Y-%m-%d")
    print(f"=== Cyber digest, {today} ===")

    for a in sorted(r["alerts"], key=lambda x: -x["severity"]["deviation_sigma"]):
        sev = a["severity"]
        iocs = a["iocs"]

        ioc_summary: list[str] = []
        for k in ("cves", "domains", "ips", "urls", "crypto_wallets"):
            if iocs.get(k):
                ioc_summary.append(f"{len(iocs[k])} {k}")
        for h_alg in ("md5", "sha1", "sha256"):
            if iocs["hashes"].get(h_alg):
                ioc_summary.append(f"{len(iocs['hashes'][h_alg])} {h_alg}")

        print(f"\n[{a['detected_at']}]  σ={sev['deviation_sigma']:.2f}  '{a['keyword']}'")
        print(f"  {a['description']}")
        if a.get("matched_cluster"):
            print(f"  cluster: {a['matched_cluster']['cluster_title']}")
        if ioc_summary:
            print(f"  IOCs: {', '.join(ioc_summary)}")
        for cve in iocs.get("cves", [])[:3]:
            print(f"    - {cve}")


cyber_morning_digest()
Sample output, real run from 2026-05-18: Cyber digest, 2026-05-19 [2026-05-18T04:00:30.148Z] σ=6.67 ‘dark web’ Multiple credible data breach disclosures (Turkish breach, FoxIT/Foxit software, gaming accounts) surfacing on dark web with fact-checker verification… cluster: Dark-web breach disclosures, May 2026 Drill-down: for any alert with a matched_cluster.cluster_id, fetch the full cluster context with GET /v1/topics/cyber/clusters/{id} (See tier) — top entities, top domains, time-series, full evidence post list.

4. Disinformation early-warning

Goal: catch coordinated narrative pushes before they reach mainstream amplification — multi-platform, multi-language synchronisation, LLM-validated. Tier: See or Know. Endpoints: GET /v1/topics/disinfo/alerts, GET /v1/topics/disinfo/narratives/history, GET /v1/topics/disinfo/platforms.
import os, httpx

BASE = "https://intel-v1.exorde.io"
H = {"X-API-Key": os.environ["EXORDE_API_KEY"]}


# 1. High-confidence disinfo alerts — LLM-validated, multi-language, multi-domain
r = httpx.get(
    f"{BASE}/v1/topics/disinfo/alerts",
    params={"hours": 168, "limit": 25},
    headers=H,
    timeout=10,
).json()

suspicious = [
    a for a in r["alerts"]
    if a.get("llm_validated")
    and a["spread"]["language_count"] >= 3
    and a["spread"]["domain_count"] >= 5
    and a["confidence"] >= 0.7
]
print(f"{len(suspicious)} high-confidence multi-platform disinfo signals")


# 2. Cross-reference with cluster context where available
for a in suspicious[:5]:
    sev = a["severity"]
    spread = a["spread"]
    print(f"\n-  {a['keyword']}  σ={sev['deviation_sigma']:.1f}")
    print(f"  spread: {spread['domain_count']} domains × {spread['language_count']} langs")
    print(f"  context: {a['description'][:200]}")
    if a.get("matched_cluster"):
        print(f"  cluster: {a['matched_cluster']['cluster_title']}")
Operational tip: combine the disinfo signal with /narratives/history (See tier) to see whether the alert is part of a longer-running narrative pivot or a sudden burst. A campaign that’s been smouldering for two weeks behaves very differently from one that’s a 6-hour burst.

5. Executive dashboard — single-pane situational awareness

Goal: one page that shows, for every curated topic: latest narrative, top 3 trending terms, alert count last 24h, freshness. Tier: See. Endpoints: GET /v1/topics/{t}/(narrative|trending|alerts) × 4 topics. See Trending and Narrative for the full response envelopes powering each tile.
import os, asyncio, httpx
from datetime import datetime, timezone

BASE = "https://intel-v1.exorde.io"
H = {"X-API-Key": os.environ["EXORDE_API_KEY"]}
TOPICS = ["global", "cyber", "finance", "disinfo"]


async def fetch(client: httpx.AsyncClient, topic: str) -> dict:
    n, t, a = await asyncio.gather(
        client.get(f"{BASE}/v1/topics/{topic}/narrative"),
        client.get(f"{BASE}/v1/topics/{topic}/trending"),
        client.get(f"{BASE}/v1/topics/{topic}/alerts?hours=24&limit=50"),
    )
    n_data, t_data, a_data = n.json(), t.json(), a.json()
    return {
        "topic": topic,
        "narrative": n_data.get("summary", ""),
        "top_terms": [x["term"] for x in t_data.get("terms", [])[:3]],
        "alert_count": a_data.get("count", len(a_data.get("alerts", []))),
        "freshness_seconds": n_data.get("data_freshness", {}).get("snapshot_age_seconds"),
    }


async def main() -> None:
    async with httpx.AsyncClient(headers=H, timeout=10) as client:
        rows = await asyncio.gather(*[fetch(client, t) for t in TOPICS])

    now = datetime.now(timezone.utc).strftime("%Y-%m-%d %H:%M UTC")
    print(f"\n=== Exorde Intel — {now} ===\n")
    for row in rows:
        age_min = (row["freshness_seconds"] or 0) // 60
        print(f"## {row['topic'].upper()}  "
              f"(snapshot {age_min}min ago, {row['alert_count']} alerts/24h)")
        print(f"   trending: {', '.join(row['top_terms'])}")
        print(f"   {row['narrative'][:240]}\n")


asyncio.run(main())
Cost: 12 API calls per refresh (3 endpoints × 4 topics). Refresh every minute = 12 calls/minute, comfortably inside See’s 120 RPM (10% utilisation) and a rounding error against See’s 250,000 monthly quota.

What to build next

PatternEndpointsValue
Backfill into a warehouse/posts paginated, /narratives/historyTrain your own models on the curated post stream
Custom topic for an enterpriseEmail [email protected] with the desired scopeKnow-tier private topic with bespoke baselines and tailored narrative voice
Multi-tenant SaaS layered on ExordeOne paid Know key, watchlist-per-tenant, your own quota planeResell intelligence to N customers from one contract
Design partner — narrative voiceNarrative is in alpha; tell us what shape your ideal output takesDirect influence on the v1 contract, early access to tailoring features
Reach out if you have a use case that doesn’t map cleanly onto these — most of our paid contracts started as a “can you do X” email.
Last reviewed: 2026-05-19. API version 1.2.8. All snippets tested against production.