Threat intelligence API / integrations

curl

The ThreatCluster threat-intelligence API is plain HTTP: 1 request header in, JSON out, with cost and rate information carried in response headers. This page documents that contract with curl, including 1 GET for each data family and the exact status codes you will meet.

Prerequisites

  1. A free API key. Sign in and mint one under Settings → API. Free keys carry 100 credits a day, 30 requests a minute and a 7-day data window.
  2. Every request sends the key in the X-API-Key header.
  3. Base URL: https://threatcluster.io/api/public/v1

Setup

  1. Export the key and base URL once per shell (on Windows, see the Windows Terminal guide):

    export TC_KEY="tc_live_your_key_here"
    export BASE="https://threatcluster.io/api/public/v1"
  2. Make a first call with -i to see the response headers alongside the body:

    curl -si -H "X-API-Key: $TC_KEY" "$BASE/threats?time_filter=24h&limit=1"

Headers

Request: the key is the only header the API needs.

X-API-Key: tc_live_your_key_here

Response: every budgeted response reports what it cost, and free keys also get budget headers so a script can pace itself. Header names are exact; the numbers below are illustrative:

HTTP/2 200
content-type: application/json
x-request-cost: 1
x-ratelimit-limit: 100
x-ratelimit-remaining: 87
x-ratelimit-reset: 1788307200

X-Request-Cost is the credits this request spent (0 when an empty result was refunded). X-RateLimit-Limit and X-RateLimit-Remaining track the daily credit budget, and X-RateLimit-Reset is the unix time of the next refill at 00:00 UTC. A 429 adds a Retry-After header.

The data families

# search across clusters, entities and the dark web (5 credits)
curl -s -H "X-API-Key: $TC_KEY" "$BASE/search?q=Qilin"

# threat clusters (1 credit)
curl -s -H "X-API-Key: $TC_KEY" "$BASE/threats?time_filter=24h"

# 1 cluster, by the 8-character short id from /threats or /search (1 credit)
curl -s -H "X-API-Key: $TC_KEY" "$BASE/threats/93fb80f8"

# entity detail (1 credit)
curl -s -H "X-API-Key: $TC_KEY" "$BASE/entities/ransomware_group/Qilin"

# IOC feed for SIEM or firewall polling (3 credits)
curl -s -H "X-API-Key: $TC_KEY" "$BASE/iocs/feed?format=txt"

# CVEs, here CISA KEV entries from the last 7 days (1 credit)
curl -s -H "X-API-Key: $TC_KEY" "$BASE/vulnerabilities?kev_only=true&days=7"

# ransomware leak-site victims (1 credit)
curl -s -H "X-API-Key: $TC_KEY" "$BASE/darkweb/ransomware/victims?days=7"

Worked example

/search fans out across the corpus in 1 call and returns typed buckets. Response below trimmed to 1 hit per bucket; the live call returned 30:

curl -s -H "X-API-Key: $TC_KEY" "$BASE/search?q=Qilin"
{
  "query": "Qilin",
  "clusters": [
    {
      "short_id": "3a29f452",
      "cluster_id": "b2c83a72-8d2c-4913-bb97-47c73a29f452",
      "slug": "qilin-ransomware-gang-targets-atf-in-cyberattack-3a29f452",
      "ai_title": "Qilin Ransomware Gang Claims Attack on ATF, Major Incident Declared",
      "ai_summary": "The Bureau of Alcohol, Tobacco, Firearms and Explosives (ATF) is investigating a major cybersecurity incident after the Qilin ransomware gang claimed…",
      "urgency_level": "medium",
      "keywords": ["ransomware", "attack"],
      "threat_score": 61.25,
      "severity_score": 70.0,
      "article_count": 31,
      "date_range_latest": "2026-08-26T22:35:02+00:00",
      "is_new": false,
      "is_trending": false,
      "entities": {}
    }
  ],
  "entities": [
    {
      "entity_type": "ransomware_group",
      "entity_value": "Qilin",
      "cluster_count": 124,
      "article_count": 202
    }
  ],
  "darkweb": [
    {
      "type": "victim",
      "name": "Commission de la construction du Quebec",
      "id": "87989ff49ba65be4",
      "date": "2026-09-01T03:57:10.128191+00:00",
      "group": "qilin",
      "country": "CA",
      "sector": "Government & Defense"
    }
  ],
  "limit": 10,
  "total": 30
}

Chain the short_id into /threats/3a29f452, and entity_type plus entity_value into /entities/ransomware_group/Qilin.

Status codes

200: success. The body is JSON except for the txt and csv IOC formats.

401: missing or bad key. Error details arrive wrapped in a detail object:

{"detail": {"error": "Invalid or revoked API key",
            "message": "The provided API key is invalid, revoked, or expired"}}

403 insufficient_scope: the key lacks the scope the endpoint needs. The body names required_scope and granted_scopes. Free keys carry threats:read, iocs:read, entities:read, vulns:read and darkweb:read.

403 lookback_exceeded: a by-id lookup of a record older than the free 7-day window:

{"detail": {"error": "lookback_exceeded",
            "message": "This threat cluster is older than the 7-day window included with a free API key.",
            "lookback_days": 7,
            "upgrade_url": "/pricing"}}

422: invalid parameters, for example a /search q that is too short.

429: 2 causes. The per-minute rate limit (30 requests a minute on free keys) and the daily credit budget (daily_budget_exceeded). Both send Retry-After; honour it before retrying.

Costs and limits

Free keys spend a daily budget of 100 credits. Most GETs cost 1 credit; the IOC feed and export, STIX bundles, dark-web keyword hits and trends cost 3; /search costs 5; a dark-web victim enrichment record costs 10. The rate limit is 30 requests a minute and data goes back 7 days. A request that finds nothing refunds its credits and returns X-Request-Cost: 0.

Endpoint reference: /api/public/v1/docs. Bigger windows and budgets: /pricing.