API
Every number on GCD AI RADAR comes from static files anyone can fetch: no key, no sign-up, open to scripts on any site. This page lists each file with its fields, says how often it changes, and states the counting rules a figure must keep when it is carried elsewhere. Carry the numbers, cite the site and link back.
stable since 2026-10-18 · change policy
Quick start
For the headline figures, fetch summary.json. As of this build it says 117 incidents in the 12 months from 2025-11 to 2026-10, of which 85 real-world.
curl -s https://gcd-ai-radar.vercel.app/data/summary.json
curl -s https://gcd-ai-radar.vercel.app/data/summary.json | jq '.trailing_12'
curl -sO https://gcd-ai-radar.vercel.app/data/incidents.csvconst res = await fetch("https://gcd-ai-radar.vercel.app/data/summary.json");
const s = await res.json();
const t = s.trailing_12;
console.log(`${t.incidents} AI cybersecurity incidents from ${t.from} to ${t.to}`
+ ` (${t.real_world} real-world), source: GCD AI RADAR, https://gcd-ai-radar.vercel.app`);Files
All paths are on https://gcd-ai-radar.vercel.app. Stable files follow the change policy; internal files serve this site’s own pages and may change shape without notice.
- summary.jsonThe headline numbers and the last 24 months, in one small file. Start here for a widget or a newsletter line.5 KB
- meta.jsonRecord counts per year, last collector run, generation time and licence.2 KB
- incidents.jsonEvery published record with all fields and every source: incidents and disclosed vulnerabilities together.2.4 MB
- incidents.min.jsonEvery record without the sources array, with publishers and a disclosed flag.1.4 MB
- incidents.csvOne row per record, with a record_kind column; list fields joined with "; ".1.2 MB
- years/index.jsonThe years of first report that have records, with counts and file paths.1 KB
- years/<year>.jsonThe full records first reported in one year.
- incidents/<slug>.jsonOne full record; the slug is the one in /incidents/<slug>.
- feed.xmlRSS 2.0: the 50 most recently reported incidents (no disclosed vulnerabilities).36 KB
- og/<name>.png1200×630 chart images: default.png, monthly.png, types.png, categories.png, targets.png, severity.png.50 KB
- slim/<year>.jsoninternalSlim records per year, what the incidents explorer loads. Internal.
- rows/<year>.jsoninternalCompact rows per year, what the dashboard's lists load. Internal.
- search-index.jsoninternalToken index for the explorer's search box. Internal.199 KB
Updates and caching
- The collector runs every six hours, at 17 minutes past 00, 06, 12 and 18 UTC. A run that finds something commits to the repository, and every commit redeploys the site, which regenerates every file on this page.
- Each JSON file says when it was made: generated_at for the deploy and last_collector_run for the data. Polling more than once an hour gains nothing.
- Records change after publication: sources are added, severities rise when exploitation is confirmed, duplicates are merged. Refetch rather than append.
| path | response headers |
|---|---|
| /data/* | Access-Control-Allow-Origin: * Cache-Control: public, max-age=3600, stale-while-revalidate=86400 |
| /feed.xml | Access-Control-Allow-Origin: * Cache-Control: public, max-age=3600, stale-while-revalidate=86400 |
| /og/* | Cache-Control: public, max-age=3600, stale-while-revalidate=86400 |
Read from the site’s vercel.json. Every response also carries the site’s security headers; everything except /embed refuses to be framed.
summary.json
The headline numbers, computed by the same functions as the dashboard on the same records. Months are placed by incident date; incidents known only to the year count in totals but in no month or window. Percent changes are rounded to a tenth, and are null when the earlier period had none (the dashboard shows that as “new”).
| field | type | meaning |
|---|---|---|
| version | 1 | Bumped only for a breaking change, with a change-log entry. |
| generated_at | string (RFC 3339) | When the deploy generated the file; the windows end in this month. |
| site | string (URL) | The site's base URL, for the link back. |
| last_collector_run | string (RFC 3339) | null | When the collector last finished a run. |
| date_basis | "incident_date" | Months are placed by incident date, the site's default axis. |
| counting_rule | string | The disclosed-vulnerability rule in one sentence. |
| totals | { records, incidents, real_world, disclosed_vulnerabilities } | All-time counts. real_world is incidents of severity 3 or 4. records = incidents + disclosed_vulnerabilities. |
| this_month | { month, incidents, last_month, delta_pct } | The current month so far against the whole of last month. |
| year_to_date | { year, incidents, last_year_same_period, delta_pct } | This calendar year to date against the same span of last year. |
| trailing_12 | { from, to, incidents, real_world, prior, delta_pct } | The last 12 months including the current one; prior is the 12 before. Months are YYYY-MM, inclusive. |
| trailing_24 | { from, to, incidents, real_world, prior, delta_pct } | The same for 24 months. |
| provisional_from | string (YYYY-MM) | First month still provisional (within 60 days of generation): its count will rise. |
| monthly | { month, incidents, real_world, disclosed_vulnerabilities, provisional }[] | The last 24 months, oldest first, ending with the current month. Sums to trailing_24. |
| license | string | "CC BY-SA 4.0". |
| license_url | string (URL) | The licence text. |
| cite_as | string | A citation line; replace <date> with the date you accessed the data. |
{
"generated_at": "2026-10-19T13:12:44.504Z",
"trailing_12": {
"from": "2025-11",
"to": "2026-10",
"incidents": 117,
"real_world": 85,
"prior": 158,
"delta_pct": -25.9
},
"monthly": [
{
"month": "2026-09",
"incidents": 9,
"real_world": 7,
"disclosed_vulnerabilities": 200,
"provisional": true
},
{
"month": "2026-10",
"incidents": 25,
"real_world": 9,
"disclosed_vulnerabilities": 105,
"provisional": true
}
],
"…": "…"
}Record fields
Full record
In incidents.json, years/<year>.json and incidents/<slug>.json. Types are generated from the validation schema, so a record that does not match them is never published. Definitions of each value are on the methodology page.
| field | type | meaning |
|---|---|---|
| id | string | Stable identifier, AIR-<year>-<number>. Never reused. |
| slug | string | URL slug; the record's page is /incidents/<slug>. |
| title | string (≤ 160 chars) | Headline written for the record. |
| summary | string (≤ 700 chars) | Two sentences in the site's own words, never quoted article text. |
| type | "attack_on_ai" | "ai_enabled_attack" | Attack on AI, or AI-enabled attack. |
| categories | enum[] (1–4 items) | Ordered by relevance; the first is the primary category, which monthly counts use. prompt_injection · jailbreak · agent_hijack · data_leak · model_theft_extraction · supply_chain · data_poisoning · adversarial_evasion · infra_vuln · deepfake_fraud · ai_generated_malware · ai_phishing_social_engineering · ai_assisted_intrusion · other |
| severity | 1 | 2 | 3 | 4 | 1 research, 2 disclosed vulnerability, 3 confirmed exploitation, 4 confirmed real-world loss. 3 and 4 are real-world. |
| targets | enum[] (1–6 items) | Controlled list of who or what was hit. OpenAI · Anthropic · Google · Microsoft · Meta · Mistral · xAI · Amazon · Apple · open_weight_model · framework_or_tooling · enterprise_deployment · individual_victims · n/a |
| products | string[] (0–10 items) | Free-text product names. |
| incident_date | string | When it happened: YYYY, YYYY-MM or YYYY-MM-DD. |
| incident_date_precision | "day" | "month" | "year" | How precise incident_date is. |
| first_reported | string (YYYY-MM-DD) | Earliest source date. |
| sources | object[] (1–∞ items) | The reporting: one entry per article, linking to the original. |
| sources[].url | string (URL) | Link to the original article or advisory (http or https only). |
| sources[].publisher | string | Publisher name. |
| sources[].title | string | The article's own title. |
| sources[].published_at | string (YYYY-MM-DD) | Publication date. |
| source_count | integer | Always equals the length of sources. |
| confidence | number (0–1) | Classifier confidence, 0 to 1. |
| status | "published" | "review" | "rejected" | Always "published" in the public files. |
| origin | enum | Where the record came from; seed_* are historical backfills. seed_aiid · seed_atlas · seed_avid · seed_nvd · seed_ghsa · news · manual |
| tags | string[] (0–20 items) | Free-text tags. |
| created_at | string (RFC 3339) | When the record was created. |
| updated_at | string (RFC 3339) | When the record last changed. |
Slim record
In incidents.min.json: the full record without sources, confidence, status, tags and timestamps, plus two fields.
| field | type | meaning |
|---|---|---|
| id | string | As in the full record. |
| slug | string | As in the full record. |
| title | string (≤ 160 chars) | As in the full record. |
| summary | string (≤ 700 chars) | As in the full record. |
| type | "attack_on_ai" | "ai_enabled_attack" | As in the full record. |
| categories | enum[] (1–4 items) | As in the full record. prompt_injection · jailbreak · agent_hijack · data_leak · model_theft_extraction · supply_chain · data_poisoning · adversarial_evasion · infra_vuln · deepfake_fraud · ai_generated_malware · ai_phishing_social_engineering · ai_assisted_intrusion · other |
| severity | 1 | 2 | 3 | 4 | As in the full record. |
| targets | enum[] (1–6 items) | As in the full record. OpenAI · Anthropic · Google · Microsoft · Meta · Mistral · xAI · Amazon · Apple · open_weight_model · framework_or_tooling · enterprise_deployment · individual_victims · n/a |
| products | string[] (0–10 items) | As in the full record. |
| incident_date | string | As in the full record. |
| incident_date_precision | "day" | "month" | "year" | As in the full record. |
| first_reported | string (YYYY-MM-DD) | As in the full record. |
| source_count | integer | As in the full record. |
| publishers | string[] | Distinct publishers of the sources, in source order. Replaces the sources array. |
| origin | enum | As in the full record. seed_aiid · seed_atlas · seed_avid · seed_nvd · seed_ghsa · news · manual |
| disclosed | boolean | True for a disclosed vulnerability (a CVE or advisory with no confirmed exploitation). Count only records where this is false. |
incidents.csv
Columns in this order. List fields (categories, targets, products, tags) are joined with “; ”. record_kind is incident or disclosed_vulnerability; first_source_url is the first source’s link and url the record’s page here. owasp_llm and atlas list the OWASP LLM Top 10 entries and MITRE ATLAS techniques an incident maps to; they are derived from its type and categories, not classified per record (standards mapping), and meta.json names the editions. A cell that a spreadsheet would run as a formula is prefixed with a single quote.
id, slug, title, type, categories, severity, targets, products, incident_date, incident_date_precision, first_reported, source_count, confidence, origin, record_kind, owasp_llm, atlas, tags, summary, first_source_url, url
meta.json
| field | type | meaning |
|---|---|---|
| generated_at | string (RFC 3339) | When the deploy generated the files. |
| site | string (URL) | The site's base URL. |
| records | integer | Every published record: incidents plus disclosed vulnerabilities. |
| incidents | integer | Records that count as incidents. |
| disclosed_vulnerabilities | integer | CVEs and advisories with no confirmed exploitation; not incidents. |
| counting_rule | string | The disclosed-vulnerability rule in one sentence. |
| years | object[] | Per year of first report: year, records, incidents, disclosed_vulnerabilities, file (the years/<year>.json path). |
| last_collector_run | string (RFC 3339) | null | When the collector last finished a run; null before the first run. |
| standards_mapping | { owasp_llm_top_10, mitre_atlas_release, rule } | The OWASP LLM Top 10 edition and MITRE ATLAS release the CSV's owasp_llm and atlas columns refer to. Those columns are derived from type and categories, not classified per record. |
| license | string | "CC BY-SA 4.0". |
| license_url | string (URL) | The licence text. |
years/index.json
| field | type | meaning |
|---|---|---|
| generated_at | string (RFC 3339) | When the deploy generated the files. |
| years | object[] | Newest year first: year, records, incidents, disclosed (disclosed vulnerabilities), file. |
feed.xml
RSS 2.0. Each item has the title, a link to the record’s page, the record id as a non-permalink guid, the first-report date as pubDate, a description (summary, severity, type, source count) and one category per incident category.
Internal files
rows/<year>.json holds compact rows (id, slug, title, incident_date, incident_date_precision, first_reported, type, categories, severity, targets, source_count, disclosed, product); slim/<year>.json holds slim records; search-index.json maps title, id and product tokens to records. They exist for this site’s pages and are not covered by the change policy. Use the stable files instead.
Counting rules
A figure carried from these files means what it means here only if it keeps these rules. Quoted from the methodology:
- One real-world event is one incident, however many articles cover it. Each article becomes a source on that incident.
- Disclosed vulnerabilities, CVEs and advisories with no confirmed exploitation, are never part of an incident count, chart, tile or the RSS feed. The dashboard shows them as a separate monthly series.
- Monthly charts count each incident once, under its primary category.
- The default time axis is the incident date (when it happened, approximate dates allowed). A toggle switches to first reported (earliest source date). Incidents known only to the year are excluded from monthly series but counted in totals.
- The most recent two months are marked provisional: reporting lags events, so the latest bars will rise.
- “This month” compares the current month so far with the whole of last month; “year to date” compares the same calendar span of the previous year.
- Severity 3 and above is what the site calls real-world.
Disclosed vulnerabilities. A record is a disclosed vulnerability when its severity is below 3 and it either comes from the NVD or GitHub advisory backfills or every one of its sources is a vulnerability database. Anything reported by the press or by researchers counts as an incident.
In code: severity < 3, and either origin is seed_nvd or seed_ghsa, or every source’s publisher is one of “NVD”, “GitHub Advisory Database”, “MSRC”. summary.json, meta.json and feed.xml already apply it; incidents.min.json carries it as disclosed and incidents.csv as record_kind. incidents.json and the year files hold both kinds without a flag, so apply the rule before counting them.
Full context, including the known limits of the numbers, is on the methodology page.
Embed
A card with the trailing count, its change, the real-world count and the monthly bars, linking back here. It is plain HTML that renders without JavaScript; with JavaScript it also fetches summary.json, so a copy cached by your page still shows the latest figures.
<iframe src="https://gcd-ai-radar.vercel.app/embed?theme=dark&window=12m"
title="GCD AI RADAR: AI cybersecurity incidents, last 12 months"
width="360" height="260" loading="lazy"
style="border:0;max-width:100%"></iframe>
<noscript><a href="https://gcd-ai-radar.vercel.app">GCD AI RADAR: AI cybersecurity incidents</a></noscript><iframe src="https://gcd-ai-radar.vercel.app/embed?theme=light&compact=1"
title="GCD AI RADAR: AI cybersecurity incidents, last 12 months"
width="360" height="150" loading="lazy"
style="border:0;max-width:100%"></iframe>
<noscript><a href="https://gcd-ai-radar.vercel.app">GCD AI RADAR: AI cybersecurity incidents</a></noscript>| option | values | effect |
|---|---|---|
| theme | dark (default) | light | Colours for a dark or a light page. |
| window | 12m (default) | 24m | The trailing window counted and drawn, compared with the window before it. |
| compact | 1 | One line of figures without the bars: 150 px tall at 320 px wide, 100 px from 480 px wide. |
Any other value falls back to the default. The card fills the iframe: 260 px tall fits every width from 320 px, and from 480 px wide, where the bars sit beside the figures, 190 px is enough.
Attribution
The data is CC BY-SA 4.0. Reuse it, including commercially, as long as you credit GCD AI RADAR with a link and the date you accessed it, say if you changed it, and release a derived dataset under the same licence. Upstream sources carry their own notices, listed on the data page; keep them when you redistribute records.
GCD AI RADAR, https://gcd-ai-radar.vercel.app (accessed YYYY-MM-DD). Data licensed CC BY-SA 4.0.Source: <a href="https://gcd-ai-radar.vercel.app">GCD AI RADAR</a>, accessed YYYY-MM-DD.
Data licensed <a href="https://creativecommons.org/licenses/by-sa/4.0/">CC BY-SA 4.0</a>.Stability and changes
Stable since 2026-10-18.
- New fields may appear in any stable file at any time. Read the fields you need and ignore the rest.
- Existing fields are not renamed, removed or given a new meaning without an entry in the change log below.
- A breaking change to summary.json also raises its version.
- Counts change when the data does: records are added, merged and reclassified. A change to how things are counted goes in the methodology’s change log.
- Internal files are not covered.
Change log
- 2026-10-18 · API documented. summary.json added. /embed card added. Existing files and fields documented as they were; nothing renamed.