What ships in the notas-archivo release, and how it grows
Published
July 18, 2026
Abstract
The explorations and tools on this site are not built from a static snapshot: they read from a GitHub Release, tagged notas-archivo, that packages the daily notes index of the Diario Oficial de la Federación as downloaded by dofjson’s –archivo mode. This page documents what that release contains, the monthly workflow that keeps it growing, and shows its current contents fetched live from GitHub at render time. It is the same archive that DOF Titles is built from — every figure on that page, from the 1,221,451 notes to the vocabulary by decade, is computed from these files.
Open in Colab
1 One release, one file per publication day
The repository keeps its collected Diario Oficial data out of git and in a single GitHub Release instead, notas-archivo: the raw archive is large, grows every month, and would not sit comfortably as committed files. Each asset in that release is a .tgz of one JSON file per publication day, produced by dofjson’s incremental --archivo mode against the DOF’s open-data service — the day’s index of what was published, never a note’s full content or its scanned images.
One asset, one span of time
A closed year packs into a single notas-YYYY.tgz; the year under way ships instead as one notas-YYYY-MM.tgz per finished month, until it closes.
One file, one day
Inside each archive, one JSON file per publication day — DDMMYYYY-notas.json — holding that day’s morning, evening and extraordinary editions.
One record, one note
Each note carries its codNota identifier, titulo, the section and page it was printed on, its issuing branch, and whether HTML, PDF or scanned images exist for it.
A single note record looks like this (trimmed to the fields the site’s pages actually use):
{"codNota":5793639,"titulo":"Acuerdo por el que se establecen acciones para la primera etapa...","fecha":"15-07-2026","codOrgaUno":"PE","nombreCodOrgaUno":"PODER EJECUTIVO","codSeccion":"UNICA","pagina":5,"existeHtml":"S","existeImagen":"S","existePdf":"S"}
Stub entries without a title — an internal bookkeeping artifact of the service — are filtered out before the file is written, using the same quita_notas_sin_titulo helper described in From the gazette to Markdown.
2 How the archive grows
The release is kept current by a scheduled workflow, notas-archivo.yml, that runs once a month, at 09:00 UTC on the 1st. In the ordinary case it downloads the month that just ended with dofjson --archivo --desde ... --hasta ... and uploads it as notas-YYYY-MM.tgz, overwriting any asset of the same name (--clobber), so a re-run of the same month is harmless. January 1st is a special case: the month that just ended is December, so instead of one more monthly asset the workflow downloads the entire year that just closed, publishes it as a single notas-YYYY.tgz, and deletes the twelve monthly assets that covered it — folding a year’s worth of monthly archives into the one closed-year asset the pattern above expects. The workflow can also be run by hand, for a specific month, through its workflow_dispatch input — useful for backfilling or forcing a re-run. None of this depends on the workflow always succeeding: dofjson --archivo resumes from where a previous attempt left off, so a failed or partial run is picked up cleanly the next time it fires.
3 The release today
Rather than describe the current contents in prose — which would be stale the day after this page is rendered — the cell below asks GitHub directly:
Code
import reimport pandas as pdimport requestsresp = requests.get("https://api.github.com/repos/INGEOTEC/LegalIA/releases/tags/notas-archivo", timeout=30,)release = resp.json()assets =sorted(release["assets"], key=lambda a: a["name"])rows = []for a in assets: m = re.match(r"notas-(\d{4})(?:-(\d{2}))?\.tgz", a["name"]) year, month = m.group(1), m.group(2) rows.append( {"asset": a["name"],"span": year if month isNoneelsef"{year}-{month}","kind": "year"if month isNoneelse"month","size_MB": round(a["size"] /1_000_000, 2), } )table = pd.DataFrame(rows)print(f"{len(table)} assets, spanning {table.span.min()}\u2013{table.span.max()}: "f"{(table.kind =='year').sum()} closed years, "f"{(table.kind =='month').sum()} months of the year under way "f"({table.size_MB.sum():.0f} MB total).")table.tail(8)
115 assets, spanning 1917–2026-06: 109 closed years, 6 months of the year under way (59 MB total).
asset
span
kind
size_MB
107
notas-2024.tgz
2024
year
1.09
108
notas-2025.tgz
2025
year
1.08
109
notas-2026-01.tgz
2026-01
month
0.05
110
notas-2026-02.tgz
2026-02
month
0.07
111
notas-2026-03.tgz
2026-03
month
0.09
112
notas-2026-04.tgz
2026-04
month
0.09
113
notas-2026-05.tgz
2026-05
month
0.09
114
notas-2026-06.tgz
2026-06
month
0.11
The tail of the table is the interesting part: it is exactly the boundary described above, the last couple of closed years followed by the monthly assets accumulated so far for the year in progress. Run this page again next month and one more row will have appeared there.
4 From release to the DOF Titles page
Nobody points a plotting library at the release directly: the .tgz files hold one JSON per day, which is the right shape for incremental downloading and resuming but the wrong shape for analysis. A local, gitignored notas-archivo/ extracts the release’s assets into that per-day layout, and website/scripts/aggregate_titles.py walks it once, tokenizing and counting titles, to write six small CSVs — notes_per_year.csv, days_with_notes.csv, title_length.csv, first_words.csv, headings.csv and terms_by_decade.csv — into website/data/. Unlike the raw archive, those CSVs are committed, which is what lets DOF Titles rebuild without re-downloading anything.
That page’s every figure traces back to this same release: its 1,221,451 notes over 1917–2025 are exactly what the closed-year assets above account for, and the months of 2026 already sitting in the release — visible in the table’s last rows — are the raw material for the fuller update that page still owes once 2026 itself closes.
5 Getting the raw archive yourself
Any asset can be pulled without cloning the repository:
Without gh, every entry of the assets list in the API response used above carries a browser_download_url — a direct link to that .tgz. Pick the one you want out of that same response:
url =next( a["browser_download_url"] for a in assets if a["name"] =="notas-2025.tgz")# https://github.com/INGEOTEC/LegalIA/releases/download/notas-archivo/notas-2025.tgz
To extend a local copy going forward instead of downloading a fixed snapshot, dofjson --archivo is the same tool the monthly workflow runs, and it is resumable in the same way: interrupt it and rerun it, and it picks up only the days it is still missing.
6 Downloading every title in the archive
Beyond browsing one release asset at a time, dofjson ships a --titulos mode that builds a compact codNota + titulo + fecha dataset out of every note ever published: it downloads each notas-archivo asset straight into memory and reads its daily JSON indexes without ever writing them to disk, keeping only those three fields from every note (titulo is Spanish for “title”, fecha for “date”) — codNota to fetch the note’s full content later (dofjson.client.get_nota), titulo for exploratory analysis of the titles themselves with other techniques (in the spirit of DOF Titles), and fecha to place each title in time. The result is a single gzip-compressed JSONL file — the 1.2 million-plus notes in the table above fit in a few tens of MB, small enough to move to a Colab GPU runtime for experiments.
dofjson--titulos# -> titulos/titulos.jsonl.gz
or directly from Python, with dofjson.titulos.download_titulos:
from pathlib import Pathfrom dofjson.titulos import download_titulosdownload_titulos(Path("titulos.jsonl.gz"))
import gzip, jsonwith gzip.open("titulos.jsonl.gz", "rt", encoding="utf-8") as f: notas = [json.loads(line) for line in f]notas[0]# {"codNota": 4434476, "titulo": "CIRCULAR nº. 164, relativa a los asuntos# que se traten por telégrafo con esta Secretaría", "fecha": "23-03-1917"}
Methodological note
This page is a Jupyter notebook that queries the GitHub REST API for the notas-archivo release at render time; the table above reflects the release’s contents at the moment this page was last rendered, not necessarily today. The archive, the workflow that maintains it and the aggregation script that feeds the DOF Titles page are all part of the repository.
The archive is itself a product of the collaboration that runs through the whole project: the incremental --archivo downloader and the --titulos reader in dofjson, the monthly workflow that packages and publishes each release, and this page were written by the LegalIA team together with Claude, Anthropic’s coding assistant, through Claude Code, with the authors reviewing and validating each contribution before it landed.