Back to GDELT Cloud
Last updated August 22, 2026

Changelog

Product-meaningful changes to the GDELT Cloud platform. Internal fixes and small refactors are tracked in git; this page covers changes integrators may need to know about.

2026-08-22

Changed — /api/v2/events/summary and /api/v2/stories/summary now reject a query parameter they do not declare, with 400 UNKNOWN_PARAM and a suggested spelling, exactly as their list twins have. Both endpoints published a strict contract that nothing enforced: `?countries=NGA` — one character off `country` — returned HTTP 200 and the WORLD's 57,806 events where Nigeria's figure was 2,955, while /api/v2/events answered 400 for the identical typo. `applied_filters.ignored` did report it, but only to a caller who reads that field.

Changing for integrators — five parameters the two summaries accepted and never read now return 400 UNSUPPORTED_PARAM naming the list endpoint instead: `sort`, `cursor`, `offset`, `include_images` and `include_entity_images`. A summary returns buckets, so there was nothing for them to order, page or illustrate; being on the allowlist meant they were echoed back as APPLIED, so paging a summary with `offset` returned page one every time under a response that agreed the offset had been used. `limit` still bounds the number of buckets. In the other direction, `languages`/`language` were applied by both summaries all along and are now declared, documented and generated like every other filter.

Changed — an entity filter whose material attribution is not yet built for the requested window now returns broad story coverage and SAYS SO, rather than failing. `applied_filters` carries `entity_match: "coverage"`, `coverage_fallback_applied: true` and a sentence explaining what the rows are. Sending `entity_match=material` explicitly still returns 503 ENTITY_ATTRIBUTION_UNAVAILABLE — if you asked for material by name you get a straight answer, not a substitute.

Fixed — the same unavailability could also produce an empty HTTP 200. The availability check sat behind entity resolution, and entity resolution is scoped to the requested window, so an entity with no coverage in that window skipped the check entirely and received zero rows next to our own note saying the filters were understood and nothing matched. Whether a capability is available never depended on the entity; it now cannot depend on how much news that entity made.

Fixed — /api/v2/events/summary returned an untyped 500 INTERNAL_ERROR where /api/v2/events returned 503 ENTITY_ATTRIBUTION_UNAVAILABLE with `details.retryable` for the same condition. A retry policy written against one surface behaved wrongly against the other. Every v2 endpoint now answers that condition identically.

Fixed — `include_total=1` produced no `pagination.estimated_total`, was echoed back as though it had been applied, and never appeared in `applied_filters.ignored`; `include_total=True` was accepted by the contract and then silently dropped. Booleans are `true` or `false` in any casing, and anything else is now a 400 INVALID_BOOLEAN that says so. If you were sending `1`, send `true`.

Fixed — the MCP `summarize_events` and `summarize_stories` tools reported the per-event AVERAGE under the field name `article_count`, which is the total everywhere else. For 2026-07-29 an agent read 2.589 where the REST bucket says 321; over a 30-day window, 89 articles against 8,399. Both numbers are now published, under the names REST uses. Three neighbours went with it: story summary buckets reported no `count` at all, the four CAMEO+ metrics were dropped from every digest, and a nested aggregate was emitted where a number was promised.

Added — a published API stability and deprecation policy, at docs.gdeltcloud.com/reference/stability. At least 30 days' notice before a breaking change or an endpoint removal; what changes without notice, and why data corrections are on that list; what deprecated, retired and removed each mean and how to tell them apart from a response. Its inventories are generated from the same contract the API enforces, so the page cannot outlive its subject.

Added — the changelog is now an RSS feed at /changelog/feed.xml, linked from this page's head. It was previously the only channel for a breaking change and the only way to read it was to remember to visit it.

Fixed — the parameter reference miscounted which endpoints share a parameter, because a name accepted on one endpoint and rejected on another was recorded once and judged by whichever it met first. `as_of` was missing from the shared table entirely and several geography parameters were overcounted.

2026-08-21

Updated — Energy Data now serves the July 2026 Global Coal Plant Tracker and August 2026 Global Energy Ownership Tracker. The refresh contains 14,674 coal units representing 5,493,824 MW and 26,943 ownership entities; both releases were loaded through validated staging tables and atomic warehouse exchanges.

Changed — entity filters on Events and Stories now mean material involvement by default. Every returned row carries the role, decision, confidence, resolution method and supporting evidence that caused it to match. The former broad Story co-occurrence behavior remains available explicitly as entity_match=coverage; the API never silently falls back to it.

Changed — event_date is the evidenced occurrence date. Events now separately disclose observed_at, event_date_basis and event_date_evidence, and unknown fatalities or canonical Story article counts remain null instead of becoming zero.

Changed — Facilities now default to one canonical physical site. A site reports its source registry units, a status breakdown and mixed when those units conflict; granularity=unit exposes the source rows, and a legacy f_ unit id resolves to its s_ parent site.

Changed — Screening separates candidate-retrieval score from identity confidence. Exact verified identifiers and aliases are deterministic; gray candidates are independently adjudicated and remain inconclusive until calibrated, while rejected candidates are not counted as matches. min_match_confidence is canonical and threshold is a deprecated alias for one compatibility period.

Added — Alerts can target one or more terminal entity IDs, a Facility, or an explicit latitude/longitude/radius. Entity alerts use material matching and collapse duplicate incidents by default, and the preview reports query units from the actual number of alert lanes.

Changed — descriptor-backed endpoint families now return 400 UNKNOWN_PARAM for undeclared query parameters, with a suggested spelling. OpenAPI, docs and Arena fields are generated from those executable descriptors; supported deprecated aliases remain declared during their compatibility period. Remaining legacy families are explicitly inventoried and cannot be described as contract-complete until they adopt the same runtime.

2026-08-20

Fixed — a gated source flag on /api/v2/search is now absent rather than false when you are not entitled to it. Yesterday's note said `gov` "reports availability only to callers entitled to it"; that was the intent and the published spec, but not what the code did. It set the flag to `false`, which is an answer — it says we looked and the entity has no such records — when in fact we had not looked on your behalf. A caller on Professional searching for Anduril received `sources.gov: false` on RTX Corporation, which holds 111 current federal awards worth $44.3B. The same row on an entitled key returns `true`. This affected `sec`, `sanctions`, `china` and `epoch` in the same way and for longer; `gov` is simply the one that made it visible.

Changing for integrators — test with `'gov' in sources`, not `sources.gov === false`. An absent flag now means the source was not checked for you; it never means the entity has no records there. If you were treating `false` as evidence of absence on any of the five gated sources, that reading was wrong before this change and is impossible after it. Nothing that was `true` has changed, entitled callers see exactly what they saw, and the identifiers were already being withheld correctly — it was only the flag that disagreed.

Changed — entity risk scores are recalibrated, and a risk series that spans today mixes two calibrations. The tone and risk scorer moved to a stronger model tier (it applies a seven-band rubric with worked examples, and was running on a tier below the one that rubric was written and measured for). Scored on identical evidence, the new tier returns risk about 7 points lower on a 0-100 scale, a difference that is statistically real rather than noise; tone scores are unchanged. Rows written before 2026-08-20 21:30 UTC carry the old calibration and have not been rewritten — they are not wrong, they are a different instrument. If you compare entity risk across that boundary, expect a step, and prefer comparing within one side of it until the history is re-scored.

Fixed — the loader-audit panel in the ingest console had never worked, in any environment. A column alias shadowed the column two aggregates sorted by, so the query failed on every call and an over-broad error handler reported it as "this table has no load audit". Eleven data lanes showed no loader status where they should have shown a pass and a date; twenty-eight now report one. This is an internal console fix with no API surface.

2026-08-19

Added — /api/v2/search now reports a `gov` source. A hit carries `sources.gov: true` when the entity has a US government identifier (its SAM UEI, the key USAspending awards resolve on) or a government record (a federal award, or a FARA registration on either side of the foreign-agent relation). Anduril Industries is the case that prompted it: the response already returned `US_SAM_UEI: KC3CH2MSK7Q3` among its identifiers while the source map said it carried no government data. Like the other paid sources, `gov` reports availability only to callers entitled to it, and the UEI is redacted alongside the flag rather than left behind. EPA ECHO enforcement is deliberately not part of this signal — operator-name attribution over-matches, and a source flag is an attribution claim.

Fixed — the Unified Search box in the API Arena. `q` is the parameter the endpoint cannot run without, and it was rendering inside the collapsed Refine section: you opened the endpoint that is a search and saw no search field. The universe selector, which decides which corpora are searched at all, was collapsed with it. Both now sit at the top of the form. The same omission had buried the primary input on Screening Match, Facilities and Energy Owners; those are fixed too. This is a console fix only — no API behaviour changed.

2026-08-20

Fixed — an event id that was merged away now tells you what replaced it. When two coded events are adjudicated duplicates we keep one and remove the other, and until now the removed id simply stopped resolving: /api/v2/events/{id} returned a bare 404 and the public event page showed a not-found. There was no way to find the surviving event, so an integration polling a stored id just saw it go dark — one of them polled the same retired id 133 times over six days. That id now returns 404 with code EVENT_MERGED and a successor_event_id you can follow, and /events/{slug} redirects to the surviving event instead of dying. EVENT_WITHDRAWN still means what it always meant: the event was suppressed and there is no replacement.

Fixed — the probe behind that answer could not see the most common case. It looked for the retired id on the link table, but a merge re-points that column to the surviving event, so a merged-away id appeared nowhere and was indistinguishable from a typo. It now also matches the id the link records as its origin.

Fixed — when two events covering the same incident were merged, we could keep the wrong one. The choice was made on accumulated evidence — how many clusters an event had absorbed, how many articles its story held — without checking whether that story was still live. On 2026-08-20 the Evergrande sentencing existed as two stories; the story layer correctly merged them at 05:54, and the event merge ran eight hours later and kept the event belonging to the story that had been superseded and held zero articles, deleting the event of the live 85-article story. Whether an event's story is still live is now the first thing the comparison asks. Where both are live nothing changes.

Note — ids that were retired before this shipped can be recovered from links we already store, and that history is being backfilled. A small number cannot be: about 1 in 75 retired ids maps to more than one surviving event, and there the honest answer is still a 404 rather than a guess.

Added — an event can now name more than one actor on a side. A court that sentences a founder and in the same ruling fines two companies has three targets, and until now we recorded one: the other two simply were not in the data, so filtering for corporate legal action against either company did not find the event. `actors[]` now carries them all, each with its own country. Every actor gained a `primary` field — true for the one entity the event is chiefly about on that side, which is the one `source_actor` and `target_actor` and every existing filter still refer to. Nothing that worked before changes; the array is simply no longer capped at two.

Note for anyone reading actors[] — it could previously hold at most two entries, and some code assumes that. It can now hold more. Conflict-family events are unaffected: they record associated actors differently and are unchanged. Co-actors are recorded going forward, not backfilled, so events coded before today carry none.

Measured before shipping — 24 real clusters coded twice with the feature off and once with it on, from identical cached source text. Every field we already published (scope, event code, domain, the primary actors, the title) moved LESS between off and on than it moves between two identical off runs, so there is no measurable change to anything that existed. Co-actors appeared on 16% of events; every one was checked by hand.

Fixed — an actor that failed to resolve because of a network timeout stayed unresolved forever. When we link an event's actors to entities, the answer is cached so a miss does not cost a slow lookup on every run. But a timeout is not an answer about the actor — it is a statement about the network at one moment — and it was being kept as though it were permanent. 7,866 actors were in that state; a sample found roughly one in five is a real, resolvable entity, including public figures who simply happened to be looked up during a blip. Those are now re-checked weekly. Verdicts that genuinely are a property of the name, such as "this is a job title, not an entity", are unchanged and still cached indefinitely.

Not changed, and worth saying — an actor that was cached as unknown to our entity registry is still cached that way. Re-checking those looked cheap and measured out otherwise: it would push a fifth of the work back onto the slowest stage of the pipeline for a yield under one in a thousand, and because a resolution is kept permanently once made, a wrong one would be too. That needs a precision measurement first.

Changed — QU top-up pricing has doubled on Builder, Analyst and Intelligence. Builder is now $12 per 250 QU, Analyst $60 per 2,000, Intelligence $160 per 10,000. Top-ups are an overflow valve, not a cheaper way to buy volume, and the old rate had drifted well below the in-plan rate on every tier. The maximum of three top-ups per period is unchanged, so the ceiling on an unexpected bill still sits at roughly a third of plan spend, and you can turn top-ups off entirely from your subscription page.

Fixed — semantic search now tells us when it breaks. `search=` on /api/v2/events, /api/v2/stories and /api/v2/search embeds your query before it runs, and when that upstream call failed the endpoint returned a correct 503 and nothing alerted anyone — so the feature could stay down for a day or more. It now pages us the moment the cause is one only we can fix, and the underlying reason is recorded rather than collapsed into a single generic message.

Fixed — /api/v1/entity-geg returned a bare 404 instead of 410 Gone. Every other retired path names its replacement in the message, in details.replacement and in an RFC 8594 Link header; this one was inventoried when the v1 surface was retired and never got its stub, so the handful of callers still hitting it got an answer indistinguishable from an outage. It now points at /api/v2/entities/{entity_id}.

Fixed — a plan gate reached through the MCP was recorded as an error rather than as a plan restriction. This never changed what you received (still a 403 naming the feature and the upgrade path); it only affected our own reliability reporting, where upgrade prompts were being counted as platform failures.

2026-08-18

Added — bulk downloads. Every coded event we serve is now also a file: one per calendar month plus a rolling full-history cut, in Parquet and gzipped CSV, at /downloads or through /api/v2/bulk/files. 172,213 events across 171 settled days, March 2026 to now. It is the same table /api/v2/events reads, so a row in a file and the same row from the API agree field for field — we check that on six dates spanning the range before publishing, comparing the exported projection against the live serve path row by row. Included on Intelligence, Enterprise and the new Academic plan.

Added — an Academic & Research plan. Free, verified access for individual researchers and journalists: the full data surface at 10,000 query units a month, including bulk downloads. It is invite-only rather than purchasable — request it from your subscription page and a person reads every one. Previously this was a card on the pricing page whose only mechanism was an email address.

Changed — the bulk files publish their own coverage rather than implying it. Each one reports settled days against calendar days, so an in-progress month reads 18/31 instead of looking complete, and a sha256 you can verify before loading. A month is only whole when those two numbers agree.

Note for anyone loading the files — a null metric is not a zero. The conflict family carries no magnitude, systemic-importance, propagation or market scores, and the CAMEO+ family carries no fatalities, because those questions were never asked of those events. Coalescing them to 0 on import invents measurements that were never taken. civilian_targeting is three-valued for the same reason: true, false, and null where no coder evaluated it.

Changed — /api/v2/stories/summary is now a Story summary. The 43 linked-event metric rollups (avg/min/max of goldstein scale and severity, magnitude, systemic importance, propagation potential, market sensitivity, confidence, and avg_recency_score) have been removed. A Story has no metrics of its own; those belong to its linked Events, and /api/v2/events/summary is where they live — it supports every group_by this endpoint does, plus two more, and accepts the same entity scope. Every response now carries a notice field naming it.

Fixed — and this is why they were removed rather than moved. Those averages were wrong. A story with no linked events contributed a literal 0 rather than a null, so the not-null guard never fired and each average was spread over every story in the bucket instead of only the stories that had events. Measured over 2026-08-14..16, avg_linked_event_magnitude read 0.4693 where the event-level figure was 2.9316 — understated by 84% — on the default, most-common call. If you were reading those fields, the numbers you had were not the numbers you wanted, and the events endpoint computes them per event, so they will not match what this endpoint used to return.

Kept — everything that is genuinely a property of a Story: story and article counts and their distributions, significance (an article-volume measure, not an event rollup), counts of linked events, stories with events, fatalities, and country/region counts. No request that worked before returns an error now; the response is narrower, not stricter.

Changed — ?country= on Stories now finds Stories with no coded Event. A Story has no coordinates of its own, so the filter was answered entirely through its linked Events, which meant ?country= silently also meant has_events=true and roughly four in five Stories were unreachable by any country filter, in any spelling. Stories now also match on their own attributed country. Over 2026-08-14..16 that took the reachable set from 5,386 to 24,207 of 25,463 Stories — about 4.5x. Nothing that matched before stops matching; this only adds.

Scope — combining country with an Event-scoped filter (event_category, subcategory, admin1, bbox, domain, civilian_targeting) keeps the Event-only definition, because those ask a question about the Story's Events and a Story with none cannot answer it. Attribution begins 2026-07; query a window before that and the filter behaves exactly as it did.

Changed — group_by on /api/v2/stories/summary now buckets Stories by the same country the filter matches on. Previously the filter and the grouping disagreed: you could filter to a country, group by country, and not find it, for exactly the Stories with no coded Event. Two numbers move as a result. On a date bucket, country_count and region_count rise (167 to 193 countries on 2026-08-14) because those Stories now have a country at all. Inside a country bucket, region_count falls sharply — United States read 10, now reads 1 — and that is the correction: a United States bucket holds US Stories, which are in one region; the old number counted the regions of their Events, which can be anywhere.

Faster — date, country, region and continent summaries now read the pre-settled snapshot. Same numbers to within 0.1 to 0.3 percent, and the category and subcategory dimensions are unchanged because those group by linked-Event taxonomy.

