Spanish law, article by article, as it stands today.
An Apify Actor that turns Spain's consolidated legislation into RAG-ready chunks and keeps your vector store current: after every reform, only what changed, deletes included.
pathEstatuto de los TrabajadoresTítulo ICapítulo ISección 2.ªArtículo 4
Artículo 4. Derechos laborales.
1. Los trabajadores tienen como derechos básicos, con el contenido y alcance que para cada uno de los mismos disponga su específica normativa, los de:
- a) Trabajo y libre elección de profesión u oficio.
- b) Libre sindicación.
- c) Negociación colectiva.
- d) Adopción de medidas de conflicto colectivo.
- e) Huelga.
- f) Reunión.
- g) Información, consulta y participación en la empresa.
- Published on Apify Store
- Open source, MIT licence
- Tested on the real XML of four laws
- Checked every Monday against the BOE API
Seventeen wordings, one article in force.
Article 37 of the Workers' Statute comes out of the open data with all seventeen wordings it has had since 2015, plus the notes of every reform. This is how it becomes chunks:
«Las leyes sólo se derogan por otras posteriores.»
Laws are only repealed by later laws.
1.º The XML. Each wording carries the norm that enacted it, its publication date and the date it took effect.
2.º Notes out. «Se modifica el apartado 6 por…», «Téngase en cuenta…». Editorial notes often quote the previous wording, so they are removed before splitting and repealed text never reaches your index.
3.º The later law wins. Two reforms of paragraph 6 took effect on the same day, 23 October 2026: Ley 4/2026 and Real Decreto-ley 31/2026. The one published later prevails, as the Civil Code rules and as boe.es resolves it.
4.º Nine chunks. The wording in force is split by paragraphs up to 2,000 characters. Every chunk carries its path in the law, a stable
chunk_idand apoint_idready for Qdrant.
After every reform, only what changed.
On 1 April 2025, Real Decreto-ley 11/2024 took effect and rewrote partial retirement in article 12 of the Statute. The article got shorter: it went from eight chunks to seven.
6. Para que el trabajador pueda acceder a la jubilación parcial antes de alcanzar la edad ordinaria de jubilación, en los términos establecidos en el texto refundido de la Ley General de la Seguridad Social y demás disposiciones concordantes, deberá acordar con su empresa una reducción de jornada y de salario de entre un mínimo del veinticinco por ciento y un máximo del cincuenta por ciento y la empresa deberá concertar simultáneamente un contrato de relevo, de acuerdo con lo establecido en el apartado siguiente, con objeto de sustituir la jornada de trabajo dejada vacante por el trabajador que se jubila parcialmente indefinido y a tiempo completo.
What a sync would have emitted that day, computed with the Actor's code on the real XML:
- upsertBOE-A-2015-11430:a12:0
- upsertBOE-A-2015-11430:a12:1
- upsertBOE-A-2015-11430:a12:2
- upsertBOE-A-2015-11430:a12:3
- upsertBOE-A-2015-11430:a12:4
- upsertBOE-A-2015-11430:a12:5
- upsertBOE-A-2015-11430:a12:6
- deleteBOE-A-2015-11430:a12:7superseded
261 unchanged chunks: neither emitted nor charged.
On Apify, with the whole Statute and the same syncId:
- upserts on the first run
- 273
- operations on the second, with no reform in between
- 0
Try it with one request.
With your Apify token, one call runs the Actor and returns its records. No code: open the Actor in the console, keep the prefilled Workers' Statute and press Start.
curl -X POST "https://api.apify.com/v2/acts/VazquezDev~spanish-laws-rag/run-sync-get-dataset-items" \
-H "Authorization: Bearer $APIFY_TOKEN" \
-H "Content-Type: application/json" \
-d '{"laws": ["estatuto-trabajadores"]}'{
"op": "upsert",
"chunk_id": "BOE-A-2015-11430:a4:0",
"point_id": "d29a273d-7c0c-57d6-8e33-5a06157d5991",
"context_header": "[Estatuto de los Trabajadores > … > Artículo 4]",
"text": "**Artículo 4. Derechos laborales.**\n\n1. Los trabajadores…",
"in_force_from": "2023-06-30",
"approx_tokens": 496,
"source_url": "https://www.boe.es/buscar/act.php?id=BOE-A-2015-11430#a4"
}Built to be maintained.
Functional core. Only the download and the Apify shell do I/O; the parser, the splitter and the diff are pure functions.
Real data, no network. Tests run on real XML trimmed from four laws: the Workers' Statute, the Civil Code, the Penal Code and the income tax law.
Checked on every commit. Strict mypy, Ruff with every rule and pytest, in a pre-commit hook and in CI.
A canary every Monday. It checks against the BOE API that the twenty aliases still point to laws in force and that a real law still splits correctly.
Decisions in writing. Five ADRs explain what is expensive to change: the sync manifest, the record contract and why the hash leaves out the update date.
Price. Free during launch: you only pay for your Apify platform usage. Later, one charge per article synced; unchanged articles and deletes are never charged.
Questions
Does the text have legal validity?
No. The consolidated text is informative only; the official text is the one published in the BOE. Every chunk carries that notice and the source attribution.
Are embeddings included?
No: you compute them with the model of your choice. The Actor delivers text, path and metadata, and the README's loader takes them to Qdrant or pgvector.
Which laws can I ask for?
Any consolidated norm with a BOE-A-… id, by id or by its boe.es URL, and twenty common laws by alias: constitucion, codigo-civil, irpf…
What about case law or the daily gazette?
Out of scope: consolidated legislation only.
What if I wipe my vector store?
Run once with fullReload: it emits every chunk in force again and deletes the stale ones.