GET /legal/history
Returns the version timeline of a legal article, French or
European Union: each stored version with its version_id, its
stored etat status and its validity dates. The timeline is served from a
local store built from the official LEGI and EUR-Lex datasets, so a covered
reference resolves in milliseconds without an account or API key. The corpus
currently covers a selected set of French codes and EU acts and is
expanding; an uncovered reference returns 404, never a paid empty timeline.
This is the monitoring primitive: an agent re-checks an article periodically to detect when a new version has appeared - regulatory drift, amendment tracking, compliance monitoring - without re-reading full text every time. The payload is deliberately compact: it answers when an article changed and which versions exist, not what the text says or what changed between two versions.
- For the full consolidated text of a version, call
GET /legal/article(orGET /legal/eu-act) withdate=. - For what changed between two dates, call
GET /legal/diff.
See the live /catalog for the authoritative endpoint
listing and price.
x402 golden rule: the agent pays for the answer to its question. A well
formed, known article returns its timeline -> 200, including when the most
recent version is abroge: the timeline is the data being sold. Requests the
service cannot answer - malformed references, unknown articles, unknown acts -
leave the 200 range.
Parameters
A single endpoint covers both corpora. Supply a French identity or a
European Union identity. There is no date parameter - history always
returns the whole timeline.
| Parameter | Type | Required | Description |
|---|---|---|---|
code | string | FR identity | French text identifier, for example code-civil |
article | string | FR identity; optional for EU | Article number within the text, for example 1240 |
celex | string | EU identity | CELEX identifier, for example 32016R0679 |
eli | string | EU identity | ELI identifier; used when celex is absent |
For a French article, provide code + article. For an EU article, provide
celex (or eli) and optionally article; omitting article targets the act
head.
GET /legal/history?code=code-civil&article=1240
GET /legal/history?celex=32016R0679&article=17
200 response - UnifiedResponse
{
"data": { ... },
"provenance": {
"source": "legifrance-legi",
"fetched_at": "2026-06-20T12:00:00Z",
"freshness": { "kind": "snapshot", "as_of": "2026-06-01T00:00:00Z" }
}
}
provenance.source: source identifier from the served store, commonlylegifrance-legifor French law oreur-lex-cellarfor EU law.freshness.kind:snapshot;as_ofis the store dump date that backed the answer.
Fields of data
| Field | Type | Description |
|---|---|---|
code | string | Echo of the requested French text identifier (FR requests) |
article | string | Echo of the requested article number, when supplied |
celex | string | Echo of the requested CELEX identifier (EU requests) |
eli | string | Echo of the requested ELI identifier (EU requests) |
label | string | Human label for the article |
versions | array | Every known version, sorted by date_debut ascending |
count | number | Number of versions, equal to versions.length |
The identity fields are echoed exactly as supplied: code/article for a
French request, celex/eli/article for an EU request. There is no
text and no diff in the payload - timeline only, by design.
versions[]
| Field | Type | Description |
|---|---|---|
version_id | string | Stable identifier of the source version |
etat | string | LEGI / EUR-Lex status carried as stored, never reinterpreted |
date_debut | string | Start date of this version, inclusive, in YYYY-MM-DD |
date_fin | string | null | End date, exclusive; omitted for the current version |
Versions are sorted by date_debut. etat is carried as stored and may
include vigueur, abroge, abroge_diff, vigueur_diff, modifie and
perime. A version with no date_fin is the open-ended current version.
Example - French article with two versions
{
"data": {
"code": "code-civil",
"article": "1240",
"label": "Code civil, art. 1240",
"versions": [
{
"version_id": "v-1804",
"etat": "abroge",
"date_debut": "1804-03-21",
"date_fin": "2016-10-01"
},
{
"version_id": "v-courant",
"etat": "vigueur",
"date_debut": "2016-10-01"
}
],
"count": 2
},
"provenance": {
"source": "legifrance-legi",
"fetched_at": "2026-06-20T12:00:00Z",
"freshness": { "kind": "snapshot", "as_of": "2026-06-01T00:00:00Z" }
}
}
Example - a repealed article still returns its timeline
When the most recent version is abroge, the article is still a successful
answer: the repeal is part of the timeline, not an error. The status lives in
each version’s etat; it is never converted into an HTTP error.
{
"data": {
"code": "code-test",
"article": "abroge",
"label": "Code test, art. abroge",
"versions": [
{
"version_id": "v-1",
"etat": "vigueur",
"date_debut": "2010-01-01",
"date_fin": "2020-01-01"
},
{
"version_id": "v-2",
"etat": "abroge",
"date_debut": "2020-01-01"
}
],
"count": 2
},
"provenance": {
"source": "legifrance-legi",
"fetched_at": "2026-06-20T12:00:00Z",
"freshness": { "kind": "snapshot", "as_of": "2026-06-01T00:00:00Z" }
}
}
Example - EU article
{
"data": {
"celex": "32016R0679",
"article": "17",
"label": "Regulation (EU) 2016/679 (GDPR), Article 17",
"versions": [
{
"version_id": "32016R0679-art-17-v1",
"etat": "vigueur",
"date_debut": "2018-05-25"
}
],
"count": 1
},
"provenance": {
"source": "eur-lex-cellar",
"fetched_at": "2026-06-20T12:00:00Z",
"freshness": { "kind": "snapshot", "as_of": "2026-06-01T00:00:00Z" }
}
}
Coverage honesty
The service answers from a local snapshot of the LEGI and EUR-Lex corpora. It
does not fetch Legifrance or EUR-Lex at request time, search by keyword, infer
missing versions or reinterpret statuses. The versions array reflects exactly
what the served store dump contains; a newer amendment that postdates the
snapshot appears only after the store is refreshed (provenance.freshness.as_of
tells you which dump backed the answer).
etat is carried from the source store as legal metadata; callers should treat
each value as the authoritative status for that version. Unknown articles and
unknown acts are not partial paid answers: they return 4xx errors.
Errors
Only requests the service cannot answer leave the 200 range.
| Status | code | Case |
|---|---|---|
| 400 | INVALID_REF | No usable identity: neither a French code+article nor an EU celex/eli |
| 404 | UNKNOWN_ARTICLE | The reference is well formed but no matching article exists in the store |
| 404 | UNKNOWN_ACT | A bare EU act identifier is well formed but no matching act exists |
| 500 | INTERNAL | Internal error (detail logged, not exposed) |
{ "error": "query parameter `code` (e.g. `code-civil`) with `article`, or `celex`/`eli`, is required", "code": "INVALID_REF" }
{ "error": "no article `9999` found for text `code-civil` in the store", "code": "UNKNOWN_ARTICLE" }
{ "error": "no act `39999R9999` found in the store", "code": "UNKNOWN_ACT" }
Attribution
French legislation and regulation data is derived from the LEGI dataset made available by the Direction de l’information legale et administrative (DILA) on Legifrance, under the Licence Ouverte / Etalab open licence. EU law data is derived from EUR-Lex / Cellar data made available by the Publications Office of the European Union; reuse must preserve attribution to the European Union and EUR-Lex.
See also
GET /legal/diff- what changed in an article’s text between two dates.GET /legal/article- full consolidated text of a French article at a date.GET /legal/eu-act- full consolidated text of an EU act or article at a date.- For agents - discovery surfaces, the live
/catalogand how settlement works.