Yearly snapshots of the ULB academic catalog, one JSON file per academic year, scraped from ULB's public program search and formation API.
These snapshots feed DocHub: the
scrape_catalog management command produces a file here and sync_catalog
replays it into the DocHub database. Keeping the snapshots in their own repo
gives us a diffable history of how ULB's catalog changes from year to year.
| File | Academic year |
|---|---|
2026-2027.json |
2026–2027 |
Only the current academic year is available: ULB's live catalog no longer serves past years (as of the 2026–2027 scrape, programs had already been dropped from 2025–2026 and earlier), and the scraper refuses to emit an incomplete snapshot. New years are added here as each one is scraped.
Each file is a single JSON object:
{
"academic_year": "2026-2027",
"programs": [
{
"slug": "BA-SCI",
"name": "Bachelier en sciences",
"faculties": [{"color": "#1f77b4", "name": "Faculté des Sciences"}]
}
],
"memberships": [
{
"program": "BA-SCI",
"bloc": "1",
"course_code": "MATH-S101",
"title": "Mathématiques",
"mandatory": true,
"quadri": "q1",
"lecturers": "Prof. Alpha"
}
],
"warnings": []
}programs— every accepted program (options folded into their parents are dropped), sorted byslug.memberships— one row per course appearance in a program bloc, sorted by(program, bloc, course_code).warnings— non-fatal issues the scraper reported for that year (missing blocs, conflicting titles, …).
The files are written with sorted keys and two-space indentation so that a re-scrape produces a minimal, readable diff.
From a DocHub checkout:
uv run manage.py scrape_catalog \
--academic-year 2026-2027 \
--output ../catalog/2026-2027.jsonULB only serves years that still exist in their live catalog, so older years may scrape with more warnings or fail outright.