Fixed — the docs said /api/v1/* was still supported. It is not, and has not been since 2026-08-12: every v1 path returns 410 Gone naming its v2 replacement in the message, in details.replacement, and in an RFC 8594 Link header. Three pages carried the old claim. /api/v2/events?family=conflict and ?family=cameoplus are strict supersets of the two v1 event routes.

Fixed — the docs said metric_version was stored but not returned by the API. It is returned on the event card whenever it is set, so it is the direct way to tell a v2-scored event from an older one.

Changed — the event metric reference is now one page per metric. /reference/metrics keeps the design rule, significance in full and a summary of each of the four scored metrics; magnitude, systemic importance, propagation potential and market sensitivity each have their own page with the sub-factors, the anchors, the NULL rule and what that metric does not claim. Existing #anchor links still resolve. The cross-cutting caveats that were repeated on every metric — the frameworks we adapt, how thin the registry coverage is, and the run-to-run noise floor — now live once, on /metrics/limits.

Corrected — the coding-latency explanation. It said a Story had to accrete corroboration before it could be coded, and named that as the main reason events are coded after the day they happened. That is not how the pipeline works: single-article clusters are a first-class coding path and are most of what we code. Measuring latency from the earliest contributing article instead of the one the coder read moves the median by 32 seconds. The real causes are the hourly ingest cycle, publication lag in the sources, and the fact that event_date is a date rather than a timestamp. The page now also names observed_start / observed_end, which is the point-in-time filter on the API.

2026-08-17

Removed — the five-key limit on API keys. It was never a plan limit: nothing sold more of them, and nothing in the interface counted down toward it, so you met it as a red error at the moment you were trying to integrate. There is now a safety ceiling of 100 live keys per person per organization, which exists only so a runaway script cannot fill the table. Revoking a key has always freed its slot; an auto-disabled key still occupies one, because it is re-enableable and therefore still yours.

Changed — revoked keys have moved out of the main list on the API Keys page into their own collapsed section. A revoked key is permanent and carries no controls, so it was inventory-shaped dead weight sitting between the keys you actually use. It is still there, still counted, still readable as an audit trail of what was issued — just folded away by default.

Changed — Connect now has three doors instead of two: Plugin, MCP and REST API, in that order. The plugin path (one command in Claude Code or Codex) was previously only documented on a page that no longer exists. The API key panel moved out of the REST section and now sits above all three, because every route in needs the same credential and the previous layout told MCP users to go looking for it under a heading about applications.

Changed — the code samples on Connect are now syntax-highlighted, default to Python, and are generated from the same contract the API validates against, so the endpoint path and parameters in a snippet cannot drift from the ones the service accepts. They also read your key from a GDELT_API_KEY environment variable rather than inlining it, so you can paste them straight into a repository. One correction that came out of it: the FastMCP example called a method that does not exist on the client (get_tools); it is list_tools.

Fixed — the key picker on Connect read from a pre-organization code path, so a member of a shared organization could see a different set of keys there than on the API Keys page. Both now read the same organization-scoped source.

2026-08-16

Added — you can now ask who acted on whom. /api/v2/events and /api/v2/events/summary accept source_actor_country, target_actor_country and actor_country. Until now country was the only geographic filter, and it matches the event's location OR either actor's origin — so "China acting on the West" could not be expressed and had to be reconstructed on your side from each event's actors array. Measured over thirty days: country=CHN returns 1,995 events, of which 129 are China-source against a Western target. You were downloading fifteen rows for every one you kept, and writing the classifier that decided which. The new parameters accept the same four spellings country does — ISO-3, ISO-2, FIPS or an English name — and combine with AND, so source_actor_country=CHN&target_actor_country=USA,GBR,FRA is one call. Direction matters: over that same window the mirror question, the West acting on China, is a different 119 events.

Added — actor_country on its own, for the actor-origin half of country without the location half. Use it when an event that merely happened somewhere, with no actor from that country, is not what you meant.

Added — group_by=source_actor_country and group_by=target_actor_country on /api/v2/events/summary, so "China acted on whom, ranked" is one request rather than paging the list and tallying. Note the Stories summary deliberately does NOT gain these: a Story has no source or target actor of its own, only an undirected set of actor origins across its linked events, so offering them there would be a filter we could not honestly answer.

Added — meta.actor_filter, so what the filter could not judge is a number rather than a silence. About 12% of events carry no coded actor-origin country; under a directional filter those are neither in nor out, and a WHERE clause discards them without a trace. When you send an actor filter the response now reports evaluated, unevaluable and unevaluable_share for your window. The block is absent rather than zeroed when it could not be measured cheaply — an absent key means "not measured", which is a different claim from "nothing was dropped".

Fixed — an event whose two actors share the same name string could report the wrong target country. The snapshot pairs each actor with its country by matching the actor's name, which is right when the two names differ and ambiguous when they do not: the target then matched the source's entry first and inherited the source's country, or lost its own entirely. Found by comparing the two query paths over thirty days — fifteen rows in 14,420 on the target side. Corrected snapshots roll forward automatically on the next rebuild of each date; if you are reading history you may see a small number of target countries change, always toward the coded value.

Note on direction — on CAMEO+ events the source actor is the entity that performed the action. On conflict (ACLED) events the first actor is the PRIMARY actor and not necessarily the initiator, so source_actor_country there means involvement in that role rather than who started it. Worth knowing before building an escalation metric on it.

Fixed — subcategory on /api/v2/events matched nothing for every CAMEO+ code. GET /api/v2/events?category=ECONOMIC&subcategory=EC04 returned HTTP 200 with zero rows for a value the API itself declares valid, and applied_filters reported the filter as applied, so nothing in the response indicated a problem. The snapshot table stores subcategory as the display form 'EC04 · Trade Policy Action' while the request is validated down to the bare 'EC04', and the two were compared directly. It affected 100% of CAMEO+ events and none of the conflict family, whose values happen to be stored bare — which is why a Protests filter worked and every EC, TE, CR or POLITICAL code did not. The summary endpoint agreed with the list only by accident: it shares the same predicate, so subcategory with group_by=country returned zero buckets too. Both are fixed and now reconcile.

Changed — subcategory is now the code, and subcategory_label is the words. It used to serve 'EC04 · Trade Policy Action', which you could not send back: that exact string is rejected, and the code it contains returned nothing. A value you receive should be a value you can filter on, so the field now carries 'EC04' and the readable name moved to a new subcategory_label. If you display subcategory, read subcategory_label instead; if you match on it as a string, you now want the code. Query values are unaffected — label-style values are still accepted. Summary buckets keyed by subcategory changed the same way and gained a label, so a bucket you can see is now one you can fetch.

Fixed — event_description no longer appears on summary buckets that are not grouped by subcategory. It is carried through the aggregation to label CAMEO+ leaf codes, and was being published on every bucket of every grouping, so a POLITICAL bucket of 361 events reported the description of the single highest-significance event in it — reading as a property of the whole bucket.

Added — actors[].role is now documented, with the rule that governs it. The field separates who acted from who was acted upon, and it was published nowhere. Which pair appears is decided by family: CAMEO+ events are directed and carry source and target; conflict (ACLED) events are undirected — two parties to a clash — and carry actor1 and actor2. actor1/actor2 is not a legacy spelling of source/target, so a filter on actor direction cannot return conflict events. match_type and search_score are documented for the first time too: search_score is null off a search= request and also on a literal name match, because a name hit has no computed distance and inventing one would assert a similarity nobody measured.

Added — incident_resolution as a filter. Duplicate adjudication is partial by design (about 7% of events on a typical day), and the guidance to count incidents rather than rows therefore applied to a minority of any result set, with no way to select the rest. Pass incident_resolution=llm,self to get exactly the events where incident.uid is a trustworthy grouping key.

Added — include_total=true returns pagination.estimated_total, so a page can say '45 of 320' rather than only detecting that more exists. It is opt-in because it costs a second scan, and it is counted from the same filters and the same source as the page it accompanies. It returns null rather than a number on a search= request: semantic retrieval works from a bounded candidate pool, so any total there would describe the pool and not your query. Without it, pagination.next_cursor remains the truncation signal — non-null means more rows exist, null means the walk is finished.

Documented — goldstein_scale is null by definition outside two families, not sparsely covered. It is populated for CAMEO+ POLITICAL events and for every conflict event, and is null for the other nine CAMEO+ domains because the scale is not defined there. Measured on one production day that is 100%, 100% and 0% — about 42% overall, which reads like a coverage gap and is not. Worth knowing before you filter: a range filter on goldstein_scale excludes the null rows, so it narrows your results to those two families whether or not you intended it.

2026-08-15

Retired — confidence_profile no longer affects results on /api/v2/events, /api/v2/stories or either summary. It is still accepted, so nothing breaks, and any request that sends it now names it under applied_filters.retired with an explanation. It was never a filter on the confidence score its name suggests: any value other than loose required a story cluster to be graded primary and strong — internal pipeline grades we have never documented — which discarded roughly 61% of retrievable Stories. Worse, that grade is stamped once when a cluster is created and never revised, so a story graded weak in its first minutes and then covered by three hundred articles stayed excluded forever. If you were sending it, you will now see MORE results, not fewer, and those extra results were always real. Use confidence_min for a floor on the coder's per-event confidence score. Two side effects worth knowing: on /events the values balanced, precise and strictest were applying an identical cluster filter and differed only in that numeric floor; and an explicit confidence_min silently overrode the preset, so confidence_profile=strictest&confidence_min=0.1 applied 0.1.

Retired — min_confidence, a second spelling of confidence_min. Also still accepted and also reported under applied_filters.retired. Sitting beside confidence_profile it made three unrelated things look like one feature. Use confidence_min; its behaviour is unchanged. Note this is scoped to the events and stories endpoints — min_confidence remains a live, applied parameter on /api/v2/entities/{entity_id}/tone, where it means what it says.

Changed — the daily snapshots now serve a much wider history, and freshness is reported rather than guessed. Requests were previously refused from the snapshot path for any date older than fourteen days, regardless of whether that snapshot was current — so historical windows always paid the slower live query. A snapshot is now used whenever one exists and is complete, and meta.settled_at tells you when it was built. Measured over the last fourteen days, thirteen dates match the warehouse exactly and the largest difference is 0.43%.

Added — every event now says which real-world INCIDENT it belongs to. event_uid identifies a coded story, not an incident: when one event is covered by two story clusters, it is coded twice, so counting rows over-counts incidents and summing fatalities double-counts the dead. There was no field that let you tell. Each event record now carries an incident block with a uid you can group or count on instead of event_uid. Nothing is removed — the duplicate keeps its own event_uid and stays retrievable — so uniqExact(event_uid) is still the coded-story count and the incident uid is the incident count. Both are available; neither is imposed.

Added — incident.resolution, because coverage is partial and you should be able to see it. It reads unadjudicated (nothing ever compared this event to anything), self (compared and found unique) or llm (an independent adversarial judge confirmed a duplicate, and incident.uid names the surviving event of the group). The first two produce the same uid and mean opposite things, which is exactly why the field exists rather than being inferred. incident.confidence accompanies an llm verdict, and for a group of three or more it is the weakest link holding the group together, not the average. Only events that were candidates for a duplicate are ever adjudicated, so unadjudicated is the honest majority — on a typical day it is most rows.

Changed — history back to 2026-03-01 has been adjudicated. Earlier months turn out to be considerably more duplicated than recent ones: roughly 60% of examined March conflict pairs were confirmed duplicates against 15-30% in August, and many are byte-identical — the same arrest, the same strike, the same roadside attack, coded twice from two clusters on the same day. If you have built totals over the spring you may find them meaningfully lower once collapsed on the incident key. The survivor of a group is its earliest-dated member, chosen so the key does not move between two identical requests.

2026-08-14

Fixed — country_iso3 returned every event, everywhere. The events and stories lists read only the `country` spelling, while the response echoes the resolved set back to you under `country_iso3` — so replaying that value, which is the natural thing to do and what an agent does automatically, returned the entire unfiltered window with a 200 rather than the country you asked for. A country-scoped screen read as though there were no matches anywhere. Both spellings now resolve identically, as they already did on filings, maritime and the screening lists. If you have been filtering with country_iso3, your results change: they were previously not filtered at all.

Fixed — story URLs on event records could contain mangled characters. The link an event carries to its story was built from the raw cluster label rather than the cleaned one, so a story about the río Iguazú produced /stories/cataratas-el-rxedo-iguazxfa-… The story page itself always used the cleaned form, so the two disagreed. Affects roughly one link in 150, concentrated in non-English coverage. Old links continue to resolve — a story URL is matched on the identifier at the end, not on the words.

Added — every /api/v2/events response now carries a meta block. meta.row_source says where the rows came from: live, meaning computed from the warehouse at request time, or settled, meaning read from a pre-built daily snapshot. meta.settled_at gives the build time of that snapshot — the oldest one in the window you asked for, so it understates rather than overstates — and is null on the live path, which has no snapshot to date. meta.row_source_reason is one of three values: settled, not_settled, or filter_not_supported, the last meaning a filter you sent cannot be answered from a snapshot so the request was computed live. Today every response reads live; the snapshot path is built but not yet switched on, and this note will be updated when it is. Nothing about the rows changes either way — the two paths return the same events, in the same order, with the same fields.

Changed — event records now carry their article count and language breakdown in the daily snapshots, matching what the live path computes. Those two fields were previously only ever computed live, so a snapshot-backed read would have reported an article count of zero and an empty language list.

Fixed — an actor could be duplicated across both slots of an event. Where an event had a target actor and no source, the stored record put that same actor in both positions, so a record with one participant read as two, and its generated title named the actor against itself. This affected the stored daily snapshots only, never the live API, and the affected records are being rewritten.

Added — a plugin for Claude Code and Codex, and a /build page that installs it. One command wires both the data MCP server and the documentation MCP server into your agent, along with five workflow skills: a Core-API primer plus war-risk underwriting, counterparty exposure, supplier disruption, and country risk as a joinable time series. The marketplace is public at github.com/gdelt-cloud/plugins. A documentation-only plugin is available with no account at all, if you just want your agent reading the reference while it writes your code.

Fixed — the documentation MCP URL was linked as if it were a page. docs.gdeltcloud.com/mcp is a JSON-RPC endpoint, so opening it in a browser returns a 405 error blob; it was linked that way from the homepage, the app sidebar, the public mobile nav and the Connect page. The MCP documentation now lives at /mcp/overview and the endpoint is only ever offered as something to paste into a client. Two pages of the documentation, /mcp/integrations and /mcp/skills, were also infinite redirect loops, and /reference/core-articles returned 404 while sitting in the navigation. All are live again.

Fixed — the Connect page's code samples pointed at a hostname that does not resolve. Every cURL, Python and TypeScript snippet on that page used api.gdelt.cloud, which has no DNS record, so a copied snippet failed before it reached us. The base URL is gdeltcloud.com/api/v2, as the OpenAPI spec has always said.

Changed — the summary endpoints now document the filters they actually accept. /api/v2/events/summary and /api/v2/stories/summary honour days, date, entity, country_match, geo_precision and the observed_* window, and the published spec declared none of them — so the endpoint index, which is generated from that spec, listed all of them as unavailable. They are documented now. The handful genuinely not accepted is short and the endpoint says so itself: near, lat, lon and radius_km come back under applied_filters.ignored, and search is refused with a 400. Nothing about the API changed; the description of it did.

Fixed — bounding boxes have two axis orders and the error reference documented only one. Events, stories, facilities and energy take latitude first; the maritime endpoints take longitude first. For most real boxes both orderings are numerically valid, so a swapped box cannot return an error — it silently queries a different part of the planet. The INVALID_BBOX guidance now names both families and says which endpoints each applies to.

Changed — five pages said list endpoints default to the last 24 hours. Event and story lists default to seven days; other families carry their own default. The parameter reference now carries an identifier table showing, for every endpoint that takes an entity, what that endpoint says it accepts — because a spine id, a news id, a GEM id, a CIK and an LEI are not interchangeable, and sending the wrong one returns an empty result rather than an error.

2026-08-13

Fixed — an entity-scoped event summary returned global totals. GET /api/v2/events/summary with an entity filter and the default grouping ignored the filter entirely and reported counts for the whole corpus: 14,296 events over one week where the correct answer was 104. The list endpoint and the same summary grouped by country were always right, so the two disagreed by a factor of 137. If you built a volume-over-time chart on the default summary call for a single entity, re-run it — the numbers have changed.

Added — country_match, so you can ask for events located in a country rather than events connected to it. country= has always matched a country either by where the event happened or by the origin of an actor involved, which on a typical day means about one in seven results for a country query happened somewhere else. That behaviour is unchanged and remains the default. Passing country_match=location restricts to geography alone. It works on events, stories and both summary endpoints, and the parameter reference now states the default semantics plainly.

Changed — the daily serving tables now refill gaps in both directions. A date that had never been settled could sit empty indefinitely while the hourly job kept refreshing the current day; two days of events were affected. Backfilling now takes priority over refreshing, so an empty day is filled before a complete one is updated.

2026-08-11

Changed — the documentation is smaller and the reference is now generated end to end. docs.gdeltcloud.com went from 73 pages to 31. What went is duplication, not information: a taxonomy page restating 451 lines of codes, categories, regions and languages that the generated reference already published, a concepts page whose response schemas were already in the OpenAPI spec, and per-dataset pages whose value tables now link to the value reference instead. Every published value — every enum, code, parameter, formula and licence — is emitted from the same objects the server imports at request time, so a documented value is one the API actually accepts. A test now fails the build if a hand-written page restates one.

Added — two task-shaped guides. "Build a monitoring feed" walks the pattern the API is designed around: resolve a name to an entity id once with GET /api/v2/search, then reuse that id across events, stories, government awards, facilities and ownership exposure. "Answer a question with MCP" does the same work through a connected model, and both show the REST and MCP forms side by side. The quickstart is one screen and now opens with an unfiltered call rather than a five-filter example against a fixed date window.

Added — a data catalog at /data/catalog stating which datasets we produce and which are open feeds we ingest, with the source, licence, coverage window, refresh cadence and known limitations of each. It is generated from the same records that back the public data page, so the two cannot disagree.

Changed — the API reference sidebar is now ordered Core, then Open Feeds, then Intel, and is generated from the specification rather than maintained by hand. All 75 operations and their URLs are unchanged.

Changed — every parameter that takes a fixed or measured set of values now links directly to that value list in the reference, on all 101 of them. Deprecated parameters (event_family and domain, both superseded by category) moved out of the main parameter tables into a section of their own naming the replacement. They continue to work exactly as before.

Changed — the v1 API documentation has been retired. The v1 endpoints continue to serve existing integrations unchanged; they are simply no longer documented, and their pages redirect to the v2 reference. New work should use v2.

2026-08-10

Fixed — exposure list filter returned a confident empty result. GET /api/v2/exposure accepted any string for list (and its alias source) without checking it, so an unrecognised value returned 200 with zero rows and no warning. On a sanctions surface that is the worst possible failure: an empty result reads as "this asset is not on that list" when the truth was "we did not understand your filter". Two lists our own exposure page offered in its picker, UFLPA and the EU consolidated financial sanctions list, are declared but not yet ingested and carry no rows at all, so selecting either produced exactly that false negative. An unrecognised value now returns 400 with the accepted values, and a list we declare but do not yet carry returns 400 with details.reason set to declared_but_not_ingested and a message saying so explicitly — never an empty success. The picker no longer offers a list we cannot answer for; both it and the API Arena now derive their options from the same registry the server validates against, so they cannot drift apart again.

Fixed — filtering exposure by a China list returned nothing. Under lens=china the exposure rollups record a list membership internally as list_<name>, so filtering the same data with list=csl_ofac_cmic matched none of it — 121 exposed assets for OFAC CMIC and 111 for DoD 1260H were unreachable through the filter, while the identical filter worked under lens=sanctions. Both spellings are now accepted on every lens, so one value works throughout, including the prefixed form if you send back what a response gave you. The two signals that are not lists, state_owned and cn_state, remain valid filter values and are now documented as such. Matching is also case-insensitive across the screening and exposure endpoints.

Changed — one error code for an invalid screening list. Rejecting a list value used to return either INVALID_LIST_SOURCE or LIST_SOURCE_NOT_AVAILABLE depending on why. Both now return INVALID_LIST_SOURCE, and the distinction moved into details.reason as declared_but_not_ingested or unknown_source_key, which is structured rather than something to infer from a code. The message is unchanged and still says plainly when we do not carry a list, so no information is lost. If you branch on the error code, branch on details.reason instead.

Fixed — maritime activity time bucket. GET /api/v2/maritime/activity documented bucket=hour|day but did not enforce it: any other value was silently replaced by the automatic grain and echoed back as the one we chose, so bucket=week returned daily rows labelled bucket: "day". An unrecognised value now returns 400 with the accepted values, matching every other filter on the surface. Omitting bucket is unchanged and still means automatic — hourly for spans of two days or less, daily beyond — and the response continues to report which grain was used.

Fixed — entity tone. Our API reference listed a bucket parameter on GET /api/v2/entities/{entity_id}/tone with values day, week and month. No such parameter exists and none of them ever did anything: entity tone is scored per entity per story per day, every row carries bucket_granularity: "day", and bucket=month simply returned daily rows with a 200. The parameter has been removed from the specification rather than left as a request that looks honoured and is not. If you were sending it, nothing about your results changes; aggregate on your side, or narrow the window with date_start and date_end.

Fixed — Atlas GPR component. The specification declared component as a flat five-value list, but which components exist depends on which lens you ask for: the events lens carries all five, the GPR lens has no verbal or material split, and the attention lens is a single undecomposed series. Six of the fifteen combinations the flat list implied cannot return a row, and the server was already rejecting them — so the document was promising requests that could not succeed. The dependency is now published: the reference shows the legal set per variant, the specification states it, and the 400 returns that variant's set in details.accepted_values. Server behaviour is unchanged.

Changed — source languages. The languages filter is documented as a measurement rather than a fixed list. Two hand-maintained lists of "62 recognized source languages" existed — one in the docs, one behind the filter UI — and they were different 62s: they agreed on 50 and each carried a dozen the other did not. Neither matched our corpus. Sixteen languages we actually publish could not be selected in the filter at all, including Croatian, Urdu, Tamil, Telugu, Malayalam, Marathi, Punjabi, Kannada, Gujarati, Sinhala, Catalan and Malay, and three codes were advertised with no coverage. Both lists are replaced by a single generated reference showing every language with measured coverage — 77 over the last 30 days — refreshed from the warehouse and dated. Validation is unchanged: we reject only what cannot be an ISO code, so a valid code we have not seen yet is still accepted.

Known issue — three language codes are not what you would expect. Azerbaijani coverage is stored under axe rather than az, Galician under glg rather than gl, and some Mongolian under mon rather than mn, because those are the spellings our upstream feed uses and we do not currently fold them. The practical effect is that languages=az returns nothing while Azerbaijani coverage exists, and Mongolian is split across two codes. Until we fold them and reprocess the affected rows together, filter on the spelling shown in the value reference. Coverage from our own directly-ingested sources is unaffected.

2026-08-08

Changed — event coding routing. The router that decides which coding pipeline a story goes to can now also move a story out of the conflict pipeline and into a CAMEO+ domain. Previously the correction only ran the other way, so a story the first-pass rule sent to conflict stayed there. That rule is a fixed lookup on the story's category, decided before any model reads the text, and on a 432-story labelled sample it picks the wrong pipeline about 37% of the time; letting the correction run in both directions takes that to about 29%. To be plain about what this is and is not: the accuracy of the resulting coded events is unchanged. Measured paired across 4,311 coding runs, exact agreement with the labels went from 39.1% to 39.4%, which is not a real difference, and we are not claiming one. The pipeline was already recovering most wrong-pipeline stories on its own — the coder refuses when the story does not fit the book it was handed, and the story is then re-coded in the right one — so routing correctly the first time mostly removes a second pass rather than changing the answer. The benefit is cost and latency: re-codes fell 32% and total model spend on the same corpus fell 10.6%. Nothing is added to or dropped from the event set; refusals and empty results were flat. What can differ is which taxonomy a given story is coded in, so an event that would previously have been recorded as a conflict event may now be a CAMEO+ event with a domain and root code, or the reverse.

Changed — event coding methodology. The top-level router, the first step that decides which coding pipeline a story goes to and proposes its CAMEO+ root, now runs on a higher model tier. Routing is a judgment over unstructured text rather than a classification, and the smaller model was not stable at it: asked the same question twice, it gave the same answer on 52.5% of stories, against 74.0% for the new model, measured paired over 200 production stories. Carried through to the final coded event, agreement rose from 57.5% to 69.0%. The practical effect is that fewer stories are sent down the wrong pipeline, and the event type and CAMEO+ code attached to a story are more likely to be right and more likely to be reproducible. The previous router proposed a root code on 26.8% of stories and was correct on 28% of those; the new one proposes one on about 71%. Coded events are otherwise unchanged in shape — same fields, same taxonomy, same endpoints. Only the classification a story receives can differ, and we expect the difference to be an improvement. We also tested giving the router full article text instead of headlines and it made routing worse, so the router continues to read headlines.

Fixed — events. GET /api/v2/events and GET /api/v2/events/{id} could name different stories for the same event. When an event's story sits on a different calendar day from the event itself — about 13% of events, from cross-day coding and the prior-day ingest window — the list endpoint could not see that story and fell back to a stale pointer, often one that had since been merged away and held no articles. The by-id endpoint resolved it correctly, so the same event returned one story when listed and another when fetched. On a sample of 40 cross-day events, the two endpoints disagreed on 5; they now agree on all 40. This affects story_refs, primary_story_url and metrics.article_count on the list endpoint, and the same fields wherever events are summarised. No event was added to or removed from any result — only the story an event points at changes.

Fixed — events by id. GET /api/v2/events/{id} returned 404 for an event whose story sits more than two days from its event date, while the same event appeared normally in GET /api/v2/events. Those ids now resolve.

2026-08-07

Added — 75 official CAMEO event codes that our taxonomy had never carried. The POLITICAL taxonomy went from 164 leaf codes to 240, and now contains every code the CAMEO codebook defines under roots 01 through 13 and 15 through 17. The gaps were whole branches, not stragglers: every root's official "not specified below" residual (020, 030, 040, 050, 060, 070, 080, 090, 100, 110, 120, 130, 160, 170), and eleven complete four-digit sub-trees — intent to provide aid (0331-0334), intent to reform (0341-0344), intent to yield (0351-0356), acceding to reform demands (0831-0834), releasing persons versus property (0841, 0842), receiving peacekeepers, inspectors and humanitarian access (0861-0863), demanding material cooperation (1011-1014), demanding aid (1031-1034), demanding that a target yield (1051-1056), rejecting cooperation (1211-1214), rejecting aid requests (1221-1224), rejecting reform (1231-1234), refusing to yield (1242, 1244, 1245, 1246) and reducing aid by type (1621-1623). Also added are the two codes CAMEO introduced in its 2012 revision and we had never picked up: 155, mobilize or increase cyber-forces, and 176, attack cybernetically. Three further singleton appeals were missing and are now present: 0234, 0252 and 0255.

What this means if you call the API. Every one of these is now a valid subcategory filter value on /api/v2/events — previously they returned 400, because we only accepted codes we ourselves could emit. Nothing that worked before has changed. Note that these codes are newly emittable, so they carry no history: an event whose text would today be coded 1056 (demand de-escalation) was, before today, coded at its parent 105. When you query a newly added child code, read a low count as "new", and query the parent alongside it if you need the full historical series. Coverage of the parents themselves is unaffected.

Two things deliberately stay out. Codes 173 (arrest or detain) and 175 (violent repression), and the whole of CAMEO roots 14, 18, 19 and 20 (protest, assault, fight, mass violence), remain absent from this taxonomy because those events are coded by our ACLED pipeline instead; carrying them here would double-code the same event under two families. That exclusion is now the only difference between our POLITICAL taxonomy and the official CAMEO code list. Separately, the new residual codes mean the same thing as the bare two-digit root they sit under — 020 and 02 are the same answer — so our coder continues to prefer the root, and we recommend querying the root if you want both.

Fixed — event taxonomy labels. Eight more POLITICAL labels named a narrower thing than the CAMEO code they sit on, which told both our coder and anyone reading the taxonomy to rule out cases the code exists to catch. Code 101 was "Demand information/investigation" and is now "Demand material cooperation" — official 101 covers any demanded material exchange, economic or military as much as judicial. Code 103 was "Demand aid/protection" and is now "Demand material aid". Code 1044 was "Regime change" and is now "Institutional/regime change", which reaches any change to the rules of the game, down to amending an electoral law. Code 1385 was "Threaten attack with WMD", a name CAMEO retired in 2012; it is now "Threaten unconventional mass violence", meaning weapons of mass destruction but equally a threatened mass expulsion, mass killing or ethnic cleansing with no weapon mentioned. Codes 1661, 1662 and 1663 were "Expel peacekeepers", "Expel inspectors" and "Expel aid agencies" and are now "Expel or withdraw …" — the official codes are symmetric and cover a voluntary pull-out by the provider exactly as they cover an expulsion by the host. And root 17 was "COERCE (non-violent)" and is now "COERCE": CAMEO does not scope that root to non-violent acts, and seizing, confiscating or destroying property (171, 1711, 1712) is officially a use of force and has always been carried under it. If you display our subcategory names or match on them as strings, these eight will read differently from today.

As with the code corrections below, the numeric codes have not changed — only the labels attached to them. The direction of the error matters for anyone querying history: because each old label was narrower than its code rather than pointing at a different one, events coded before today should be read as under-covered on these codes rather than mislabelled. Over the last 30 days the eight carried 478 events, nearly all of them on 101 and on the bare root 17; codes 1385, 1661, 1662 and 1663 carried none at all.

Fixed — event taxonomy. Four CAMEO codes in the DEMAND family carried the wrong labels. Codes 105, 107 and 108 were rotated against the official CAMEO codebook: 105 was labelled "Demand mediation" when it means demand that the target yield, 107 was labelled "Demand ceasefire" when it means demand settling of a dispute, and 108 was labelled "Demand meeting/negotiation" when it means demand mediation. Code 106, demand meeting/negotiation, was missing from our taxonomy entirely and has been added. Code 1241 was labelled "Refuse to ease sanctions" without qualification; official 1241 covers administrative sanctions only, so economic-sanctions cases were being pulled into it. The numeric codes themselves were always correct and have not changed — only the meanings attached to them. Events coded before today carry the old meanings: in the last 30 days that is 73 events across codes 105, 107, 108 and 1241. If you filter on those four codes, treat records created before 2026-08-07 as carrying the previous interpretation.

Fixed — quad class on investigation events. Events under CAMEO root 09 (INVESTIGATE) were being classified as "Verbal Cooperation". By the standard four-way aggregation, roots 09 through 13 are Verbal Conflict — and our own Goldstein anchor for root 09 is negative, which cannot sit in a cooperation class. Root 09 is now classified as Verbal Conflict. This affected 598 of 619 investigation events in the last 30 days, roughly 3 percent of all political events in that window. Historical events retain the quad class they were coded with and were not backfilled; the goldstein_scale field on these events was always correct and is unaffected.

2026-08-05

Changed — plans. Every tier, including Free, can now call every source dataset we have: events, stories and entities, the fused facilities directory, GEM energy and heavy-industry assets, media tone and Share of Voice, government awards and FARA, SEC filings, Epoch AI, macro, screening lists, GLEIF and maritime. Plans differ on volume rather than on which data you can reach. Free stays a trial rather than a production tier — 100 query units a month is well under the roughly 750 it takes to keep a single entity current — and Monitoring Briefs, Atlas indices, the entity dossier and point-in-time history remain on the paid tiers. If you previously received a 403 on energy or facilities, those calls now work.

Fixed — screening. Passing an entity id to /api/v2/screening/match returned no matches for entities that are in fact sanctioned, because the id form the endpoint itself returns was not one it could look up. A screen by id now resolves through the same entity linkage the lists endpoints use, so a listed counterparty matches. An id we cannot resolve at all now returns a 400 that says so, rather than an empty result that reads as a clean screen. Separately, raising the match threshold could previously increase the number of matches and return scores below the threshold you set; an explicit threshold is now a real floor on what comes back.

Fixed — government awards. Combining filters on /api/v2/gov/awards widened the result instead of narrowing it: an entity filter plus a recipient filter returned the union of both, so the headline obligated-dollar figure could be the sum of two unrelated companies. Filters are now combined as expected. Totals returned for a single filter were always correct.

Fixed — request validation. An unrecognised language code (for example languages=english rather than languages=en) returned an empty result while the response asserted the filters had been understood; it now returns 400 with the accepted format. A malformed CIK on /api/v2/filings/events silently returned every filer's events instead of an error. An out-of-range offset or cursor returned a 500 on several endpoints, including /api/v2/events; it now returns an empty page.

2026-08-04

Fixed — reliability. Two hourly windows on 2026-08-04 (09:00 and 10:00 UTC) produced almost no coded events, and the same failure had been quietly costing roughly one hour of event coding every twenty-eight hours for several weeks. The cause was not the coding step itself: after each hour's editorial review, the pipeline cleans up superseded records across seven tables, and it was doing so in batches of a hundred identifiers — around seventeen separate blocking cleanup operations per hour against the analytical store, each one rewriting data on disk. That cleanup had grown into the single largest cost of the hourly run, larger than the event coding it follows, and on busy news hours it pushed the run past its execution limit, which discards that hour's coding entirely. The cleanup now runs in far fewer, larger operations covering the same records. Articles, clustering, and the underlying stories were never affected — those are written before coding begins — and the hourly catch-up pass recovers coded events for affected windows.

Improved — the hourly pipeline now records when each half of the run begins and ends, so a run that is killed by its execution limit is visible as a failure rather than as an absence. Previously such a run left no error of any kind, which is why the recurrence above went unnoticed. This is internal instrumentation and does not change any API response.

2026-08-03

Fixed — reliability. The entity-tone scoring queue had been accumulating rows that could never be worked: 9.8 million of the table's 10.7 million rows were stale queue entries for work that was already complete. Reading that queue scanned nearly 13 million rows every fifteen minutes and twice pushed the analytical cluster to its memory ceiling, which is what took the API offline briefly on 2026-08-02 and again on 2026-08-03. The queue now retires entries as soon as their work is done, carries a retention bound, and has been cleaned up — the table went from 10.7 million rows to 860 thousand. No tone data was affected: every scored result is untouched, and the historical tone record is complete.

2026-08-02

Fixed — an empty result from GET /api/v2/events and GET /api/v2/stories now says what it means. These endpoints returned a bare success response with an empty array, which is indistinguishable from a rejected filter value or a broken endpoint, so a caller sweeping many filter combinations had no way to tell a genuine coverage gap from a mistake on their side. Empty responses now carry a note stating that empty means no coverage for that combination and not zero real-world activity, listing the narrowing filters that were applied in order of how much each one restricts the result, and pointing at the corresponding summary endpoint. Invalid values still return 400 with the accepted set, so a 200 with an empty array now unambiguously means the filters were understood and nothing matched. Nothing else about the response shape changed, and the note is absent when rows are returned. Worth knowing when planning a sweep: conflict categories are genuinely sparse per country — over a representative 23-day window there were 280 non-empty country-and-category combinations out of roughly 1,170 possible, and category=Battles had coverage in 18 countries.

Fixed — Atlas GPR could serve a stale reading for a day indefinitely. Each daily observation is stamped with the date it first became knowable, and the serve layer shows whichever stamp is latest. A restatement run against a day that was still in progress captured a few hours of coding and stamped it with a date in the future, so that partial snapshot outranked the correct, complete measurement computed the following day — permanently, because no later run could ever produce a higher stamp. For 2026-08-01 the world reading was computed over 122 events when the day actually held 1,272. Vintages are now clamped so one can never be dated in the future, restating a day that is still in progress is refused outright, and any day whose served value is being outranked is reported. Previously affected days are being restated.

Fixed — the Atlas panel no longer states a conclusion it cannot support. When a series has too little history to place a reading on its band ladder, the band is deliberately withheld; the summary sentence was treating that withheld state as an ordinary value and asserting the place was holding near normal, including while the index read 0.63x normal. It now says the reading is shown and the interpretation withheld. The same sentence also named a leading child region drawn from whichever day each region last had sufficient coverage, which could be a different date from the one on screen; a leader is now only named when its reading is for the displayed day.

Fixed — reliability. A single cluster whose embedding could not be recovered would abort an entire hourly ingest run, because the story table's vector index requires every value to be the same length and rejects the whole batch otherwise. This cost several hours of event coding between 2026-07-29 and 2026-08-01, visible as gaps in the hourly volume on the Data Status page. An unusable embedding now costs that one cluster and is reported, never the hour. Separately, a query that fails because the analytical cluster is out of memory is no longer retried — retrying asks the exhausted resource for more of itself, and that amplification turned a brief capacity event into an extended one. Deliberate back-pressure from our own concurrency limiter is unaffected and still retries.

2026-08-01

Fixed — a badly fragmented Story could never be consolidated, no matter how many merges our adjudicator approved. Duplicate Stories are merged by taking the transitive closure of accepted merge pairs, then requiring that a component carry no more than two pairs no adjudicator ever examined. That guard is right — an implied weld nobody checked is false about half the time — but it was enforced by discarding the entire component. Candidate generation supplies at most 3n/2 examined pairs for a component of n clusters while the check demands roughly n(n-1)/2, and those cross just under five, so any group of five or more fragments was mathematically unmergeable. Measured on 2026-07-31: two-fragment groups merged 481 of 481, groups of six or more merged 0 of 62. One migration story held 132 fragments with 750 already-approved merges, every one discarded, leaving 111 duplicate Stories live.

Merges are now applied one pair at a time in ascending distance order, and only the pair that would breach the limit is refused — the rest of the group still merges. Every resulting Story still satisfies the identical two-unexamined-pair guarantee, so this is not a partial merge of a suspect group; each one independently passes the same test. Replaying five days of real verdicts on the hourly schedule the pipeline actually runs (the harness reproduces 91-98% of production's real merge counts): 8% to 24% more duplicates consolidated per day, and the migration story falls from 111 live Stories to 26.

Duplicate detection now probes each Story against the day's others individually instead of computing every pair in one query. The old approach grew with the day and stopped finishing around 8,500 Stories, which meant the last six hourly passes of every UTC day failed and merged nothing — and because a day is never revisited, the entire evening news cycle stayed un-deduplicated permanently. The new path is 9.6x faster at exactly that size, finds no fewer duplicates (verified against every merge verdict from a full day), and surfaces roughly 53 more real duplicates per pass that the previous method could not propose at all.

Fixed — a Story pair we had explicitly judged to be DIFFERENT incidents could still be merged. The safety check counted any examined pair as satisfying it, so a "no" was treated exactly like a "yes" — and because the exhaustive sweep over the day's biggest Stories compares every pair among them and roughly 95% come back rejected, the more carefully a group had been examined and refused, the more freely it could be welded together. Over 2026-07-27..07-31 that merged 494 pairs we had explicitly rejected, including one 24-cluster Story joining US and Saudi strikes on Iraqi militia facilities to a separate US strike on Iran's Qeshm Island. A rejected pair now blocks those two Stories from ever sharing a merge group, and the count is zero on every day tested. Separately, rate-limit and transport failures were being stored as "different incident" verdicts — 11.2% of one day's rejections were HTTP 429s — which both suppressed the pair from ever being judged and fed this same defect; they are now retried instead of recorded.

Known limit — this fixes what happens to merges we approved, not how many pairs we look at. A Story fragmented across hundreds of clusters still has most of its pairs never compared, because candidate generation ranks each cluster's three nearest neighbours and the hourly pass that would widen that has been failing in the evening since 2026-07-27. Expect fewer duplicates, not none, until that lands. If you deduplicate Stories yourself, note that merged Stories continue to be marked superseded rather than deleted, and the counts on surviving Stories will move upward.

2026-07-31

New — a second Atlas GPR construction, served in parallel as construction=world_corpus. It is the construction the field's reference index actually uses: a place's share of the WORLD's coded events over a 14-day window, normalized to that place's own frozen normal. The default construction, construction=own_coverage, divides by the place's own coverage, which is topic volume rather than corpus size — and that let the index move opposite to its own events, as on 2026-07-16 when Ukraine's qualifying events went from 8 to 15 while the published index printed its monthly low. Fourteen days is two whole weeks, so each weekday appears exactly twice and the corpus's weekday cycle cancels by construction: measured day-of-week amplitude falls from 35.0% to 3.7%. No severity weighting, because it does not survive testing — the Fed's own intensity-weighted variant correlates 0.97 with plain counts, and fatalities exist on only 28% of qualifying events, so weighting by them would flatten the entire threats half of the index by schema accident. The default is unchanged. We named these by their denominator rather than v1/v2 on purpose: the endpoint already lives under /api/v2, so a version-shaped value would have read as which API rather than which index.

world_corpus enforces a coverage floor on the base window, not just on the day: a series needs 100 qualifying events behind its reference distribution or the reading is withheld. Without it the construction is unusable where data is thin — Burkina Faso, with one event in its base window, medians an index of 1,166, and 31 of 109 geographies are persistently mis-levelled. Above the floor the median is 91 and nothing falls outside 50 to 200. Coverage is published as precision rather than folded into the index: every response carries days_in_window and base_events whether or not the reading clears. A world_corpus reading is a trailing 14-day state, so the series peaks in the days FOLLOWING a burst of events — never read a peak date as the date something happened.

Changed — the deviation baseline is now frozen over a fixed window instead of recomputed over the trailing 90 days each week. A rolling baseline re-anchors a country to its own recent past, so a country at war for the whole window gets a wartime normal and its index decays toward 100 at the peak of exactly what the index exists to detect. Both GPR and EPU anchor permanently. Each re-freeze mints a new baseline_version and retires the previous one; nothing is overwritten.

Fixed — every reading is now normalized against the baseline built from its own coder generation. The event coder changed on 2026-07-15, and the two generations disagree sharply: across 940 country series the newer anchor differs from the older by more than 25% on 638 of them. The serve layer had been picking one baseline per series with no coder condition, so which one you got was an arbitrary database tie-break — and normalizing a new-coder reading against the old-coder norm moves the published index by up to two or three times. Responses now carry baseline_coder_version. A series spanning a coder change is not one comparable series, and we would rather say so than quietly splice it.

Fixed — asking GET /api/v2/intelligence/gpr for a decomposition a lens does not publish now returns 400 with the components that lens actually has, instead of an empty 200. Components are per-lens: the Attention lens is a single undecomposed series, and the GPR lens has no Verbal or Material split. Six of the fifteen lens-and-component combinations the API Arena offered could never have returned a row. The Arena picker now derives its options from the same registry the server validates against, so the two cannot drift apart.

Fixed — Atlas readings now withhold the band, not just the value, where the reference distribution cannot support one. A band is a promise about how often a reading is unusual — roughly a tenth of a place's days sit above the ninth decile — and that promise needs a distribution with spread in it. On 160 of 206 country series the frozen cut-points are either collapsed (every cut-point equal, so any non-zero reading printed as Extreme) or unreachable (the top band would require 100% of a place's coverage to be tension-bearing). Those series now return band: null with band_status: 'degenerate_baseline'. The reading itself is unchanged, and the baseline block now carries n_obs and bands_usable so you can see which case you are in.

Fixed — source_mix returns null with status: 'not_measured' instead of zeros. The distinct-outlet and new-source diagnostics are not computed yet; the columns behind them are non-nullable, so the compute pass writes 0 as a storage placeholder. Serving that 0 read as a measurement of zero sources. It becomes a real number when the diagnostics pass lands, and the response shape will not change.

Fixed — GET /api/v2/intelligence/posture compared a GDP-weighted current window against an attention-weighted prior one, so the trend differenced two estimators rather than two periods and could flip its own direction word. Both windows are now measured on the requested weighting. The trend object also states its basis: the delta describes the dynamic axis, while the published score blends that with an annual structural axis that is frozen within any window Posture serves — so a new headline_delta_equivalent gives what the headline actually moved.

2026-07-29

Fixed — Event article counts. An Event carried the article count of its Story as it was when the Event was coded, and that snapshot was never revised as the Story kept accreting coverage. So /api/v2/events/{id} and /api/v2/events/{id}/stories could report different counts for the same Story, and 26.3% of Events over a 7-day window disagreed with the live Story (1,604 of them under-counting). Both now read the live count, so metrics.article_count and story_refs[].article_count agree with /api/v2/stories. If you have cached Event article counts, expect them to move — upward in most cases.

Fixed — an Event's Story pointer could name a Story it was no longer linked to. When a Story is re-homed between two live Stories, the Event's stored pointer was left behind, so the Event card named one Story while /events/{id}/stories named another. 17.6% of Events disagreed with their live link; 7.6% pointed at a Story that was still live, which is why the existing repair (which only fired on merged-away Stories) never caught them. The link table is now authoritative whenever it contradicts the stored pointer.

New — event_code is now null rather than an internal id. When an Event has no CAMEO+ code, event_code previously fell back to an internal identifier that looked like a code but was not one. This affected every conflict-family Event, which has no CAMEO+ code by construction. It is now null. The Event id is unchanged and still available as id.

New — taxonomy_status on the Event card. A small share of Events (4.2–8.5% per day) are coded but are missing their taxonomy record, so category, subcategory, event_code and the sub-factor metrics are all null for a data-integrity reason rather than because the Event is genuinely sparse. taxonomy_status is 'coded' or 'detail_missing' so you can filter for fully-coded Events instead of inferring it. The underlying cause — a re-review step that could strip taxonomy from an Event it was not re-coding — is fixed going forward.

New — Event metric evidence is now served and shown. metrics.metric_inputs returns the observables the coder read off an Event (magnitude path, systemic size/interconnectedness/irreplaceability, propagation channel/coupling/breach/primed/containment, market claim-exposure/economic-bite) with the coder's recorded reason for each value, plus metrics.metric_version identifying the formula contract. Event pages render these next to each score with the published formula, so a score can be re-derived by hand. Present only on Events coded under the current contract; the key is omitted entirely otherwise, never zero-filled.

Fixed — /api/v2/stories/summary now honours the entity scope. entity, entity_id and entities were accepted and silently ignored on this endpoint, so an entity-scoped request returned the global corpus: an entity holding 2 Stories reported 69,358 over 7 days. The scope now applies on every group_by, is echoed in applied_filters.entity, and an unresolvable entity returns no rows with an explanatory note rather than falling back to global totals. /api/v2/events, /stories and /events/summary were already correct.

Entity pages now read the same endpoints you do. Linked Stories, the Event timeline and the Event map are served by /api/v2/stories?entity= and /api/v2/events?entity= instead of page-specific queries, so the labels match the API exactly — an Event subcategory reads '010 · Make statement (other)' on the page as it does in the response. The 30-day activity pulse is backed by /api/v2/stories/summary?group_by=date.

Terms: the Accounts and access section now states explicitly what the free plan is for and what it is not. Free accounts are for individual evaluation and non-production use, one per person and one per organization, and you may not spread usage across multiple accounts — including accounts on different email addresses, domains, or affiliated entities — to obtain extra free quota or avoid fees that would apply if the usage were combined.

Where accounts belong to or are operated for the same organization, we may treat their usage as a single account for plan limits and billing, and ask you to consolidate onto a paid plan. Accounts that keep circumventing plan limits after we have raised it, and after a reasonable opportunity to move to a suitable plan, may be rate-limited, suspended, or closed.

This is a clarification of the existing Acceptable use rule against bypassing limits rather than a new restriction, and it is not applied retroactively. If you are a team currently running on several free accounts, a single organization plan pools one quota across every member and gives you per-member and per-endpoint visibility that separate accounts cannot — email hello@gdeltcloud.com and we will help you consolidate.

2026-07-28

New: the GLEIF Global LEI Index is now an Open Feed at /api/v2/gleif/entities, /entities/{lei}, /relationships and /isin — 3.39M legal entities with their registered name, jurisdiction, entity and registration status, and LEI↔ISIN mappings. Published by GLEIF under CC0 and served close to as-published. Available on Analyst and above. Because the feed covers 3.39M records, an unfiltered listing is refused with 400 FILTER_REQUIRED rather than served slowly — pass at least one of lei, name, country, jurisdiction, entity_status, registration_status or entity_category.

New: /api/v2/entities/{entity_id}/hierarchy returns an entity's corporate hierarchy — direct parent, ultimate parent and children. It accepts the same identifiers as the rest of the API (a name, an e_ id, a wikipedia_url) and additionally a bare 20-character LEI, so the id you searched with works at this hop too. Gated on the existing exposure entitlement; the LEI itself stays ungated.

Read the hierarchy as what it is: accounting consolidation as reported to the Global LEI System under IFRS/US-GAAP. It carries no ownership percentages and excludes natural persons, so it is not beneficial-ownership data. Coverage is partial by design — a filer may declare a reporting exception instead of naming a parent, and the response says which: coverage.reporting_exception distinguishes a declared absence from an unknown one, and an entity with no LEI in our crosswalk returns lei_present: false rather than an empty hierarchy that would read as 'no parents'.

The relationship graph is no longer American-only. Until now /api/v2/entities/{id} returned relationships extracted from SEC filings plus, as of today, global energy ownership. GLEIF Level 2 adds 56,188 corporate-hierarchy edges — 29,800 ultimate-parent, 26,174 consolidating-parent and 214 branch-of — taking the entity relationship graph from 209,093 to 245,256 edges. These edges never assert a stake: where GLEIF records no percentage, none is returned.

Search: /api/v2/search gains universe=registry, which searches all 3.39M GLEIF records by legal name or LEI. Registry results are deliberately kept in their own lane and never mixed into universe=all or universe=reference — a registry hit is a legal-entity registration, not evidence of news coverage, and each result carries resolved_in_spine telling you whether we also cover the entity itself.

Non-Latin company names now resolve. Entities we already cover picked up 18,699 alternative and transliterated names from GLEIF, 2,412 of them non-Latin. Searching a company by its registered Cyrillic, Han or Arabic name — for example the full Russian legal name of Alrosa — now returns the entity, where before it returned nothing.

★ Corrected: ownership relationships were being under-reported by a large margin, and the affected numbers are moving up sharply. Our global energy-ownership records store a parent company as an identifier with the ownership stake appended to it — for example `E100001011238 [47.5%]` — and every part of the system that read those records matched them as plain identifiers, so 82.4% of the ownership links never matched anything. Of 31,516 parent links, only 4,903 were resolving; now 30,878 do.

What that looked like: `ownership_edges.child_count` on the entity endpoint reported zero subsidiaries for some of the largest power companies in the world. China Energy Investment went from 0 to 139, Huaneng Power International from 0 to 102, China Cinda Asset Management from 0 to 112, and China Huadian from 2 to 129. There was no error and no warning — just an HTTP 200 with a number that was wrong.

Ownership-chain exposure was affected the same way, because the chain walk stopped after a single hop on 82.4% of edges and so never reached indirect owners. When the next daily exposure rebuild runs, expect exposed entity counts to rise substantially: sanctions exposure from 220 to 392, China exposure from 47 to 181, and state-ownership exposure from 1,372 to 2,339. If you have alerting or screening tuned against these, entities that previously came back clean may now come back exposed. This is a correction, not a change in method — those entities were exposed all along through ownership chains we were failing to traverse.

New: the ownership graph itself is now published. Until now `/api/v2/entities/{id}` returned only relationships extracted from SEC filings, which meant the relationship graph was entirely American. We have added 24,562 ownership edges covering global energy ownership, 19,668 of which carry a declared ownership percentage. They are typed `ownership` and are distinct from the `subsidiary` relationships extracted from SEC filing text: those are a legal-subsidiary claim read out of a document, these are a registry-declared stake. Where no percentage was recorded, none is asserted.

Fixed a bug in /api/v2/events/summary that reported a fabricated 0 for the four CAMEO+ metrics on events that do not have them. The three 0–1 metrics are stored as non-nullable floats, so an event with no CAMEO+ detail row was being averaged in as 0 rather than skipped. Measured over 7 days: all 1,074 Conflict-family events reported avg_systemic_importance = 0 instead of null, and 780 of 12,197 CAMEO+ events with no detail row pulled the CAMEO+ average down from 0.241 to 0.2255. avg, min and max for systemic_importance, propagation_potential and market_sensitivity now return null when no event in the bucket carries the metric. magnitude was already correct.

If you read /events/summary, expect these three fields to change: Conflict-family buckets now return null where they previously returned 0, and CAMEO+ averages move up slightly. A 0 asserted 'measured, and it is the minimum' about a metric that does not apply to that family, which is the opposite of what we intend — we omit rather than fabricate.

Events coded from today also return metrics.metric_inputs: the sub-factors behind each score with the reason the coder recorded for each one, so a score can be re-derived by hand. The field is absent (not empty) on events coded earlier, and on Conflict events, which have no sub-factor contract. A backfill of the existing corpus is planned but has not run.

The event-metrics documentation is now published at /event-metrics and in a dedicated Event Metrics section of the docs, with a page per metric covering the formula, the plain-language definition of every variable, worked examples from real coded events, and the measured error bars.

2026-07-27

The four CAMEO+ event metrics — magnitude, systemic_importance, propagation_potential and market_sensitivity — are now produced by published formulas rather than by a free-form model judgement, and the formulas are documented in full. The coder reads a handful of concrete sub-factors off the source text and records a reason for each; a fixed, versioned function turns those into the score. Any value can now be re-derived by hand from the inputs. The field names, ranges and response shape are unchanged, so no integration needs to change.

What each metric is grounded in: magnitude follows Richardson's log-fatality convention on the conflict path (2 + 2·log₁₀(deaths)) with anchored 0–10 tiers elsewhere; systemic_importance is the equal-weighted mean of size, interconnectedness and irreplaceability, matching Basel BCBS G-SIB and ECB O-SII practice; propagation_potential averages five observed transmission conditions behind a channel gate, following the ERCS barrier model and Rinaldi–Peerenboom–Kelly dependency coupling; market_sensitivity applies the reasonable-investor materiality test. We adapt these frameworks — none of the institutions named endorses or reviews this work.

★ Expect the default significance ordering to change. Because significance blends these four, re-coding moves it: measured across 59 paired real events with the formula held identical, rank correlation between the old and new values is 0.76, and 32 of 59 events move by more than 0.05. To be sure that was a real change and not coding noise, we ran the same events three times through the new path and compared: identical runs agree at 0.92, so the shift is larger than the noise. Verbal appeals and announcements deflate, real in-force action inflates. If you have alerts or thresholds tuned against current significance values, they will fire differently. sort=significance is the default on /api/v2/events, and Atlas Pulse and Posture read the same expression.

We now publish the error bars, and ask you to use them. Repeat coding of the same event moves magnitude by about 0.36 on its 0–10 scale and the three 0–1 metrics by about 0.03. Treat differences below roughly 0.72 (magnitude) and 0.07 (the others) on a single event as noise. Aggregates are far steadier than individual events, so distribution-level comparisons hold well below those thresholds.

Corrected how we describe these numbers, in several places where the wording oversold them. market_sensitivity is not a predicted price move — it measures how much market-relevant information an event carries for a reasonable investor, and carries no direction. propagation_potential is not a probability of cascade — it scores transmission conditions present in the event as reported, not a forecast. systemic_importance is not a count of affected states — it is how much the wider system depends on the specific node an event touched. And the published significance weights were wrong: fatalities is 0.55, log-scaled by body count, not 10%; civilian targeting (0.10) was missing; and the per-family renormalization that makes conflict and CAMEO+ events comparable was undocumented.

These four are rubric scores, not measurements, and the docs now say so wherever they appear. A hard registry attribute — the observable that would make systemic_importance empirical rather than judged — resolves for about 1 event in 45, and a traded instrument for about 1 in 67. Treat all four as ordinal ranking signals. magnitude is comparable within a domain only; use significance to rank across domains. A magnitude of null means the severity observable was not found — it means unknown, never zero, and should not be coerced to 0.

Fixed: Stories that already had a coded event but weren't linked to it now show it. A cluster's event id is derived from the cluster id, so a Story stuck in this state was re-analysed on every pass, the result discarded as a duplicate, and the link never written — leaving has_events:false permanently on a Story whose event existed the whole time. We swept the backlog: 1,414 Stories reconnected to their events. For Stories with 4+ articles the has_events:false rate fell from 60.4% to 58.0%, and the improvement scales with size — 6-9 articles went 49.5% to 40.0%, 10-19 went 58.9% to 45.1%, and 20+ went 57.7% to 43.8%. Going forward the link is written before the re-analysis rather than after, so the loop cannot re-form.

Fixed: big Stories that grew over the course of a day were being excluded from event coding. Every cluster is graded once when it is created, and a single-article cluster that isn't worth analysing is marked as such — but nothing re-graded it when it later accumulated articles. Stories that grew to hundreds of articles kept the original grade and were never analysed for events; the largest we found held 283. Codeability is now derived from the Story's current size rather than read from the grade stamped at creation, so growth is picked up automatically.

Duplicate Stories: the same-day de-duplication pass now spends its full comparison budget on pairs it hasn't seen. It was re-checking pairs already settled earlier the same day, which consumed about 70% of each pass. On a measured day this raised the range a single pass covers rather than truncating it. Half of the duplicate Stories removed on 2026-07-27 came from a similarity range the previous rule could not reach at all, and an independent audit put the false-merge rate at 3.3% — none of them in the newly reachable range.

Note on story_id stability: reconnecting a Story to its event does not change any story_id, and no Story was removed by this work. If you cache has_events, expect it to flip from false to true for some Stories dated on or after 2026-07-13.

2026-07-24

New: entity tone now breaks down BY LANGUAGE across all scored coverage in the window. Pass group_by=language to GET /api/v2/entities/{id}/tone (or /api/v2/entity-tone), and each entity's coverage gains a by_language array — mean tone, risk, and confidence per language, with the number of scored stories and the real total article volume covered in that language, plus a per-language daily series. It's also embedded in the entity-detail response under entity_tone.by_language when include_tone=true. The point is to see how an entity is covered differently across languages — e.g. an entity that reads markedly more negative in Arabic and Russian than in Spanish. The mean tone per language is computed over every scored story (not a sample); it reads the per-story score rollup because the daily table doesn't retain per-language splits.

Docs: the entities-detail response now documents the fields the endpoint has been returning all along but the schema omitted — cooccurrences (co-occurring entities: name, type, wikipedia_url, story_count, source, url), plus timeline, source_mix, map_pins, and the per-source sources/sections map. No response change; the OpenAPI schema and the MCP get_entity tool now describe them.

The "API view" drawer on the event, story, and entity detail pages now shows the exact call and the exact response the named endpoint returns — the promise that you can copy a snippet and get the same data back is now literally true. Previously these drawers reconstructed a stand-in body by hand, so several snippets returned a different shape than shown, and a few named sub-endpoints that were never actually built (for example /entities/{id}/cooccurrences or /stories/{id}/related). Each drawer now runs the same serve function the public endpoint does, so a copied cURL/Python/TypeScript snippet reproduces the drawer byte-for-byte. A verification gate fetched every shown snippet and deep-compares it to the drawer across the anonymous, free, and Intelligence tiers.

The entity drawer's snippet is now plan-aware. GET /api/v2/entities/{id} returns a body whose per-source sources map and gated sections depend on your plan, and the drawer now reflects the tier the page is being viewed at — so what you see is what your key would receive, and the public (signed-out) page never shows a body richer than the free tier. The event and story detail responses are unchanged.

2026-07-23

New: Media — entity Tone and Share of Voice now have their own place in the app at /media, with the two views side by side. Two fixes went with the move. Picking an entity alias from the suggestions (for example choosing "Israeli military" after searching IDF) used to report "No canonical entity match found" for an entity the API resolves perfectly well — the page recognised fewer id formats than the resolver behind it, and now uses the same one. And the Tone page no longer shows a Share of Voice figure: share of voice needs a peer set to divide by, the page only ever had a single entity, so the API was correctly refusing the request and the page was displaying the refusal as "0.00% · 0 of 0 stories". Share of voice is now only where a peer set is actually collected.

Tone's per-language breakdown now states what it is measured on. It is computed from a bounded evidence sample, not from every scored story, so its per-language article counts never summed to the scored total shown above it — a page reporting 258 scored stories showed language counts totalling 27, with the explanation in a footnote. The sample size is now labelled on the panel itself. Selecting a language still refilters the headline figures against the full window.

Entity pages no longer borrow another entity's portrait. For entities we have not linked to Wikipedia, the page was falling back to a Wikipedia search on the entity's name, which could resolve to a different organisation and show its logo beside the first entity's name and numbers. No link, no portrait — those entities now show initials.

The Core Search results pages are faster and show 10 results at a time with a "Show 10 more" control. Typing in the search box no longer fires a search per keystroke — it now runs on Enter or the Search button, which matters because this is a semantic search that embeds the query and runs a vector lookup each time. Card imagery is now opt-in from the filter bar, because fetching it was the single largest cost in the response.

GET /api/v2/events and /api/v2/stories now honour include_entity_images. It was accepted but ignored, so callers could not turn off the per-entity Wikipedia enrichment that dominates response time on image-bearing requests. It defaults to true, so existing callers are unaffected.

Fixed a silent under-return on GET /api/v2/facilities. Filtering by status matched the stored value exactly, but Global Energy Monitor records inferred states as their own values ("shelved - inferred 2 y", "cancelled - inferred 4 y"), so status=shelved returned 39% of shelved facilities and status=cancelled 71% — while reporting the filter as applied. Status now matches on the base state, and those filters return the full set.

New: Core Search — one signed-in surface at /search that browses the Core API groups (events, stories, entities and facilities) behind on-page tabs, with every result linking through to its own page. Switching tabs carries your query and date window across, so the same search can be viewed as events, as stories, or as the entities involved. Each tab is a live call to the public endpoint it demonstrates, and the "API view" panel now shows the request and the complete, unmodified response body — previously it displayed a shortened stand-in rather than the real payload, so the copyable cURL / Python / TypeScript snippets could return something different from what the page showed. They now reproduce the page exactly.

Facilities now has a UI. The fused directory (power plants, mines, pipelines, terminals, ports and AI data centers from Global Energy Monitor, the NGA World Port Index and Epoch AI) is searchable by name, owner, type, class, source, country, status and capacity, and every facility has its own page with location, resolved owner entities, the source record and the license attribution. Signed-in users additionally get the full owner list, every source attribute, the cross-silo merge history, and recent news coverage of the facility's owner over a 7-, 14- or 30-day window. Facilities is included from Builder up.

Fixed pagination on GET /api/v2/stories in the web app. Stories pages with a keyset cursor, and the Next control had been computing a numeric offset instead of passing the cursor the API returns — the server could not decode it and silently re-served page one. Paging forward now advances correctly. The API itself was always correct; this was the web app mis-using it.

Semantic search on GET /api/v2/events (the search / query parameter) is now backed by the vector index end-to-end. It had been running an unindexed similarity scan over the whole requested date window, which made larger searches slow and, under concurrent load, run up against the query time limit — the source of intermittent errors on the events search endpoint. It now ranks against the HNSW index and hydrates only the top matches; the result ordering is unchanged (same cosine-similarity relevance), and a representative 30-day search dropped from around eleven seconds to well under a second.

The query-embedding step that powers semantic search now retries a transient timeout instead of failing the whole request. A slow embedding call had been surfacing as a hard 503 SEMANTIC_SEARCH_UNAVAILABLE on /api/v2/events and /api/v2/stories; it is now given more headroom and retried, so those intermittent search failures are largely eliminated. A genuinely unavailable embedding service still returns an honest 503 rather than silently wrong results.

Semantic search now honours sort and exposes a relevance score. A bare search returns results in relevance order (most similar first) and reports sort:"relevance"; sort=significance or sort=recent now re-rank the relevant matches by significance or recency, whereas before sort was effectively ignored once a search term was present. Every result carries a search_score (0–1, higher = closer) so you can threshold on match quality, and on /api/v2/events a low-relevance floor drops nonsense/no-match queries to an empty result instead of returning confident-looking but irrelevant events. Forward pagination via next_cursor on /api/v2/events?search= is also fixed — it previously stopped after the first page.

Entity ids are now consistent across every endpoint. The id returned by GET /api/v2/entities is now the same universal canonical id that GET /api/v2/search returns — an e_… id when the entity is in our cross-source reference spine, otherwise a wiki:… id — and it round-trips on every entity-taking endpoint (/events?entity=, /entity-tone, /share-of-voice, /gov/awards?entity=, entity detail). Previously the entities list handed back a name-based id (person:Name / organization:Name) that resolved on some endpoints but was rejected by the cross-source ones, so chaining calls could silently drop data. Old name-based ids still resolve, so existing integrations keep working; new responses simply return the canonical id. The Wikipedia URL is unchanged and still returned as the wikipedia_url field.

2026-07-22

Corrected the end-to-end latency figure, which we had been quoting as "about 15 minutes" across the site. That number was the ingest pipeline's own duration mistaken for end-to-end latency. Measured directly (median over the seven days ending 2026-07-21, from an article's publication to the coded event being queryable in the API): the median is 38 minutes and the 90th percentile is 61 minutes; 10.7% of events are available within 15 minutes and 89.1% within an hour, with a longer tail for events recovered by the hourly catch-up pass and by backfills. The floor is the hourly ingest cycle, not processing time. The methodology and pricing pages now carry the measured distribution instead of the old estimate.

Corrected the native-source compliance handling. Each native RSS/sitemap source carries a declared content policy (blocked, metadata-only, or cleared-for-extraction); the pipeline was overriding that declaration and processing every source at full extraction. It now honors each source's declaration, so a source is processed for full article body only when it has been explicitly cleared, and the stored compliance status reflects a real decision. The methodology page's source-universe section describes the three tiers precisely.

New: the Atlas Coverage Atlas — GET /api/v2/intelligence/coverage (Intelligence plan) and a map on the Atlas product page. It reports, per country, how much coded-event coverage we actually have and therefore which country-level Atlas products each country is dense enough to serve — distinguishing the roughly five dozen countries that clear the slower Posture condition-index floor from the roughly fifteen that clear the higher bar for a daily Pulse tension reading. The exact live counts are published by the endpoint itself rather than fixed here, since coverage grows over time. Rather than print a confident score for every country, it shows the rest as thin or empty. Filter to the daily-servable and condition-servable sets with ?tier=depth.

GET /api/v2/events now reports geo_precision on every event's geo block — a code (1 exact place, 2 general area, 3 country or region centroid) and a matching label — so you can tell an exact-place fix from a country centroid instead of treating every coordinate as precise. You can also filter on it: geo_precision_max=1 returns only exact-place events, which is what you want before triggering on a specific asset. Across a recent sample about a third of events are exact-place and roughly two-thirds carry an area or centroid coordinate.

The events endpoints now reject as_of with a 400 instead of accepting it and returning live data. as_of implies a reproducible point-in-time read; the Atlas and macro series are vintaged and honour it, but event metrics are computed live and are not, so accepting it would have returned today's values while implying otherwise. Use observed_start / observed_end to bound by when an event was coded, or the Atlas and macro endpoints for point-in-time reads.

Fixed a composability break: the entity_id that GET /api/v2/search returns (a registry id of the form e_…) now works as the entity filter on /api/v2/events, /api/v2/events/summary, and /api/v2/stories. Previously that id resolved to nothing on those endpoints and returned an empty result — a false "no events" — while the entity's name worked; you had to know to pass the name instead of the id you were just handed. The shared entity resolver now bridges a registry id to the news-layer identity the event surfaces are keyed on, so search → id → events is a clean round-trip. Verified across TSMC, Boeing, Gazprom and Volkswagen: the id returns exactly what the name does.

2026-07-21

Atlas now has a public page at /product/atlas, with real screenshots of the GPR and Posture surfaces and the coverage caveats stated up front. It is labelled experimental deliberately: world and continent readings are usable daily from April 2026, but country coverage is thin — measured over 107 days, only 2 countries clear the per-country coverage floor on 90 or more days and 12 clear it on most days, and every other country returns null rather than a fabricated score.

Corrected the Atlas entitlement across the site. The /api/v2/intelligence/gpr and /posture endpoints are gated on the Intelligence plan and have been since the plan simplification, but the data reference and the methodology page both still described them as Admin-only or an internal preview. Both now match the code.

The homepage carries a measured throughput and coverage band — articles processed, stories clustered, events coded and entities resolved over the last 30 days, read live from the same database the API serves and refreshed hourly. It renders an explicit unavailable state rather than zeros if the reading fails.

The comparison (/compare) and industries (/industries) page trees have been retired and now redirect. They carried a hardcoded price range that no longer matched the published plans, and the pricing page, data reference and use-case pages cover the same ground against live data.

Corrected several stale claims: media tone and share of voice are computed over news coverage only and social posts are not blended into them (the FAQ previously said otherwise); the REST API and MCP server are included on every plan including the free tier; the CAMEO+ taxonomy has ten domains; and the Early Signal index now regenerates hourly rather than only on deploy. (The latency figure standardized here — "about 15 minutes" — was itself an estimate of pipeline duration; it was later measured directly and corrected in the 2026-07-22 entry.)

2026-07-20

New endpoint: GET /api/v2/intelligence/posture — Atlas Posture, a country condition index. Where Atlas GPR measures how hot a place is running against its own norm, Posture measures what state it is in, scored against fixed real-world reference points. It returns a 0–100 composite over four published pillars (Conflict & Security 0.35, Systemic Gravity 0.25, Markets & Economy 0.25, Flows & Contagion 0.15), each pillar's sub-indicators, a fixed band (Calm / Steady / Elevated / High / Critical, plus a neutral L1–L5 form), the pillar driving the score, and a trend arrow. Every pillar is also split into internal and external readings by whether an event's actors sit inside one country or span a border. Accepts country or level+geo, window=7d|30d|90d (30d default), date, as_of for point-in-time reads, and scope. Included with Intelligence.

Posture withholds more than it publishes, by design. A geography is scored only when its window clears a coverage floor, and each sub-indicator also requires a minimum denominator of its own — a share computed over two events can land anywhere in the distribution. About 60 countries currently clear the floor over 30 days; the rest return null with an insufficient-coverage flag rather than a fabricated number. A well-covered country with no violence does report zero, because that is a real measurement. A fifth pillar, Narrative & Information, is specified but deliberately not shipped — the inputs are not dense enough at country-day granularity — and responses name it as deferred rather than omitting it silently.

Posture values are computed daily and stored point-in-time, so an as_of query never looks ahead: a day revised by late-arriving events keeps its original vintage alongside the correction. Scores, bands and trends are derived at request time from a frozen cross-country reference, which means re-baselining adds a new version rather than rewriting history. The index is provisional while its reference distribution is pooled over a short window.

Posture also gains a second, slower axis: structural condition. Alongside the event-derived pillars, it now scores the standing institutional and material condition a country brings to any week — governance quality and rule of law (World Bank Worldwide Governance Indicators), how democratic the regime is (V-Dem electoral, liberal, civil-liberties and free-expression indices, newly loaded for 176 countries from 1990), development, economy, and militarization. Governance and democracy are deliberately separate dimensions: the World Bank measures whether a state is effective, V-Dem measures whether it is democratic, and the two come apart routinely. These inputs are annual and lagged, so this axis is slow by design — it describes the ground a shock lands on, not the shock.

The structural axis is scored against fixed good/bad anchors on each indicator's own scale rather than against a live cross-country ranking. A rank is zero-sum: it pins any world or regional aggregate near the middle no matter what is happening, and lets a country's score improve only because another country's got worse. Anchoring means a country's structural score depends only on its own condition and an aggregate becomes a real number that can move. Indicators whose meaningful range is multiplicative, such as GDP per capita, are anchored on a log scale.

2026-07-19

Entity Dossier is now included with Intelligence. GET /api/v2/entities/{entity_id}/dossier returns one entity's statecraft timeline — sanctions designations, FARA foreign-agent links, federal awards, coded events and tone in a single cited chronology, resolved across both the news and registry identity universes so government data and events line up. It was built and reachable only on internal plans; it is now part of the Intelligence tier and Enterprise. Atlas indices are likewise no longer marked "preview" in API responses: they have been included with Intelligence since the plan simplification, but every response was still stamped preview:true, which understated what the tier includes.

Energy assets moved from Core Data to Open Feeds. The /api/v2/energy/* endpoints serve Global Energy Monitor's published registries close to as-published, so they now sit with the other open feeds (government, filings, AI & compute, macro, risk, maritime) rather than alongside the events, stories and entities we produce ourselves. They are included from Analyst up. Existing Builder subscribers keep raw energy access — no action needed. The fused facilities directory (/api/v2/facilities), which is our own cross-silo join and owner resolution over GEM, the NGA World Port Index and Epoch AI, stays in Core Data and is unchanged from Builder up.

Fixed: the subscription page listed retired plans. Signed-in accounts with billing-alpha access saw the full historical plan list on /settings/subscription — including internal states that are not products — while /pricing correctly showed the four current tiers. Both surfaces now render the same catalog, plus your own plan if you are on a retired one.

2026-07-18

Simpler plans. The eight public tiers and five à-la-carte modules are now four plans, each a strict superset of the one below it: Free, Builder ($79/mo), Analyst ($399/mo) and Intelligence ($899/mo), plus Enterprise. Plans are organised the same way the API Arena is — Core Data (the events, stories, entities and facilities we produce), Core Analytics (media tone and share of voice), Open Feeds (government, filings, energy, AI & compute, macro, risk and maritime) and Intel (Briefs, alerts, the research agent and Atlas indices). Intelligence now includes every open-data feed, so the modules that used to be bought separately are simply included. The previous tiers — Monitor, Media Intelligence, Corporate & Supply Chain and Global Intelligence — are retired for new signups.

Existing subscribers keep everything. If you are on a retired plan you keep your plan, your price and every feature you have today; nothing changes and no action is needed. If you are on Builder, Analyst or Intelligence you keep the price you subscribed at, and your limits only go up — Analyst rises from 12,000 to 20,000 QU and 90 to 120 requests/min, and Intelligence from 180 to 300 requests/min with more included Briefs.

Query Units are now easy to size. Because our data refreshes hourly, keeping one entity, topic or query current costs about 750 QU a month — polling faster returns the same answer. Divide a plan's QU by 750 to get the number of things you can keep current: roughly 3 on Builder, 27 on Analyst and 137 on Intelligence. The pricing page states this rule directly, and Briefs continue to be metered separately and never draw down your query units.

Bounding-box filtering now works on Stories. /api/v2/stories and /api/v2/stories/summary accept bbox=lat_min,lon_min,lat_max,lon_max and keep stories with at least one linked event inside the box — the same linked-event geography semantics as country and admin1. Previously bbox was accepted on these two endpoints but silently ignored, so a bounded request quietly returned the global feed. Note that linked_event_count and fatalities remain full-story aggregates rather than box-restricted, and malformed or out-of-range values now return 400 INVALID_BBOX.

The published API reference now matches the live surface. We reconciled the OpenAPI spec and docs against the serving code: filters that worked but were undocumented (languages, include_images, seasonal_adjustment, foundry, filings country, and the full China project filter set) are now documented; parameters the server rejects or ignores were removed; and endpoints that shipped some time ago are no longer labelled "coming soon" or admin-only when they are in fact plan-gated and generally available.

New Atlas GPR — a Geopolitical Risk index built from our own coded events, in the tradition of the Caldara–Iacoviello GPR. A reading of 100 means a place is at its own 90-day norm, 200 means twice its usual, so countries stay comparable to themselves rather than to each other. Two lenses: Events (coded-event tension, decomposed into Threats, Acts, Material and Verbal) and Attention (the share of news attention that is tension-bearing — the lens closest to the original GPR). Places below the coverage floor return no reading rather than a fabricated zero. The page moved from /dashboard to /atlas and adds a world choropleth, a 30-day time machine, and per-country hover showing that country's top event or narrative. Atlas is a preview: the page is open to every signed-in user, while GET /api/v2/intelligence/gpr stays on the Admin plan until the history and evaluation gates pass.

2026-07-16

New API Arena — a single, live demo of every GDELT Cloud endpoint. The Arena consolidates the old API Playground and the per-source list pages (Events, Stories, Entities, Energy, Screening, and more) into one results-first surface: pick an endpoint from the left rail, fill real inputs with sensible defaults, and see the rendered result full-screen alongside a raw-JSON toggle and copyable curl/Python/TypeScript. Endpoints are grouped into Data (our feeds) and Intel (the Atlas indices; Briefs and Alerts are marked coming soon). Every request is a real, metered API call (1 QU). Any request is shareable and deep-linkable via its URL. The old page paths now redirect into the Arena.

2026-07-15

Broader global coverage — more events, especially from under-represented regions. Until now a single-source story was only coded into an event if it cleared a signal score that rewarded well-known publishers and heavy entity tagging — which quietly favoured large Western outlets and dropped genuine events reported by regional and non-Western sources. Coverage now follows what the article reports, not where it was published, with priority given to China, the Middle East, Africa, and Eastern Europe. Expect materially more events from those regions (arrests, protests, sanctions, corporate and infrastructure actions), with the same coding accuracy.

The event coder now runs on a more capable model across the board (see the 2026-07-14 note), so classification and location accuracy improve for every event, not just the newly added ones.

Deeper coverage of major, fast-moving stories. A new catch-up pass codes large multi-source narratives that build up across the day — summits, ongoing conflicts, disasters — that the hourly pipeline previously left uncoded. These now surface as events on /api/v2/events and in story feeds, without introducing duplicates.

More reliable ingest on the busiest news hours. The pipeline now persists its clustering before the coding stage, so if a heavy hour runs long, that hour's events are recovered by the catch-up pass rather than lost.

2026-07-14

Event coding accuracy: mis-classified events cut by 60%. We audited the coder by re-reading the exact source articles behind each event it produced and judging the event against them. On a random sample of 139 live clusters, 36% of coded events were wrong about their own sources — the wrong CAMEO+/ACLED type, or a location the text did not support. Three fixes bring that to 16%: (1) the coder now runs on a mid-tier model, because assigning a 200-code taxonomy to unstructured news is adjudication, not a small-model task; (2) it is no longer locked into the code family guessed from headlines — a router that reads only titles was pinning the coder's choices to a subtree that sometimes made the correct code unreachable, turning a routing miss into a guaranteed coding error; (3) each code now carries its official codebook definition, so a threat to impose sanctions is no longer confused with sanctions actually imposed, and a threat of military force is coded as one.

Events are no longer dropped because a news article did not date itself. The coder was instructed never to infer an event's date from the article's publication date, and to refuse anything lacking explicit same-day date evidence. Most news copy never states a date — it simply reports what happened — so the instruction, read literally, meant refusing most of the feed. Real arrests, protests, court verdicts and an airstrike were being declined for the sole reason that the article did not say 'on Wednesday'. An article reporting an action as current news is now presumed to be reporting it from that news cycle, and is dated to the source day unless the text places it earlier. Reports that explicitly describe older, background, anniversary or cumulative events are still excluded.

Story significance is now attention — the number of articles covering the story. It used to be 65% event-severity + 25% article count, which hard-capped an event-less Story at 0.35 no matter how large it grew. Because the biggest accreted narratives are the ones least likely to carry a coded event, that formula inverted attention: a 1-article Story with an event outranked a 265-article narrative, and the day's most-covered Story never appeared in the default ranking at all. Sorting by significance now means sorting by coverage. Event severity is unchanged and still returned separately as metrics.max_linked_event_significance.

Far fewer duplicate Stories. Same-day reconciliation only compared clusters that shared 3+ literal words in their headlines AND sat in the same category — a lexical gate on top of a semantic system. It was discarding real duplicates before they were ever considered: on one day it hid 1,157 of 1,373 true candidate pairs (84%), leaving a single story split across six live clusters. Candidacy is now decided by meaning alone (every cluster pair is compared), and every candidate is adjudicated by a model before anything merges. Expect materially fewer near-identical Stories, and article counts that reflect a narrative's true footprint.

Stories now count events that lag one day behind their cluster. An event whose date falls in the previous UTC day from its story's cluster date (a normal effect of the prior-day ingest window) was being dropped by the story's linked-events aggregation — so a story viewed on a single day (story detail / public story page) could show fewer, or zero, linked events than it actually had. The window now includes the one-day lag, recovering those links. Counts only ever increase toward the true value; no story loses events. Affects linked_event_count, the has_events filter, and story summaries.

2026-07-13

More reliable Event→Story links. When an event's story cluster is merged into a larger one during same-day reconciliation, the event's story reference now resolves to the surviving live story instead of occasionally pointing at a merged-away cluster (which returned a STORY_MERGED 404). Events on /api/v2/events and the per-event lookup now return their correct live story_refs / primary_story_url — or an explicit empty story when no live story exists — never a broken link.

Reconnected orphaned events. Events coded from clusters whose entire lineage was later merged away are reconnected to their live story via a semantic (embedding + LLM) match, recovering the story context they had lost. This runs forward automatically and was backfilled across recent weeks. No response-shape change — reconnected events simply resolve to a real story.

New endpoint: GET /api/v2/events/{event_id}/stories. Returns the live story cluster(s) an event belongs to, including a reconnected event's re-homed story. Each entry carries a relation of primary or reconnected, so you can tell a canonical link from a recovered one. Optional start_date / end_date narrow the window.

Consistent event→story links everywhere. The per-event story link in the Entity endpoint (/api/v2/entities/{entity_id}) now resolves through the same live-story logic as /api/v2/events, so chaining the two endpoints always agrees on which story an event belongs to — and shared story cards prefer the live cluster over a merged-away one.

2026-07-12

New Prediction Markets API (admin preview) — market-implied probability as economic-intelligence signal. /api/v2/markets serves an owned daily snapshot of geopolitically-relevant Kalshi prediction markets: implied YES probability, volume and open interest, and the market's resolution once it settles. Filter by a resolved entity (the same canonical entity selector as every other endpoint), category, theme, series, or FRED-linked markets only.

Bitemporal as-of + related FRED-series context. Pass as_of=YYYY-MM-DD to read the implied probability we captured on or before that date — no look-ahead. For macro markets (Fed, CPI, GDP, oil), the per-market view adds the point-in-time path of the related FRED/ALFRED series the market's family maps to — context on what the underlying macro series actually printed alongside the market's probability, each labelled with when it was known (it is related context, not the market's settlement — that is the separate resolution field). Market-implied probability is context/signal, not trading advice; unknown coverage is null, never a fabricated 0. Currently an admin preview (like the Dossier); plan availability will be announced at general availability.

2026-07-11

Sharper Monitoring Briefs — we upgraded the model powering Briefs to OpenAI's newer gpt-5.6 generation. In our head-to-head evaluations the new writer produced better-grounded briefs — roughly twice the GDELT evidence cited per brief — at a lower token cost, and the research step that gathers evidence now surfaces more source material. No change to how you request or receive a Brief.

2026-07-11

API normalization — a canonical entity selector. /api/v2/facilities and /api/v2/energy/assets now accept a single canonical entity parameter: pass a resolved spine id (e_…) for an exact owner match, or any name for a case-insensitive fuzzy owner-name match. Using one consistent selector means you can reuse the same entity identifier as you chain calls, instead of learning a different owner field for each endpoint.

Alias-and-deprecate — nothing breaks. The legacy owner_search parameter is now marked deprecated in the API reference but remains fully supported; owner_entity_id continues to work as the explicit opaque-id form. No parameter or response field was removed.

Uniform pagination — the Epoch AI endpoints now return next_cursor. The /api/v2/epoch/* endpoints (models, hardware, data-centers, companies, chip-sales) gained a canonical next_cursor page token and accept cursor as the page-token input, alongside the existing next_offset / offset (now marked legacy but still returned). Follow pagination.next_cursor and pass it back as ?cursor= — the same cursor loop already used by the Events, Stories, Facilities and Energy endpoints.

2026-07-11

New Foreign Agents (FARA) API — the US foreign-influence graph. /api/v2/gov/fara returns DOJ FARA registrations linking a US registrant (a law / lobbying / PR firm) to the foreign principal it represents, both resolved to the entity spine. Filter by a resolved entity (either side of the link), a fuzzy registrant or foreign-principal name, or a country. Available on the Markets plan and up (can_use_gov).

Fused to our sanctions data — the differentiator. sanctioned_only=true returns only links whose foreign principal is on one of our screening lists (OFAC SDN, BIS Entity List, DoD 1260H, OFAC CMIC, UK), with the specific lists and sanction programs attached — so you see US firms registered as agents for a sanctioned entity, not merely for an adversary country.

Also on the MCP server (gov_fara in the gov category) and in the API Playground (a Foreign Agents card with live examples). Data provenance: FARA is US public domain (DOJ NSD FARA eFile), one row per (US registrant, foreign principal) link.

Government responses now carry a cross-source footprint. /api/v2/gov/awards and /api/v2/gov/fara (and the entity Government panel) add a cross_source block for the resolved entity — its foreign-agent status (including principals on our sanctions lists), whether it's an SEC filer, and how many mapped facilities it owns — plus headline flags like “federal contractor + foreign agent” and “foreign agent for a sanctioned entity.” One lookup, the whole spine: which US firms take federal money and lobby for foreign or sanctioned governments — a join no single-source viewer offers.

2026-07-10

New Government exposure API — an entity's US federal award footprint. /api/v2/gov/awards returns an entity's USAspending federal award timeline (prime contracts + grants) with a per-recipient rollup — total obligated, awarding agencies, first/last action date — resolved to the entity spine and keyed on the SAM.gov UEI. Look a recipient up by a resolved entity id, a SAM.gov UEI, or a fuzzy name. Available on the Markets plan and up.

Fused, not siloed. Because recipients resolve to spine entity ids, federal spending joins the live Events / Stories / Entities / Facilities graph — from one entity you can pivot to its awards, the facilities it owns, and the events nearby.

Same surface on the MCP server (the gov category: gov_awards) and in the API Playground (a Government card with live examples). Both share the can_use_gov gate.

Data provenance: federal awards are US public domain (USAspending.gov), keyed on the SAM.gov UEI — Dun & Bradstreet DUNS numbers and the D&B corporate-family tree are excluded by license and never surfaced.

An EPA ECHO enforcement surface (facility-level violations and penalties, geo-matched to the facilities directory) is in development and will follow once its operator-attribution precision bar is met.

Stories pagination now walks the entire result set. /api/v2/stories moved from offset to keyset (seek-after) cursors: follow pagination.next_cursor until it is null to page through every story in your date window. Previously the cursor silently stopped at ~1,000 stories per query — a single busy day can hold well over 10,000 — so long walks were being truncated with no error.

What to do: keep reading pagination.next_cursor and passing it back as ?cursor=… — same loop as before. The token is now an opaque string rather than an offset number, so treat it as opaque (a hand-crafted numeric ?cursor=N is no longer honoured on this endpoint and restarts at page 1). sort=significance (default) ranks by significance; sort=recent gives a strictly chronological walk. Semantic search (?search=) is unchanged — it stays a relevance-ranked top-K, not a full walk.

2026-07-09

Entity Tone & Share of Voice now accept spine entity ids and typed ids. You can pass an e_… id straight from /api/v2/search — or a person:/organization: id — to /api/v2/entity-tone or /api/v2/share-of-voice and it resolves to the same entity the series is keyed on (previously a spine id returned 400, and a typed prefix could bind to a wrong same-name fragment). Resolution by plain name is unchanged.

API Playground refreshed. Every endpoint now ships a tight set of 2–3 diverse, verified examples — each confirmed to return live data — Entity Tone leads with a broader cross-sector set (politics, energy, defense, plus a per-language English-vs-Chinese example), and the Briefs card was removed. Entity Tone and Share of Voice now render as visual cards — a diverging tone trendline with a reputation-spectrum read (and separate risk view), and a share-over-time chart — instead of a raw field table, and no longer show an empty state on responses that actually have data. No REST API changes.

Facilities directory expands to heavy industry and reorganizes ports. /api/v2/facilities now includes ~5,700 GEM heavy-industry production plants — steel, cement, and chemical — plus iron-ore mines, each geolocated and (for cement/chemical, at ~100%) resolved to its owner on the entity spine. New facility types: steel_plant, cement_plant, chemical_plant, iron_ore_mine.

Maritime ports are now their own category. `port` moves out of transport_logistics into a dedicated `ports` class, and a new `heavy_industry` class groups the steel/cement/chemical plants — so the coarse class filter is now power / extraction / transport_logistics / ports / heavy_industry / digital_infrastructure (21 leaf types in total, ~216,400 facilities).

The new types and classes are live across the REST API, the MCP facilities tools, and the API Playground.

Better facility geography. All 67 AI data centers are now geolocated (previously coordinate-free), and ~4,950 gas & oil pipelines carry their route geometry (from GEM's published GeoJSON) plus a representative point — so data centers and pipelines now render on maps and respond to the near / bbox geo filters. Point-geo coverage across the directory is now ~99%.

2026-07-08

New Facilities API — a unified physical-asset directory. /api/v2/facilities puts energy assets (Global Energy Monitor), ports (World Port Index), and AI data centers (Epoch AI) on one keyed surface, each resolved to its owner entities on the spine, so you can ask what exists where, of what type, and who operates it. Filter by name, facility type or coarse class (power / extraction / transport & logistics / digital infrastructure), source, country/region/continent, owner (name or entity id), status, capacity (MW), and geography (bounding box or radius); fetch one facility with /api/v2/facilities/{facility_id}. Available on the Analyst plan and up.

Search or pivot by owner. Every facility carries its owners as spine entity ids, so a facility chains straight into the unified entity graph (news, filings, exposure, screening) — and owner_entity_id returns an operator's entire cross-silo portfolio in one call.

Same directory on the MCP server and in the API Playground. The facilities_search / facilities_get / facilities_by_owner tools mirror the REST surface for agents, and a Facilities card in the Playground exposes the full filter set with live examples.

v1 is directory-only (Epoch data centers ship without coordinates for now); the facility-to-event fusion is a separate upcoming stream.

2026-07-05

Plans restructured around use-cases, and the advanced data modules go GA. The paid ladder is now segment-named — a Monitor tier for team event/story monitoring, Media Intelligence (tone + Share of Voice), Corporate & Supply Chain (SEC filings, supply-chain & sanctions/ownership risk, macro), Geopolitical Intelligence, and Global Intelligence — plus a custom Enterprise tier. Each plan card lists exactly which data modules it includes, and modules are attachable à-la-carte on lower tiers.

SEC EDGAR filings, FRED/ALFRED macro, maritime AIS signals, Epoch AI compute data, screening-list context, ownership exposure, and China-Abroad move from internal preview to plan-gated general availability (rolling out with the new plans). API responses for non-entitled keys return 403 with code PLAN_REQUIRED instead of ADMIN_REQUIRED.

Risk Context (screening lists, ownership exposure, China-Abroad) ships as best-effort, cited reference context — not a compliance product; see the new Risk Context section on the methodology page for provenance, cadence, and appropriate-use boundaries.

Share of Voice API requests are now bounded for reliability: at most 25 entities per request and a 92-day date window (error codes ENTITY_LIMIT_EXCEEDED / WINDOW_TOO_LARGE).

Existing subscribers are grandfathered: your price does not change, and your plan gains the modules added to its tier.

2026-07-06

Entity list metrics now match entity detail. The entities list (/api/v2/entities) previously counted activity over the last 24 hours while the entity profile counted 30 days — so a list row could read 0 articles for an entity its detail showed as highly active. The list now uses the same 30-day default window, and a row with no in-window coverage no longer carries a stale last-seen date. Each list result also returns a resolvable id that round-trips directly into /api/v2/entities/{id}.

New severity tier on events. Every event now carries metrics.severity_tier (critical / high / medium / low) derived from casualties, civilian targeting, and escalation magnitude — an absolute band you can alert on, independent of the continuous significance score. Relatedly, significance now weights fatalities by body count, so a mass-casualty attack ranks above a zero-casualty diplomatic event.

Cleaner geographic rollups. group_by=country summaries now render readable names for aggregate and territory codes (European Union, Worldwide, Tuvalu, …) instead of leaking raw ISO/region codes, and an empty-country bucket is labelled Unknown rather than appearing as null.

Energy assets are better connected and clearer. Asset owners now carry a spine entity id where resolvable (so an asset chains into the unified entity, exposure, and screening); Heavy-Industry assets (iron & steel, cement, chemicals, iron-ore) are queryable on /api/v2/energy/assets via tracker=…; a new group_by=start_year (and decade) returns annual build-out; and asset rows no longer surface a placeholder 1970-01-01 date.

Share-of-Voice responses now use the standard {success, data} envelope like every other endpoint (the existing rows/status fields are retained for compatibility), and the semantic-denominator query is far faster and no longer intermittently times out. China development-finance responses carry an explicit coverage window (AidData GCDF 3.0, commitments through 2021) so the historical dataset isn't read as live. Unknown /api/v2/epoch/* paths now return a JSON error instead of an HTML 404.

The redundant /api/v2/entities/resolve endpoint is retired — use /api/v2/search, which returns the same resolution plus cross-source identifiers and a per-source availability map.

2026-07-06

Consistent country handling across the API. Any endpoint that filters by country now accepts a country name (United States), an ISO-2 code (US), or an ISO-3 code (USA) — plus common aliases like UK or Czechia — interchangeably, instead of each endpoint silently returning zero rows on the “wrong” code. An unresolvable value returns a clear 400 (INVALID_COUNTRY) listing the accepted forms.

Richer filters on the preview surfaces. AI models and hardware can now be filtered by publication/release date (date_start / date_end) and models by accessibility tier (open-weights, API access, hosted, unreleased); SEC filer relations can be filtered by counterparty jurisdiction and now include partner relationships; China development-finance projects can be filtered by project completion year; and events gained a point-radius proximity filter (near=lat,lon or lat/lon with radius_km).

Completed value pickers. The documented enums now list the full sets an endpoint accepts — the 19 AI-model domains, 11 hardware types, and 6 accessibility tiers; the 11 material-event types (8-K) including disposition, restructuring, impairment, delisting, and bankruptcy; and the full SEC relation kinds — so the API Playground and reference show every valid option.

Cleaner workflow chaining. Cross-source results round-trip on a single canonical entity id — resolve or search an entity, then pass its id straight into exposure (which now auto-selects the entity lens for you), tone, filings relations, or energy owners without reformatting. The API reference and Playground are reorganized from data-source silos into use-case groups — Events & Narratives, Entity Intelligence, Media Intelligence, Risk & Screening, Company Filings, Energy & Industry, Maritime & Trade, Markets & Macro, and AI & Compute — so the screening, media-intelligence, and cross-source join endpoints are discoverable instead of buried.

2026-07-06

Unified cross-source entity search is sharper and faster. A single query now fuzzy- and semantic-matches across every source — news, SEC/EDGAR, Global Energy Monitor, sanctions lists, China development finance, and Epoch AI — and returns one result per real-world entity with a normalized type (person, company, government, state body, …) instead of source-specific labels or a generic “entity”. Each result carries a per-source availability map, and its cross-source links now round-trip cleanly into the matching entity profile.

API Playground now shows an IDE-style code view that defaults to Python, and the curl / Python / Node snippets are the same literal call across every tool and result — copy-paste runnable, with path parameters placed in the URL. Preview showcases (AI Compute, Maritime, Unified Search) gained richer parameters and ready-made presets.

Data-quality fixes across preview surfaces: Epoch AI model filters return correct results for the frontier flag and expose real JSON booleans plus an applied-filters echo; the China development-finance endpoints accept the common parameter aliases; restricted-party list queries validate their inputs; entity Media Tone timelines render consistently; and unified entity search returns cleaner, less noisy results.

2026-07-04

Global Energy Monitor Heavy Industry — cement, chemicals, and iron-ore assets now join iron & steel on entity pages.

An entity's page shows the cement plants (3,515 tracked), chemical plants (868), and iron-ore mines (949) it owns, resolved through the GEM ownership graph into the unified entity registry.

2026-07-04

New Epoch AI dataset (CC-BY 4.0) — AI models, ML hardware, AI data centers, and chip sales, unified into the entity registry.

Entities like NVIDIA or Anthropic now surface the AI models they developed, hardware they make, data centers they own, and cumulative chip sales. Admin preview, with a dedicated /ai explorer, an AI & Compute section in the API Playground, and the /api/v2/epoch/* API.

2026-07-04

Homepage live 'Entities' counter now reflects our resolved/unified entity layer. It previously counted raw Wikipedia-linked mentions, which lag ingestion by 1–3 days and undercount; it now reports distinct resolved canonical entities over the last 24 hours.

2026-07-04

Energy & industrial assets on entity pages. For companies tracked by Global Energy Monitor — steelmakers, power producers, and their parents — profiles now show an Energy & Industrial Assets panel: the iron & steel plants they own (with steelmaking capacity) plus their ownership graph. This is included on every plan.

The entity API response is now organized into per-source sections. Alongside the existing fields, /api/v2/entities/{id} returns a `sources` map describing each data source for the entity — news, coded events, media tone, energy assets, share of voice, SEC filings, sanctions screening, and China development finance — each marked available and entitled, so you can see at a glance what an entity carries and what your plan unlocks. Each source is now an independent, per-plan feature; a source your plan doesn't include returns a uniform locked state that never discloses whether the entity has that data.

Homepage story count deduplicated. The 24-hour “stories” total on the homepage previously counted merged-away near-duplicate clusters, inflating it by roughly 15%. It now uses the same deduplicated figure the API and entity pages already report.

2026-07-02

Media Tone coverage expanded across the board. A rollup step was leaving many entities' computed tone unserved — their stories were scored, but the daily tone summary the API and entity pages read didn't reflect it. That rollup now folds in every scored story, so roughly 11,000 more entities (well-covered and lightly-covered alike) show their real Media Tone, and thin/single-source entities went from ~42% to ~65% tone coverage.

Honest tone availability: an entity whose only coverage was too thin to score (single-source stories with no extractable evidence) previously reported tone as "available" with zero scored stories. It now correctly reports tone as not-available, so "available" always means there is at least one scored story behind the number.

2026-07-01

Entity pages now resolve consistently to the canonical entity. A person or company profile could occasionally split into a near-duplicate keyed to a misspelled or translated variant of the name (e.g. “Donal Trump” vs “Donald Trump”): that variant page showed near-zero stories / articles / events and an empty 30-day activity pulse, even though the Media Tone panel — resolved separately — showed the entity's full coverage. Every section now keys off the same canonical entity, so the header counts, activity pulse, linked stories / events, connected entities, and tone all agree — and a variant-spelling URL 301-redirects to the canonical entity page.

Entity pages for lightly-covered figures no longer show an empty story list. When an entity has real, Wikipedia-linked coverage but too little to be promoted into our resolved entity registry (e.g. a founder named in a handful of funding stories), the header counted the stories while the Linked Stories panel showed “none” — a mismatch. The linked-stories list now draws those stories from the same derived Story layer as the counts, so the two agree.

Media Tone now covers lightly-covered entities. Tone was previously computed only for entities with enough multi-source coverage, so a figure who appears mostly in single-source stories (many founders, regional officials, and other niche entities) showed an empty Media Tone panel even when the rest of the profile had data. Tone is now computed across all of a lightly-covered entity's stories — including single-source ones, which are often all it has — so these profiles carry a mean tone, risk, and confidence like any other, with a confidence score that honestly reflects the thinner evidence. Well-covered entities are unchanged: their single-source stories are still excluded so their aggregates stay clean.

2026-06-30

SEC Filings resolve is now fuzzy and cross-source. Resolving a company by name returns a ranked list of close matches (not a single guess), and a common name now finds the real filer — e.g. “SpaceX” resolves to SPACE EXPLORATION TECHNOLOGIES CORP (CIK 1181412) via the entity graph, instead of a namesake investment fund. Resolved companies, filers, 8-K counterparties, and material-event entities now carry the link to jump straight to their GDELT Cloud entity page.

API Playground (early-access SEC surface): result cards now showcase the structured data. Material Events lead with the extracted headline, event-type, SEC item, counterparties, deal value, and a confidence score; filer profiles show a business description with segments and products; and Resolve shows the ranked candidate list with cross-source entity links.

Macro API: the Series catalog gained a seasonal-adjustment filter, and the Releases rollup is now filterable by originating agency or a release-name search (previously a fixed, unfilterable list).

2026-06-29

Entity & event profiles no longer repeat a story: when several near-duplicate clusters share an identical headline on the same day, the recent-stories list now shows it once (keeping the highest-coverage cluster). The same headline on a different day, and merely similar headlines, are preserved.

Entity list counts now match the entity profile. Per-entity article / story / event counts in GET /api/v2/entities come from our resolved entity layer — the same source the single-entity endpoint uses — so the list and the profile always agree (previously the list read a pre-resolution layer that could over- or under-count).

Filtered entity counts are now labelled. When you filter entities by an event category (e.g. Protests) or attribute (e.g. civilian-targeting), the counts are scoped to that filter and the response flags this with a metrics_scope field, so a small “2 stories” reads correctly as “2 protest-linked stories” rather than looking broken.

Better ranking for filtered entity queries: filtered entity lists now rank by how specific an entity is to the filter rather than by raw mention volume, so “protest-linked organizations” surfaces protest-specific bodies (parties, unions, committees) instead of being dominated by ubiquitous entities that co-occur with everything.

API Playground: the Code view (curl / Python / Node) now renders the exact public API call — path parameters such as entity_id are placed in the URL path, so the snippet is copy-paste runnable — and every record/list endpoint now defaults to 10 results.

2026-06-28

Coming soon (early-access preview, admin-only for now): a material-events feed at GET /api/v2/filings/events — dated 8-K corporate events (mergers and acquisitions, material agreements, executive changes, bankruptcies, delistings, impairments) extracted from 8-K bodies, with named counterparties resolved into the entity spine so a filed event joins the live Events/Stories/Entities graph on entity and date. Filter by company (cik), event_type, or a headline search over a ≤30-day window.

This SEC Filings surface is being finalized and is not yet generally available — it returns 403 ADMIN_REQUIRED for other API keys while we ready it for all plans. We'll announce general availability here when it ships.

8-K coverage was also improved: the pipeline now scans the newest filings first with a higher daily cap, so material events surface sooner and more completely.

2026-06-28

Event recall (methodology): the events pipeline now captures incidents reported a day after they occurred or across a timezone boundary — common for diplomacy (state visits, agreement signings, leaders' remarks) and late-breaking conflict updates that previously fell outside the single processing day and went uncoded.

Events are dated to the day the action actually occurred, which can be the prior UTC day, so date filters and timelines reflect true occurrence dates. You may notice more coded events overall and some dated to the previous day. De-duplication runs across adjacent days, so an incident reported over two days remains a single event.

Entity coverage counts (fix): per-entity article / story / event counts now consistently reflect GDELT Cloud's own Stories and Events over the selected window. Previously a high-profile entity surfaced by name search could show a large article count next to 0 stories and 0 events — an inconsistency from mixing an all-time article figure with windowed linkage. All three counts now come from our resolved entity layer and are mutually consistent, so some entities will show smaller but accurate counts.

2026-06-26

Internal preview (admin-only): added maritime vessel-flow signals derived from public and commercial AIS vessel-tracking sources. The product exposes chokepoint transit activity across 11 maritime chokepoints (Hormuz, Bab-el-Mandeb, Malacca, Suez, Panama, and others), per-chokepoint dwell time and AIS-dark gaps, and last-known vessel positions and identity — vessels matched to Global Energy Monitor's LNG-carrier registry.

These are derived signals — transit counts, dwell, gaps, and last-known positions — not a raw position firehose or full track history. There is no historical backfill: maritime signals accrue from launch forward only. Coverage is terrestrial-AIS, so open-ocean segments outside shore-station range are not observed.

Macro (FRED) preview: broadened the curated economic-series catalog to ~90 series spanning national accounts, prices/inflation, labor, rates/yields, money/credit, trade, housing, business activity, consumer, and commodities/markets. The catalog is now fully discoverable — browse the live list at GET /api/v2/macro/series (call with no filters to enumerate everything), see the grouped reference at docs.gdeltcloud.com/api-reference/macro-series-catalog, and unknown series_id responses now point to both.

2026-06-25

Internal preview (admin-only): added SEC EDGAR filings to the V2 API under /api/v2/filings — list and summarize filings by company, form type, and date; a per-filer profile with recent filings and XBRL financial highlights; an XBRL facts slice; and name/ticker resolution into the entity registry. EDGAR content is public domain; cite the SEC as source.

Corporate relationships (subsidiaries, suppliers, customers, jurisdictions) are surfaced from filing text through our proprietary AI pipeline and resolved into the entity registry as typed, traversable edges, so a news event can be traced to the public companies that disclosed exposure.

MCP: a new progressive-discovery tool category (filings) surfaces the above to the research agent; admin-only while in preview.

Internal preview (admin-only): added official U.S. economic time series (Federal Reserve / FRED and originating agencies) to the V2 API under /api/v2/macro — a searchable series catalog, point-in-time (ALFRED) observations with an as-of vintage parameter so a query never looks ahead, per-series detail, and an agency/release rollup. Third-party-copyrighted series (e.g. S&P, ICE) are never served, and macro text never enters any embedding corpus. This product uses data from the Federal Reserve Bank of St. Louis (FRED) but is not endorsed or certified by it.

2026-06-24

Admin billing now reports recurring revenue as an explicit matrix: effective MRR, effective ARR, current-month paid invoice cash, and year-to-date paid invoice cash, each split across monthly-billed and annual-billed subscriptions.

Admin outreach queues now exclude live paying customers even when cached prospect rows are stale, add "other" and "already paying" dispositions, and separate To-Do and Done loading so admins can reach actionable prospects faster.

Admin user pages now surface plan source, access, overrides, and current-period usage together, and Brief access now consistently honors frozen billing state across current and retained Brief routes.

2026-06-22

Redesigned the homepage and public navigation for a calmer, more enterprise feel: a focused hero with a subtle cursor-reactive "world-state" background (events drifting, stories clustering, fresh signals pulsing), the live-data video demoted to a small framed accent, and the per-surface deep dives (API, MCP, Agent, Alerts, Briefs) moved into structured Product / Solutions / Resources menus. Existing pages keep their URLs.

Added a "See it live" section previewing the distinctive surfaces — Event Taxonomy, Early Signals, vs Raw GDELT, Briefs Showcase, and the interactive Demo — each with a small data-literal animation (media-tone meter, share-of-voice bars, event-to-story clustering, an early-signal sparkline). All motion respects the reduced-motion preference.

Introduced an opt-in customer logo strip on the homepage for subscribing commercial customers. It is off by default and admin-curated: each customer is added explicitly and has an individual on/off switch for removal requests, consistent with the paid-customer logo-use clause in the Terms.

2026-06-20

Entity resolution now recognizes former names and common acronyms for renamed organizations. Searching a prior identity — e.g. "NARAL" or "NARAL Pro-Choice America" — now resolves to the current canonical entity (Reproductive Freedom for All) as a strong, exact match instead of returning unrelated typo neighbors. This flows through the resolver, Entity Tone, and Share of Voice.

Seeded an operator-maintained alias layer for a starter set of renamed advocacy, political, and corporate organizations, with optional enrichment from Wikipedia redirect titles and Wikidata also-known-as names. Former corporate names (e.g. "Facebook, Inc.") resolve to the current entity while staying distinct from same-named product entities.

Stories and Events now carry a per-card source-language breakdown (e.g. EN 6 · AR 2 · ZH 1) and accept a "Source language" filter, so you can isolate Arabic, Chinese, or any source language across the story and event feeds.

Entity profile pages now include a Media Tone preview: the mean tone of press coverage toward the entity over the last 30 days (−100 critical to +100 favorable), with scored-story and article counts, a reputational-risk read, model confidence, a daily tone-trend sparkline, and coverage languages. It measures how the press covers the entity — not public opinion or polling — and missing coverage shows as unavailable, not neutral.

The Data Status page now reports the distribution of source languages across ingested coverage (24-hour and 30-day windows).

Regional source coverage was expanded and hardened: restored WAF-blocked Gulf/Levant feeds, added Global Times and Argaam (Gulf energy/markets) plus additional Arabic outlets, and retired confirmed-dead frozen feeds — improving Arabic and Chinese hard-news flow into Stories, Events, and Entities.

2026-06-19

Chinese-language coverage is substantially expanded: the native-source fetcher now resolves each article's title from its page (og:title/<title>) instead of relying on homepage link text, restoring fresh hard-news flow (politics/economy/diplomacy) from People's Daily, Guangming Daily, Yicai, Eastmoney, 21st Century Business Herald, China Daily, CCTV, and Beijing News into Stories, Events, Entities, and Entity Tone. Feed-less or low-signal sources (the Xinhua homepage, Guancha, Securities Times, China Economic Net) were retired in favor of GDELT translingual, China News Service, and CGTN coverage.

Native Arabic/Chinese regional-source articles now contribute article images, provenance, source-language metadata, and gate-spotted entities into the existing V2 Stories, Events, Entities, Entity Tone, and Share of Voice surfaces.

The hourly product pipeline now turns native gate entities into article mentions, resolves likely Wikipedia pages with a GPT-5.4-nano query step plus the Wikipedia API, and carries active non-Wiki `llm:*` entities through V2 entity refs and entity detail responses.

The Ingest Command Center regional-coverage panel now tracks native URLs, sources, domains, product articles, Stories, Events, and visible entity-link contribution over the rolling 24-hour window.

Public story/entity image enrichment now gracefully skips optional native-source image columns until the ClickHouse migration is applied, and the public Briefs showcase is cache-backed while still refreshing immediately after admin curation or share/revoke actions.

Public Story, Event, Entity, and shared Brief detail pages now use route-level ISR again while preserving client-side page-view tracking and hourly data freshness.

Early Signal public pages now publish from slimmer snapshot-backed payloads and avoid deploy-time monitor/run enumeration, making scheduled marketing scorecards more reliable while preserving per-run archive links.

2026-06-18

Native Arabic/Chinese source ingest is now available behind eval-safe schema routing, with raw native-source audit tables, article provenance bridging into `gdelt_cloud`, and a local HTTP e2e acceptance script for V2 stories, events, entities, Entity Tone, and Share of Voice.

Public Data Status, demo, and GDELT comparison pages now use tighter cache lanes, route prewarming, and smaller first-load client islands so customers see faster public-page loads while hourly data freshness is preserved.

Data Status now serves a tagged hourly snapshot and lazy-loads detailed metric distributions from a cached public endpoint; fixed-window demo pages avoid unnecessary sub-hour warehouse refreshes.

Entity search restores all-time Wikipedia-linked recall for People and Organizations: explicit entity-name searches now use the GEG Wikipedia lookup alongside recent activity tables, and Entity Tone / Share of Voice resolve those searches to the same canonical wiki IDs.

2026-06-17

Admin-granted plan access now applies to the user's personal organization, mirrors back to the owner profile, and preserves Stripe billing state so demo grants unlock runtime features such as Briefs, API, and MCP without creating duplicate subscription drift.

Payment safeguards now continue to honor active, past-due, and unpaid Stripe subscriptions even when an admin plan grant is masking the visible plan source.

2026-06-16

Entity analytics search is now shared across the new entity registry, entity resolution, the legacy Entities list, and risk/exposure lookup paths, with fuzzy alias, short-name prefix, acronym, typo, token-set, and semantic-vector tiers where embeddings are available.

Share of Voice denominators that include query text now use semantic cluster search and return explicit denominator metadata: search mode, score floor, candidate cap, truncation status, and separate numerator/denominator counts.

Entity Tone evidence is easier to audit: samples are collapsible, story links use readable Story titles, article titles are cleaned, source domains are clearer, and entity names are shown separately from evidence titles.

Admins now have an Entity Universe overview for registry size, alias/vector coverage, resolved-story coverage, tone-scored coverage, date ranges, and non-Wiki extraction status. Risk and Exposure pages now include helpful defaults, scenario chips, and fuzzy counterparty resolution.

Energy Data now targets the May 2026 GEM coal mine and ownership releases and adds owner lookup endpoints plus MCP tools, so asset portfolios can be discovered from canonical GEM ownership entities before drilling into assets or exposure.

Automatic QU overage attempts now retain local invoice diagnostics for failed Stripe paths, including idempotency key, retry index, payment-method source, invoice IDs, and whether stranded open invoices were voided.

2026-06-15

Entity Tone and Share of Voice are now additive beta API surfaces: cached entity-tone reads when side-table scores exist, optional evidence samples for review, denominator-explicit share-of-voice responses, plot-ready SOV timelines, and durable beta queue rows for on-demand scoring requests.

The tone methodology separates media tone from reputational risk, treats missing evidence as unavailable rather than neutral, and keeps scoring output in side tables instead of changing the existing ingest workflow. Admin-account dogfood pages now expose Entity Tone and Share of Voice with API View.

The V2 entity-tone scorer now supports high-volume concurrent processing with a per-run Tavily fallback budget, so hourly dogfood ingest can cover thousands of queued entity-story cases without making web extraction the critical path.

2026-06-11

Bonus query-unit top-ups and time-boxed plan trials are now explicit offers: the email includes a Claim button, and nothing is applied to your account until you accept. Offer links expire after a set window, and you can always reply to the email to discuss or request something different.

2026-06-10

Query Unit grants and time-boxed limit bumps are now fully honored against the per-period usage counter: a granted account can use its entire grant before reaching its limit, where previously the effective ceiling could narrow as the grant was drawn down.

Automatic overage now measures the chargeable amount against the full free allowance — base limit, already-paid overage, and any active grant or limit bump — so granted or bonus Query Units, and headroom from a bump that has since reverted, are never billed as overage.

2026-06-08

V2 Events now support canonical metric range filters on list and summary endpoints, including significance, confidence, Goldstein scale/severity, magnitude, systemic importance, propagation potential, and market sensitivity.

The Events UI, API Playground, OpenAPI reference, docs, and MCP search/summary tools now expose the same metric filter contract, with CAMEO+ and Goldstein applicability documented.

The API Playground now includes dedicated V2 Event Summary, Story Summary, and Energy Summary tools with authenticated execution, presets, snippets, and rendered summary rows.

Data Status now shows rolling 24-hour and 7-day distributions for Event metrics using partition-pruned ClickHouse queries.

2026-06-07

Organization settings now refresh correctly when switching active organizations, so the organization name, slug, domain, member-key setting, billing panels, and usage panels stay aligned with the selected workspace.

Brief-originated tool usage now records 0 Query Units. Briefs remain metered in Brief Units, and future Brief usage rows no longer appear at the hosted research-agent QU rate.

Admin usage monitoring now groups feature usage into product surfaces such as V2 API, MCP, Agent, Brief, and Web App, with paginated feature tables plus member and API-key drilldowns and CSV exports.

Organization plan enforcement has been tightened for Alerts and Briefs: active-organization plan features, limits, Brief Units, purchased credits, schedules, and API-key usage now stay scoped to the workspace that owns the request.

Admins can now add user- or organization-specific entitlement overrides for plan-controlled quotas, permissions, feature flags, Brief limits, API rate limits, and overage settings without changing the account's current plan.

Admins can also issue one-time QU/BU grants with optional expirations and save custom user- or plan-level billing discounts that are applied during subscription checkout or plan changes.

Google sign-in now keeps signup-only welcome and founder follow-up emails out of returning-user sign-ins; signup emails are owned by the initial auth.users insert path and remain idempotent.

2026-06-06

Terms now include a limited customer identification/logo-use clause for active paid customers only, and paid plan cards link directly to that section so the subscription flow is explicit.

Admin Growth now reports one fixed UTC week-to-date comparison against the previous Monday-aligned window, with Gross Monthly Income separated from Net MRR and canceling, at-risk, and churned/lost states broken out.

Admin user-related list pages now default to 20 rows with pagination and simple case-insensitive email substring search where relevant, reducing slow initial admin loads.

Attio CRM sync now mirrors new signups and plan-status changes into People records using the imported email/name/account-created/plan/plan-mode mapping, with local development guarded to TEST-marked records only.

Attio People sync now supports validator-first daily usage rollups for recent activity, QU, and Brief Units, gated behind ATTIO_USAGE_METRICS_ENABLED.

2026-06-04

Monitoring Briefs now lead with an Event Timeline in the body of the report — a horizontal, date-anchored chronology of the GDELT Cloud Events the brief cites (not just a short list), matching the Early Signals timeline. Hover any event for its date, category, and significance.

New “Relevant entities” section. Each brief now surfaces the load-bearing People and Organizations driving the question, with their window-over-window article/story/event activity and a one-line role — drawn from the GDELT Cloud entity graph the research traversed.

Maritime briefs now pull a temporary, open-web AIS picture (vessel diversions, AIS gaps, port congestion from sources like MarineTraffic and Lloyd's List), clearly labeled as open-web-derived rather than a live feed, until a dedicated AIS feed is wired in.

The Brief library drawer now groups every run of a Brief under one definition: open an entry to see its runs split into Manual and Scheduled, with shared and searchable status shown at a glance. Schedules and their runs now live in one place.

Consistency fix: the “What changed”, comparison-windows, and benchmark visuals now derive from a single source as the Lookback/Baseline columns, so the chart and the table can no longer disagree.

Research now opens broad before narrowing — a wide discovery pass surfaces the actors, angles, and second-order threads up front, so briefs miss less relevant context from searching too narrowly too soon.

Brief polish: entity cards now show each actor's Wikipedia image (the same one on their Entity page), Event Timeline cards lift to the front on hover so an overlapped event stays readable, and official-source and article cards now link out to their source.

Prediction-market readings are now self-consistent: when a brief's quoted probability disagrees with the market's own captured history (for example, a long-horizon contract's odds mistaken for a near-term one), the headline is reconciled to the live series — so the number can no longer contradict the sparkline shown beside it.

2026-06-03

Organizations are here. Every account now belongs to an organization — your existing account is grandfathered into a personal organization with nothing to change. Create a company organization to invite teammates and share one subscription, API keys, and pooled usage. Invite by email; people who already have a GDELT Cloud account are linked automatically, and there's an organization switcher in account settings.

Owner / Admin / Member roles. Owners and admins manage members, API keys, and billing; members use the product and see organization usage. API keys are organization-scoped, with one-click rotation, last-used timestamps, and an audit-lite activity log. Members can create their own organization keys by default; an admin can turn that off, and a member's keys are automatically revoked when they leave the organization.

Annual billing. Plans can be billed monthly or annually (two months free by default). The pricing and subscription pages have a Monthly/Annual toggle that defaults to Annual; annual plans show the effective monthly price ("billed annually") so it's easy to compare with monthly.

Team features are now a plan-tier capability. Inviting teammates to a company organization — sharing one subscription, API keys, and pooled usage — is available on plans that include teams (set per plan by admins). Solo personal workspaces are unaffected; organizations on a plan without teams see an upgrade prompt instead of an invite.

Clearer, consistent usage reporting. Usage is pooled and reported per organization, broken down by feature, member, and API key — including Briefs. Plan limits, Query-Unit usage, and overage now follow your active organization everywhere — the web app, API Playground, alerts, and MCP — so personal and organization usage never mix. A color-coded indicator at the top of every page shows which workspace you're in, and admins can export usage to CSV.

Monitoring Briefs are now in Beta for everyone. The Briefs workspace is available on every account; plans that don't include Briefs see a preview with an upgrade path. Brief access follows your active organization's plan.

Google sign-in reliability improved: OAuth sessions now preserve safe post-login destinations, handle expired PKCE verifier state with a friendly retry path, and pin Supabase auth dependencies so installs no longer drift underneath the login flow.

2026-06-02

Monitoring Briefs can now be published to public search. From a Brief's page, opt in with “Publish to search” to get a stable, indexable public page at /briefs/published/… with its own preview card and structured data. Publishing is off by default and set per Brief; the existing unguessable share links stay private and are never indexed.

New discovery pages help teams find GDELT Cloud for what they actually search for: a comparison hub (/compare — including vs. raw GDELT, news APIs, Dataminr, and AlphaSense) and an Industries section (/industries) for maritime, supply-chain, energy-infrastructure, country-risk, OSINT, and finance/macro teams.

Added a homepage FAQ and refreshed site metadata and structured data (organization, software, pricing/offers, product, article, and FAQ schema) so GDELT Cloud shows up more clearly and richly in search results.

2026-06-01

Detailed Briefs are now a plan-controlled tier. Briefs are metered in Brief Units (BU): a skim/standard Brief costs 1 BU, and a Detailed Brief — which investigates first, second, and third-order effects with far more evidence — costs a Brief-Unit amount set per plan. Admins choose which plans can run Detailed Briefs and their BU cost; the pricing page now explains BU alongside QU.

New Monitoring Briefs showcase at /briefs-showcase featuring real, live Briefs. Admins curate the lineup in the Briefs admin page by pasting public share links and uploading a screenshot of each Brief's web view to use as the card's header image.

Any Brief can now be re-run on demand with “Run Now” — it generates a fresh Brief from the same inputs (one-off or scheduled) and keeps the prior runs. Re-runs are grouped into a run history: the Briefs library shows one entry per Brief with a runs list, and each Brief page has a run switcher to move between snapshots. The Briefs library and detail pages also load markedly faster.

Admin Growth, Activity, and user-detail pages now use lighter aggregate queries and user-scoped reads, and public/auth pages skip unnecessary session work before rendering. Admins should see faster dashboard loads and fewer transient enrichment errors during upstream Wikipedia slowdowns.

2026-05-31

Monitoring Briefs are now available: a short, source-backed intelligence memo that answers what changed, why it matters, what evidence supports it, and what to watch next — generated from structured events, clustered stories, mapped evidence, market context, and linked entities, with a full source appendix.

Briefs are metered separately from Query Units and never draw down your QU pool. Each plan includes a monthly Brief allotment; additional Briefs are purchased as never-expiring credits. A new Briefs scenario in the pricing calculator estimates the right plan for your monthly volume.

Briefs can be created, listed, and fetched over the REST API (`/api/v2/briefs`) and the MCP server for plans with Briefs enabled. Creation returns immediately and runs in the background (briefs take ~5–10 minutes); each brief can optionally expose a public share link.

2026-05-30

V2 Event taxonomy subcategory filters now use stable CAMEO+ event codes, so filters such as `category=TECHNOLOGY&subcategory=TE01` work consistently across Events, Stories, Entities, and the API Playground.

CAMEO+ response subcategory labels are now normalized to canonical codebook labels such as `TE01 · AI Capability Event`, while older label-style query values remain accepted for compatibility.

2026-05-29

Plan Query Unit (QU) limits are now enforced atomically. Previously, requests issued concurrently right at a plan's monthly QU limit could each pass the limit check before any was recorded, letting usage overshoot the cap by a small amount. Enforcement now reserves QU in a single atomic step, so the limit holds exactly and surplus concurrent requests receive a 429 QUOTA_EXCEEDED response.

Closed a billing-bypass vector: the internal 'no-charge' marker header used by the MCP proxy is now honoured only when accompanied by the trusted internal usage secret, so external API callers can no longer set it to zero out their own usage.

Faster public entity, event, and story pages via a date-aware cache: content for the current UTC day stays on the hourly refresh, while event and story pages from previous days — which no longer change — are cached longer and served without re-querying the warehouse on every request. Repeat and long-tail crawler traffic no longer triggers an hourly re-query. No API response shapes change.

2026-05-27

V2 list and summary responses now include an applied_filters block — a top-level peer of pagination — echoing the resolved filter values the SQL actually used (post region/continent expansion, post CSV split) plus an `ignored` map of any URL params the endpoint didn't recognize. Lets callers self-detect when a filter quietly fell through.

Every multi-value filter on the GDELT Cloud and Energy MCP tools (search_events, summarize_events, search_stories, summarize_stories, search_entities, energy_search_assets, energy_summarize_assets, energy_assets_by_owner) now accepts a list of values (country=['United States','Vietnam'], fuel=['solar','wind'], etc.). The V2 URL contract is unchanged — country=USA,VNM still works.

search_entities `search` description clarified to lead with the param name and explain the exact-match ranking, removing the wording that led some agents to invent a non-existent `name` kwarg.

MCP tool-result widgets now use the Prefab renderer as the canonical MCP Apps path, with ChatGPT compatibility metadata retained on the same renderer URI.

GDELT Cloud, energy, prediction market, macro finance, and web research MCP calls now carry richer structured widget payloads plus raw-data fallbacks for the Agent UI Artifacts drawer.

2026-05-26

V2 Events, Stories, Entities, and API Playground now default to the exact past 24 hours, guard 30-day API windows in the UI, and show friendlier validation errors.

Category is now the primary Event taxonomy filter across UI, API docs, and MCP; event_family remains backwards compatible but is deprecated. Subcategory filters now require their parent category and return scoped recovery options.

Entities gained linked Event taxonomy filters, and recent sorting for Stories and Entities now follows actual latest observed/update time.

Conflict Event civilian_targeting is now returned on V2 Event cards and exposed as a filter across Events, Stories, Entities, API Playground, docs, and MCP.

2026-05-23

Google sign-in added. New and returning users can now use the 'Continue with Google' button on the login and signup pages alongside the existing email/password flow.

/data-status V2 API latency chart fixed: query was silently capped by PostgREST max-rows and only rendered the oldest few days in the 30-day window. Now aggregates p50/p95 in Postgres and filters to /api/v2/* API-key traffic specifically.

Subprocessor descriptions clarified: Global Energy Monitor data is loaded offline from GEM, not via BigQuery; LangSmith is primarily our agent runtime host (tracing secondary); OpenAI is used for both LLM completions and embedding generation; Tavily is used for web search and content extraction.

Methodology page latency estimate corrected to ~15 minutes (the duration of the ingest pipeline), down from the earlier ~1 hour figure.

2026-05-22

Trust Center launched. New pages cover security posture, subprocessors, methodology, and this changelog. Privacy policy augmented with explicit AI-training and agent-trace language.

Wikipedia entity fetch timeouts handled more gracefully during entity enrichment runs.

Demo snapshots stabilised: snapshot files now carry a required refreshed_at timestamp; demos directory ignore rule fixed in .gitignore.

Demo pages launched at /demo for product walkthroughs.

2026-05-08

Privacy Policy, Terms of Service, and Acceptable Use policy rewritten in plain language and consolidated under a shared Trust Page layout.

Spot something off?

If a release note is incorrect or a behaviour change is missing, let us know.