Most teams treat MITRE ATT&CK as a poster on the wall — a matrix you glance at during a tabletop exercise. But ATT&CK is machine-readable at its core, and once you realize that, you can bake it directly into your detection pipeline, your SIEM enrichment, your reporting, or your product's UI. This is the part nobody explains clearly: you don't scrape the website. ATT&CK ships as structured data, and there are three clean ways to consume it depending on how fresh and how offline you need to be.
First, understand what you're actually pulling
ATT&CK is published as STIX 2.1 — a standardized JSON format for threat intelligence. Techniques, tactics, groups, software, and mitigations are all STIX objects, and the relationships between them (this group uses this technique) are objects too. The ATT&CK STIX data can be accessed via the official ATT&CK TAXII server, which is an application protocol for exchanging cyber threat intelligence over HTTPS. So the mental model is: STIX is the data, TAXII is the delivery pipe, and you pick how you tap into it.
In STIX terms, a technique is an attack-pattern object, a group is an intrusion-set, software is malware or tool, and "who uses what" is a relationship object. Once you internalize that vocabulary, the rest is just querying.
Option 1: Pull live from the official TAXII 2.1 server
MITRE runs a TAXII 2.1 endpoint at attack-taxii.mitre.org with the API root at /api/v21/. TAXII exposes three service types worth knowing: Discovery tells you the available API roots, the API Root lists collections, and a Collection is a logical grouping of STIX objects you fetch over GET. Here's the minimal Python to connect and pull Enterprise techniques:
from taxii2client.v21 import Server
API_ROOT = "https://attack-taxii.mitre.org/api/v21/"
server = Server(API_ROOT)
root = server.api_roots[0]
# Find the Enterprise ATT&CK collection
enterprise = next(c for c in root.collections if "Enterprise" in (c.title or ""))
col = root.collection(enterprise.id)
# Pull a page of objects (paginate for the full set)
bundle = col.get_objects(limit=200)
techniques = [o for o in bundle.get("objects", []) if o.get("type") == "attack-pattern"]
for t in techniques[:10]:
tid = next((r.get("external_id") for r in t.get("external_references", [])
if r.get("source_name") == "mitre-attack"), None)
print(tid, "-", t.get("name"))
One critical gotcha before you build a cron job around this: the TAXII server is rate limited to 10 requests per 10-minute period per source IP, and MITRE recommends downloading the STIX/JSON bundles and parsing them directly if you need to query more frequently. So TAXII is great for a nightly sync, terrible for per-request lookups in a hot path.
Option 2: Use mitreattack-python for real querying
If you actually want to ask questions of the data — "what techniques does this group use," "what mitigations map to this technique" — raw STIX parsing gets painful fast. MITRE's own library solves this. The mitreattack-python library provides the ability to query the dataset for objects and their related objects, and its MitreAttackData class reads in a downloaded STIX bundle so you're working offline against a pinned version.
from mitreattack.stix20 import MitreAttackData
# Point at a downloaded enterprise-attack.json bundle
mad = MitreAttackData("enterprise-attack.json")
# Look up a technique by its ATT&CK ID
technique = mad.get_object_by_attack_id("T1059", "attack-pattern")
print(technique.name)
# Get all techniques used by a group (e.g. APT29)
group = mad.get_groups_by_alias("APT29")[0]
used = mad.get_techniques_used_by_group(group.id)
print(f"{len(used)} techniques mapped to the group")
Pinning to a downloaded bundle is the move for anything in production: your results are reproducible, you're not rate limited, and you upgrade ATT&CK versions deliberately instead of having the matrix shift under you mid-incident. Note the library reads STIX 2.0 content for this module, so grab the matching bundle from MITRE's data repo.
Option 3: Emit Navigator layers for visualization
The fastest way to make ATT&CK data visible to humans without building your own matrix UI is to generate a Navigator layer. Layer files are saved as easy-to-parse, easy-to-generate JSON so ATT&CK data can be used in other applications and generated by tools for import into the Navigator. Your software scores techniques — coverage, detections seen, an incident's observed TTPs — and dumps a JSON file your analysts drop straight into the free Navigator web app.
import json
layer = {
"name": "Detection Coverage - Q1",
"versions": {"attack": "14", "navigator": "4.9.1", "layer": "4.5"},
"domain": "enterprise-attack",
"techniques": [
{"techniqueID": "T1059", "score": 100, "comment": "Full coverage"},
{"techniqueID": "T1566", "score": 50, "comment": "Partial - email only"},
],
"gradient": {"colors": ["#ff6666", "#ffe766", "#8ec843"], "minValue": 0, "maxValue": 100},
}
with open("coverage.json", "w") as f:
json.dump(layer, f, indent=2)
Upload that through the Navigator's "Open Existing Layer" button and you get an instant heatmap of your coverage — red gaps, green wins — for free, with zero frontend work on your side.
How to choose
- Need always-current data and low volume? Hit the TAXII server on a schedule, respecting the rate limit.
- Building features that query relationships? Pin a STIX bundle and drive it with mitreattack-python.
- Just need to show humans a picture? Generate Navigator layer JSON and let the existing app render it.
My default for a product: pin a bundle, wrap mitreattack-python behind a small internal service, and expose Navigator-layer export as a feature. You get reproducibility, no rate-limit surprises, and a visualization path that costs you nothing to maintain. That's the whole game — ATT&CK was designed to be integrated, not just admired.