Skip to content

Programmatic access

JSON API

The whole dataset, serialised to flat JSON at build time and served straight off the CDN. No authentication, no keys, no rate limits, no cold starts — because there's no server, just files.

Endpoints

/api/index.json

Endpoint directory and dataset counts.

/api/stats.json

Dataset totals and currency date.

/api/actors.json

All actors in summary form, with attribution and counts.

/api/actors/{slug}.jsontry /api/actors/apt29.json

Full profile: bio, aliases, tools, techniques, CVEs, campaigns, and every citation.

/api/aliases.json

Flat normalised alias → actor map. The endpoint to hit when automating designator resolution.

/api/crosswalk.json

Every actor's designators across every vendor taxonomy, with correlation quality.

/api/cves.json

CVE reference table, each with the actors that exploited it.

Single CVE with per-actor usage detail and sources.

/api/campaigns.json

All documented campaigns with sectors, CVEs, and sources.

/api/feed.json

Activity feed items.

/api/countries.json

Countries of origin with their actor rosters.

/api/sectors.json

Target sectors with the actors that hit them.

/api/vendors.json

Vendor naming taxonomies and how each constructs names.

Resolving an unknown designator

The most common automation task: a report arrives using a taxonomy your pipeline doesn't know. Normalise the string the same way the index does — lowercase, collapse non-alphanumerics to single spaces, trim — then look it up.

javascript
const norm = s => s.toLowerCase().replace(/[^a-z0-9]+/g, ' ').trim();

const { aliases } = await fetch(
  'https://analysisongoing.netlify.app/api/aliases.json'
).then(r => r.json());

const hits = aliases[norm('IRON RITUAL')];
// [{ slug: 'apt29', name: 'APT29', matched: 'IRON RITUAL',
//    kind: 'alias', org: 'secureworks' }]

const profile = await fetch(
  `https://analysisongoing.netlify.app/api/actors/${hits[0].slug}.json`
).then(r => r.json());

Who exploited a given CVE

bash
curl -s https://analysisongoing.netlify.app/api/cves/CVE-2021-44228.json \
  | jq -r '.cve.exploitedBy[] | "\(.firstExploited)  \(.name) (\(.country))"'

# 2021-12-01  APT40 (China)
# 2021-12-13  APT41 (China)
# 2022-01-01  MuddyWater (Iran)
# ...

Building an ATT&CK Navigator layer

Each profile page offers a one-click Navigator layer download, but the raw technique list is in the API if you'd rather generate your own — for example to diff two actors' coverage.

bash
curl -s https://analysisongoing.netlify.app/api/actors/sandworm.json \
  | jq '[.actor.techniques[] | {techniqueID: .tCode, score: 1, comment: .note}]'

Notes on use

  • Stability

    Slugs and CVE IDs are stable identifiers and safe to key on. Actor display names occasionally change when a vendor retires a designator; slugs do not.

  • Currency

    Every response carries datasetCurrentAsOf (currently 2025-09-01) and a generated timestamp. Check them — a stale dataset that looks live is worse than no dataset.

  • Caching

    Files are served with a one-hour cache header. They only change when the site is rebuilt.

  • CORS

    Access-Control-Allow-Origin is *, so browser-side fetches work without a proxy.

  • Attribution

    If you build on this, cite the underlying primary sources rather than this site — every record carries them for exactly that reason.