POST /legal/history/batch
Returns version timelines for a list of legal references in a single call: up
to 100 references, French or European Union, settled with one x402 payment
for the whole batch. Each entry runs through the same local timeline engine as
GET /legal/history, so the response is one ordered
array of timelines - when each article changed and which versions exist.
This is the portfolio monitoring primitive: an agent re-checks a whole set of articles periodically to detect when any of them gained a new version - regulatory drift across a contract’s citations, amendment tracking over a compliance register, due-diligence sweeps - without issuing one paid call per reference. Like the single endpoint, the payload is deliberately compact: each verdict carries the timeline only, not the consolidated text and not a diff.
The lookup is served from local snapshots of the LEGI and EUR-Lex corpora: no account, no API key and no network fetch to Legifrance or EUR-Lex at request time.
- For the full consolidated text of a version, call
GET /legal/articleorGET /legal/eu-actwithdate=. - For what changed between two dates across a list, call
POST /legal/diff/batch.
x402 golden rule: the agent pays for the answer to its question. A
well-formed batch is a successful answer -> 200, even when some references are
repealed or not covered at all. Those cases are reported per item, by the version
etat or by coverage.complete: false - never as a batch error. The 4xx range is
reserved for requests the service cannot answer as a batch: missing or malformed
body, empty list, batch over the cap or a malformed item.
Request
POST with a JSON body. Set Content-Type: application/json.
POST /legal/history/batch
Content-Type: application/json
{
"refs": [
{ "code": "code-civil", "article": "1240" },
{ "celex": "32016R0679", "article": "17" }
]
}
| Field | Type | Required | Description |
|---|---|---|---|
refs | object[] | yes | Legal references to resolve, 1 to 100 entries |
There is no date field on a reference: history always returns the whole
timeline. Each item is either a French reference or an EU reference:
| Field | Type | Required | Description |
|---|---|---|---|
code | string | FR | French text identifier, for example code-civil |
article | string | FR only | Article number for code; optional for EU act-level lookups |
celex | string | EU id | CELEX identifier, for example 32016R0679 |
eli | string | EU id | ELI identifier, used when celex is absent |
For French references, provide code and article. For EU references, provide at
least one of celex or eli; article is optional when targeting the act head.
If an item contains a non-empty code, it is treated as French.
Volume pricing
This endpoint uses per-unit pricing: one x402 settlement for the request, with
the payable amount derived from the number of references in refs - a base plus a
per-reference unit, capped at 100 references. The value of the call grows with the
size of the portfolio you monitor. The gateway counts the array length; it does
not inspect legal content to compute price.
The live /catalog is the price authority and exposes the
current base, unit, maximum units, accepted assets and networks. This page
describes the pricing model, not the amounts: do not cache figures from here.
200 response - UnifiedResponse
{
"data": {
"count": 2,
"results": [ { ... }, { ... } ]
},
"provenance": {
"source": "legifrance-legi + eur-lex-cellar",
"fetched_at": "2026-06-20T12:00:00Z",
"freshness": { "kind": "snapshot", "as_of": "2026-06-01T00:00:00Z" }
}
}
count: number of submitted references, equal toresults.length.results: one verdict per input item, in the same order as the input. Covered items have the same field shape as thedataofGET /legal/history: echoed identifiers,label,versions[]andcount.- Unknown references remain item-level verdicts in the 200 response. They echo the
resolvable identifiers, set
coverage.completetofalsewith areason, and omit fields that cannot be served such aslabel,versionsandcount. - A single
provenanceblock covers the whole batch. When the batch touches both legal corpora,provenance.sourcenames both;freshness.kindremainssnapshotandas_ofis the store dump date that backed the answer.
See GET /legal/history for the full versions[] field
table, the etat status semantics and the attribution rules.
Example - mixed French and EU batch
{
"data": {
"count": 2,
"results": [
{
"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,
"coverage": { "complete": true }
},
{
"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,
"coverage": { "complete": true }
}
]
},
"provenance": {
"source": "legifrance-legi + eur-lex-cellar",
"fetched_at": "2026-06-20T12:00:00Z",
"freshness": { "kind": "snapshot", "as_of": "2026-06-01T00:00:00Z" }
}
}
A version whose most recent etat is abroge is still part of a successful item
verdict: the repeal lives in the timeline, never converted into an HTTP error.
Example - unknown reference is still a 200 item verdict
In a well-formed batch, an unknown article does not fail the whole request. It is returned as an incomplete verdict that echoes the identity and explains the gap.
{
"data": {
"count": 1,
"results": [
{
"code": "code-civil",
"article": "9999",
"coverage": {
"complete": false,
"reason": "`code-civil-art-9999` is not covered by the store"
}
}
]
},
"provenance": {
"source": "legifrance-legi",
"fetched_at": "2026-06-20T12:00:00Z",
"freshness": { "kind": "snapshot", "as_of": "2026-06-01T00:00:00Z" }
}
}
Errors
Only requests the service cannot answer leave the 200 range. The cap is checked before any resolution, so an oversized batch is rejected without being billed an answer.
| Status | code | Case |
|---|---|---|
| 400 | INVALID_BODY | Body missing or not JSON, not an object, or refs missing |
| 400 | EMPTY_BATCH | refs is an empty array ([]) |
| 400 | BATCH_TOO_LARGE | More than 100 references in a single call |
| 400 | INVALID_REF | An item has no usable identity (neither FR code+article nor EU celex/eli); error names refs[i] |
| 500 | INTERNAL | Internal error (detail logged, not exposed) |
{ "error": "request body must be a JSON object with a `refs` array", "code": "INVALID_BODY" }
{ "error": "batch `refs` must contain at least one reference", "code": "EMPTY_BATCH" }
{ "error": "batch `refs` exceeds the maximum of 100 references", "code": "BATCH_TOO_LARGE" }
{ "error": "refs[1]: reference must provide `code`+`article` (FR) or `celex`/`eli` (EU)", "code": "INVALID_REF" }
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/history- the single-reference version timeline, with the full field tables and status semantics.POST /legal/diff/batch- what changed between two dates across a list of references, in one settlement.- For agents - discovery surfaces, the live
/catalogand how settlement works.