> ## Documentation Index
> Fetch the complete documentation index at: https://docs.gdeltcloud.com/llms.txt
> Use this file to discover all available pages before exploring further.

# As-of claims and coding latency

> What you can honestly claim about when we knew something, why events are coded after the day they happened, and how to build a point-in-time view for backtesting.

Every event in GDELT Cloud carries two different dates, and confusing them is the single easiest way
to build a backtest that looks brilliant and is wrong.

| Column       | What it means                                                                         | What it is NOT                                         |
| ------------ | ------------------------------------------------------------------------------------- | ------------------------------------------------------ |
| `event_date` | **When the event happened**, as adjudicated by the coder from the underlying articles | Not when we learned about it                           |
| `coded_at`   | **When the event first existed in our database** — the instant the coder emitted it   | Not when it was published, and not when you queried it |

`event_date` is a claim about the world. `coded_at` is a claim about us. A backtest that filters on
`event_date` alone is asking "what happened by date D", which is a question no one could have
answered on date D.

## The as-of claim we do make

**Filtering on when an event was coded gives you the set of events that existed in GDELT Cloud at
that time.** That is a real point-in-time view, and it is auditable in both directions:

* The coding timestamp is populated on **100% of events**, stamped once when the coder emits the
  event and never revised.
* Every event carries `source_urls` — the actual articles it was derived from. Nothing is coded from
  a source we do not cite, so you can go read the evidence that was available at the time.

<Warning>
  **What we do not claim.** This is not a full bitemporal history. We record when an event first
  appeared; we do not publish a revision log of every subsequent correction to that event's fields. If
  an event's actors or geography were later refined, the record carries the refined version with the
  original coding timestamp. Treat it as "first known at", not "unchanged since".

  And there is no vintaged replay of past *values* — not in the bulk files, not on the API, and not at
  any plan tier. `as_of` is refused on `/api/v2/events` for exactly this reason. Bounding by when we
  coded something is the whole of what we offer here.
</Warning>

## How to ask for it

The lever has a different name on each surface, and there is no `as_of` on events.

| Surface                                      | Filter                            | Notes                                                                                                          |
| -------------------------------------------- | --------------------------------- | -------------------------------------------------------------------------------------------------------------- |
| `GET /api/v2/events` and `/events/summary`   | `observed_start` / `observed_end` | Bounds by when an event was **coded** rather than when it happened. This is the supported point-in-time lever. |
| `GET /api/v2/stories` and `/stories/summary` | `observed_start` / `observed_end` | Same semantics.                                                                                                |
| Bulk downloads                               | the `coded_at` column             | The per-row timestamp, for filtering offline. See the [bulk schema](/data/bulk-events-schema).                 |

```bash theme={null}
# Every event we had coded as of 14 July — no look-ahead.
curl "https://gdeltcloud.com/api/v2/events?date_start=2026-07-01&date_end=2026-07-14&observed_end=2026-07-14" \
  -H "Authorization: Bearer $GDELT_API_KEY"
```

<Note>
  `as_of` on `/api/v2/events` returns **400**, deliberately. Event metrics are computed live and are
  not vintaged, so `as_of` could not return a reproducible point-in-time result there — and answering
  `200` with a plausible-looking result would be worse than refusing. Use `observed_start` /
  `observed_end`, which bound something we actually record. `as_of` *is* supported where the data is
  genuinely vintaged: `/macro/observations`, `/screening/match` and the Atlas `/intelligence/*`
  surfaces.
</Note>

## Why events are coded after the day they happened

Measured over the seven days ending 2026-07-21, across 11,993 events: the median gap between an
article becoming available to us and its coded event being queryable is **38 minutes**, with a 90th
percentile of **61 minutes**. 89.1% of events are available within the hour.

Lag measured the other way — against `event_date` rather than against the article — looks much
larger, and the difference is mostly arithmetic rather than processing. Three real causes, in order.

### 1. Hourly cadence

Ingest runs on the top of every hour, so there is an irreducible sub-hour quantisation: an article
that appears just after a run waits for the next one. **The floor is set by the cycle, not by
processing time**, which is why the median lands near half a cycle plus the pipeline's own duration.
A separate catch-up pass runs near the end of each hour and sweeps clusters the main run did not
reach, and range backfills re-code historical windows when coverage gaps are found.

### 2. Publication lag in the sources themselves

`event_date` is when the event occurred, not when it was reported. Local and non-English outlets
frequently publish on D+1 about a D event, and for some regions that is the *only* coverage. The
conflict family shows this most.

### 3. The midnight boundary — an artefact, not a delay

`event_date` is a **date**, not a timestamp. An event that happened at 22:00 UTC and was coded at
23:00 UTC — one hour later — already registers as "coded the same day" with almost no margin, and
one coded at 00:30 registers as a *next-day* event on a 90-minute pipeline. Most of the apparent
day-scale lag against `event_date` is this, not work in progress.

<Note>
  **We do not wait for corroboration before coding.** A story does not have to accumulate articles to
  become codeable — single-article clusters are a first-class coding path and are the majority of what
  we code. Measured over the same window, computing latency from the *earliest contributing article,
  including everything that arrived later*, moves the median by **32 seconds** (2,332 s vs 2,300 s). If
  coverage had to accrete before an event could be coded, that gap would be hours.
</Note>

### Later-coded events are better evidenced — but that is selection, not waiting

Comparing events coded within a day of their `event_date` against those coded in the following day:

|                            | Coded ≤24h | Coded 24–48h |
| -------------------------- | ---------- | ------------ |
| Share of events            | 88.9%      | 11.1%        |
| Mean articles in the Story | 5.14       | **6.21**     |
| Mean cited source URLs     | 1.95       | **2.16**     |
| Mean coder confidence      | 0.760      | **0.912**    |

The reason is which net caught them, not how long they sat. The hourly catch-up pass targets
clusters the main run left uncoded and requires at least two articles, so the events it recovers are
by construction the better-corroborated ones. Either way the practical conclusion holds: **if you
drop the tail to get a tighter latency number, you are dropping your best-evidenced events.**

## How to use this in a backtest

<Steps>
  <Step title="Filter on when we coded it, not on when it happened">
    For a signal you claim to have had at time T, bound `observed_end` at T. Filtering on
    `event_date` alone silently imports hindsight.
  </Step>

  <Step title="Set your decision lag from the distribution, not the median">
    If your strategy acts on same-day information, model a decision point at T+24h and accept that
    you will see roughly 89% of the day's events. If it can wait, T+48h gets you effectively all of
    them, with better evidence.
  </Step>

  <Step title="Do not treat a thin day as a quiet day">
    A day with an unusually low event count near the present is more likely still filling in than
    genuinely calm. Re-pull recent windows before drawing a conclusion about them, and prefer
    windows that are at least 48 hours old.
  </Step>

  <Step title="Check the file's own as-of stamp">
    Every bulk file carries an `as_of` value that reports what the weakest partition in it knew —
    the minimum across dates, not the maximum, so one freshly settled day cannot vouch for a stale
    month.
  </Step>
</Steps>

## Where these numbers come from

The latency figures are measured against the production events table over the stated windows, not
modelled, and the same measurement is published on the
[methodology page](https://gdeltcloud.com/methodology). They will drift as the pipeline changes; the
shape of the distribution — an hourly mode plus a catch-up mode — is the stable part, and the exact
percentiles are not a contractual guarantee.
