# When did this law last change? The version history of an article > The version timeline of a French or EU article — every version with its dates and status — in one compact call, so an agent watching for regulatory drift never re-reads the full text to spot it. Published 2026-06-28 · Updated 2026-08-15 · HTML version: https://invoket.com/blog/when-did-this-law-last-change --- An agent watching a statute for change has a cheaper question than "what does it say now?" — it is "*has it changed since I last looked?*" Re-reading the full consolidated text on every poll is wasteful and hard to diff. What it actually needs is the **version timeline**: every version an article has had, each with its validity dates and status. One compact paid call — [`GET /legal/history`](/docs/api/legal-history) — returns exactly that, for French **and** EU law, and is the primitive a compliance monitor polls on. ## The problem: monitoring is a timeline question, not a text question Compliance monitoring, amendment tracking, regulatory-drift detection — they all reduce to noticing that a *new version appeared*. If an agent fetches full article text every time, it pays for a large payload and then has to detect change itself. The signal it really wants is small: a list of versions with start dates. A new entry, or a new open-ended current version, *is* the alert. `GET /legal/history` is deliberately scoped to that. The payload answers **when** an article changed and **which** versions exist — not what the text says, and not what changed between two versions. Keeping it compact is what makes it cheap to poll on a schedule. ## The call: the full version timeline, one identity A single endpoint covers both corpora. Supply a **French** identity (`code` + `article`) or an **EU** identity (`celex` or `eli`, optionally `article`). There is **no `date` parameter** — history always returns the whole timeline: ```http GET /legal/history?code=code-civil&article=1240 GET /legal/history?celex=32016R0679&article=17 ``` It returns: | Field | What the agent learns | |------------|----------------------------------------------------------------| | `versions` | Every known version, sorted by `date_debut` ascending | | `count` | Number of versions (equals `versions.length`) | | `label` | Human label for the article | Each entry in `versions[]` is minimal and dated: ```json { "version_id": "v-courant", "etat": "vigueur", "date_debut": "2016-10-01" } ``` `date_debut` is inclusive; `date_fin` is exclusive and **omitted for the current version** — so the entry with no `date_fin` is the one in force now. "When did it last change?" is the `date_debut` of that last entry. There is **no `text`** and **no `diff`** in the payload, by design. ## How an agent monitors with it The timeline turns drift detection into a tiny stored-state comparison: 1. On first run, call `/legal/history` and store `count` (or the last `version_id` / `date_debut`). 2. On each poll, call again and compare. A higher `count`, a new trailing `version_id`, or a freshly closed `date_fin` on what used to be the current version means the article changed. 3. On a detected change, escalate: fetch the new text with [`GET /legal/article`](/docs/api/legal-article) (or [`GET /legal/eu-act`](/docs/api/legal-eu-act)), or compute [`GET /legal/diff`](/docs/api/legal-diff) between the old and new dates. The cheap, frequent poll is `history`; the expensive text/diff calls run only when the timeline says something moved. ## Repealed is part of the timeline, not an error If the most recent version is `abroge`, the article still returns its timeline — the repeal is data the agent is buying, with its own `date_debut`. Per the x402 golden rule, the agent pays for the *answer to its question*; a known article's timeline is a successful answer → **200**, even when it ends in a repeal. The `4xx` range is reserved for what the service cannot answer: `INVALID_REF` (no usable identity), `UNKNOWN_ARTICLE`, `UNKNOWN_ACT`. ## Coverage honesty: the snapshot bounds what you can see The timeline reflects exactly what the served store dump contains. The service does **not** fetch Legifrance or EUR-Lex at request time, search by keyword, or infer missing versions. An amendment that postdates the snapshot appears only after the store is refreshed — and `provenance.freshness.as_of` tells you which dump backed the answer. For a monitor that matters: you are detecting drift *as of the latest snapshot*, so read `as_of` to know how current your view is. `etat` is carried verbatim as legal metadata. ## Where it sits in the x402 loop History is the recurring lightweight probe in a monitoring loop: 1. **Discover** the endpoint and call it; receive the `402`. 2. **Pay** — sign the chosen rail and replay the request. 3. **Compare** — read `count` and the trailing version against stored state. 4. **Branch** — no change → done for this poll; change detected → escalate to text or diff. Each paid call follows the same x402 pattern as every Invoket endpoint. The [Quickstart](/docs/quickstart) walks the whole discover → `402` → pay → replay cycle with runnable snippets. Price and accepted rails are not pinned in this article — they are served live by the [catalog](https://api.invoket.com/catalog); see the [endpoint reference](/docs/api/legal-history) for the current figure. ## Monitoring many articles at once A real compliance watch covers a portfolio of provisions, not one article. [`POST /legal/history/batch`](/docs/api/legal-history-batch) returns the timelines for many references under **one** x402 settlement instead of one per article — which is what makes a scheduled sweep over a whole regulatory surface affordable. `GET /legal/history` is the timeline primitive in a family of three: `history` (when did it change?), [`article`](/docs/api/legal-article) / [`eu-act`](/docs/api/legal-eu-act) (what does a version say?), and [`diff`](/docs/api/legal-diff) (what changed?). Used for what it is — a compact, pollable change signal — it lets an agent watch the law cheaply and only pay for full text when something actually moved. For the full field reference and error codes, see the [`GET /legal/history` documentation](/docs/api/legal-history); for how agents discover and call Invoket endpoints, see [For agents](/docs/for-agents). French law data is derived from the LEGI dataset published by DILA under the Licence Ouverte / Etalab open licence; EU law data is derived from EUR-Lex / Cellar made available by the Publications Office of the European Union.