GET /legal/article
Returns the consolidated version of a French legal article in force at a given date: article text, LEGI status, validity dates and coverage metadata. The lookup is served from a local store built from the official LEGI dataset, so a covered reference resolves in milliseconds without an account or API key. The corpus currently covers a selected set of French codes and statutes and is expanding; an uncovered reference returns 404, never a paid empty answer.
Use it when an agent needs a grounded answer to questions such as “what was the
text of Article 1240 of the Civil Code on this date?” or “is this article
repealed?” See the live /catalog for the authoritative
endpoint listing and price.
x402 golden rule: the agent pays for the answer to its question. A known
article with a version covering the requested date is a successful answer ->
200, including when etat: "abroge" and including after the repeal took
effect: “yes, repealed since that date” is the answer, so the endpoint returns
the last known version with in_force_at_date: false. Repealed is data, not an
error. Requests the service cannot answer - malformed references, invalid dates,
unknown articles, or a date no version covers and no repeal explains - leave the
200 range.
Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
code | string | yes | French text identifier, for example code-civil; case-insensitive |
article | string | yes | Article number within that text, for example 1240 |
date | string | no | UTC date to resolve, strict format YYYY-MM-DD; defaults to today’s date |
GET /legal/article?code=code-civil&article=1240&date=2026-01-01
Omit date when the agent wants the version in force today:
GET /legal/article?code=code-civil&article=1240
The code slug is case-insensitive: CODE-CIVIL and code-civil resolve to
the same text, and the response echoes the canonical lowercase form.
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 LEGI store, commonlylegifrance-legi.freshness.kind:snapshot;as_ofis the LEGI dump date that backed the answer. Freshness is dated per dataset, so a French answer reflects the LEGI snapshot date, independent of the EU corpus.- French law data is derived from LEGI, published by DILA under the Licence Ouverte / Etalab open licence.
Fields of data
| Field | Type | Description |
|---|---|---|
code | string | Echo of the requested French text identifier |
article | string | Echo of the requested article number |
date | string | Effective resolution date in YYYY-MM-DD form |
label | string | Human label for the resolved article |
etat | string | LEGI status carried as stored, never reinterpreted by the service |
in_force_at_date | bool | Whether the served text is the applicable law on date |
text | string | Consolidated text of the selected version |
version | object | Validity period and source version identifier |
coverage_window | object | Period over which this text is actually queryable: from (inclusive), to (exclusive, null when open) |
coverage | object | Completeness marker for the served legal version |
Possible etat values follow the LEGI store and may include vigueur,
abroge, abroge_diff, vigueur_diff, modifie and perime.
in_force_at_date is the flag to branch on. It saves you from cross-checking
etat against version.date_fin and date - a cross-check that is genuinely
error-prone, because the French and EU corpora encode a repeal in opposite ways
(French law closes the last version’s interval; EU law opens a repeal version
that starts at the repeal). The same question gets the same answer and the
same flag on both corpora. etat itself is never rewritten.
version
| Field | Type | Description |
|---|---|---|
version_id | string | Stable identifier of the selected source version |
date_debut | string | Start date of this version, inclusive, in YYYY-MM-DD |
date_fin | string | null | End date of this version, exclusive; omitted when current |
coverage
| Field | Type | Description |
|---|---|---|
complete | bool | true when the store served an unambiguous version |
reason | string | Present when complete: false, naming the source dump anomaly |
Example - article in force
{
"data": {
"code": "code-civil",
"article": "1240",
"date": "2026-01-01",
"label": "Code civil, art. 1240",
"etat": "vigueur",
"in_force_at_date": true,
"text": "Tout fait quelconque de l'homme, qui cause a autrui un dommage, oblige celui par la faute duquel il est arrive a le reparer.",
"version": {
"version_id": "v-courant",
"date_debut": "2016-10-01"
},
"coverage": { "complete": true }
},
"provenance": {
"source": "legifrance-legi",
"fetched_at": "2026-06-20T12:00:00Z",
"freshness": { "kind": "snapshot", "as_of": "2026-06-01T00:00:00Z" }
}
}
Example - repealed article is still a 200
An abrogated article is a successful answer, at any date after the repeal. The
status lives in data.etat and the applicability in data.in_force_at_date;
neither is converted into an HTTP error. version carries the article’s real
validity interval, including the end date - the store’s data is never rewritten
to make the answer look tidier.
{
"data": {
"code": "code-test",
"article": "disparu",
"date": "2026-01-01",
"label": "Code test, art. disparu",
"etat": "abroge",
"in_force_at_date": false,
"text": "dernier texte connu avant abrogation",
"version": {
"version_id": "v-old",
"date_debut": "1990-01-01",
"date_fin": "2000-01-01"
},
"coverage": { "complete": true }
},
"provenance": {
"source": "legifrance-legi",
"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 corpus. It does not fetch
Legifrance at request time, search by keyword, infer missing articles or
reinterpret LEGI statuses. etat is carried from the source store as legal
metadata; callers should treat it as the authoritative status for the selected
version.
coverage.complete: false is reserved for source-store anomalies, such as
overlapping versions where the service selected the most recent start date.
Unknown articles, and dates that no version covers and no repeal explains, are
not partial paid answers: they return 4xx errors.
The service distinguishes a text that is out of scope from an article
number that does not exist inside a covered text. If the whole code is not
part of the store’s selection, the error is UNKNOWN_TEXT (the text is not
covered); if the text is covered but the article number is unknown, it is
UNKNOWN_ARTICLE. The covered set of French codes is listed in the live
/catalog entry.
Historical depth varies per text
Every answer carries coverage_window: the period this text can actually be
read over (from inclusive, to exclusive, null when still open). Depth is
not uniform across the corpus — the Code civil goes back to 1804, other texts
start at the first consolidation the source dataset publishes.
A date outside that window returns OUT_OF_COVERAGE_WINDOW, which says we do
not cover the period — not that the law did not exist:
{
"error": "text `code-civil` is covered from 1804-03-21 onwards; the store has no data for 1700-01-01. Historical depth varies per text: this is a coverage limit, not a statement about the law",
"code": "OUT_OF_COVERAGE_WINDOW"
}
Inside the window, a date no version covers stays NO_VERSION_AT_DATE: the text
is covered, that article simply had no version then. Both are 4xx and neither is
billed. The same window is returned by
/legal/history, so a monitoring agent knows how far
back to go before spending a call.
Errors
Only requests the service cannot answer leave the 200 range.
| Status | code | Case |
|---|---|---|
| 400 | INVALID_REF | code or article is missing, empty or malformed |
| 400 | INVALID_DATE | date is present but not strictly formatted as YYYY-MM-DD (zero-padded: 2026-07-05, not 2026-7-5) |
| 404 | UNKNOWN_TEXT | The code is well formed but the whole text is outside the store’s scope |
| 404 | UNKNOWN_ARTICLE | The text is covered, but no matching article number exists in it |
| 404 | OUT_OF_COVERAGE_WINDOW | The date falls outside the text’s coverage_window - that period is not covered for this text |
| 404 | NO_VERSION_AT_DATE | Within the window, the article exists but no version covers the requested date and no repeal explains the gap |
| 500 | INTERNAL | Internal error (detail logged, not exposed) |
{ "error": "query parameter `code` is required (text identifier, e.g. `code-civil`)", "code": "INVALID_REF" }
{ "error": "invalid `date` `01/01/2026`; expected ISO calendar format YYYY-MM-DD", "code": "INVALID_DATE" }
{ "error": "text `code-de-la-route` is not covered by the store", "code": "UNKNOWN_TEXT" }
{ "error": "no article `9999` found for text `code-civil` in the store", "code": "UNKNOWN_ARTICLE" }
{ "error": "Code civil, art. 1240 has no version in force on 1700-01-01", "code": "NO_VERSION_AT_DATE" }
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.
See also
GET /legal/eu-act- consolidated European Union act text at a date.- For agents - discovery surfaces, the live
/catalogand how settlement works.