Car market
I wanted to know what a car really costs in Tunisia, and how that compares with Europe. I couldn’t find those numbers side by side anywhere, so I scrape them every day.
- Sources
- 3 sites, 9annas.tn, AutoScout24, automobile.tn
- Runs
- GitHub Actions every day, plus a Raspberry Pi twice a month
- Started
- January 2026
Latest scrape
Snapshot from
- 9annas.tn Used cars in Tunisia 12,503 listings Every day ·
- AutoScout24 Cars up to 8 years old in Germany, France, Italy, and Belgium 2,287 listings Every day ·
- automobile.tn New-car list prices in Tunisia 565 trims Twice a month ·
Used cars in Tunisia
Asking prices, mileage, fuel, gearbox, and governorate, with the most listed models next to their European price.
New cars in Tunisia
Official list prices for every trim, by brand, budget, fuel, and body type.
Europe
Median prices in four countries over time, and every listing linked to AutoScout24.
Catalog history
Which new-car prices went up or down between two scrapes, and which trims were added or removed.
How it works
Scrapers, one build step, and static files. No server.
Scrape
GitHub Actions runs at 02:00 UTC every day for 9annas.tn and AutoScout24. A Raspberry Pi at home scrapes automobile.tn on the 1st and 15th of the month.
Clean
Listings with an impossible price, year, or mileage are dropped, duplicates are removed, and free-text fuel types are mapped to a few known values.
Build
A Python step writes a small summary file, a daily history with one point per day, and column-packed listing tables.
Publish
The workflow commits the files, and GitHub Pages serves them as static JSON.
Show
This site reads that JSON straight from GitHub Pages. There is no server and no database.
What was hard
The parts that took more than one try.
automobile.tn blocks GitHub
Since May 2026 the site returns 403 to GitHub runner IPs. The scraper moved to a Raspberry Pi with a polite rate limit, and the daily workflow fails when the new-car data is more than 18 days old, so a broken cron can’t hide.
9annas.tn has no page numbers
Its API pages with a cursor that encodes the last listing’s id and timestamp as letters. I decoded the scheme, so the scraper walks every page and stops at the first listing it already has.
Listings are written by people
Some ask 100,000,000 DT, some are from the year 8 or 9999. The dashboard keeps prices between 3,000 and 400,000 DT and plausible years, and it shows how many listings it left out.
AutoScout24 fights bots
Each listing is read from JSON-LD first, then from the spec table in the country’s language, then with a regex. Requests run five at a time with jittered delays and back off on 429 and 403.
The raw data is 29 MB
The dashboard first loads an 80 KB summary. The full listing tables load only when you open them.
Built with
Python for the scrapers, plain JavaScript and SVG for the charts.
Read the code on GitHub- Python
- GitHub Actions
- Raspberry Pi
- GitHub Pages
- JavaScript
The scrapers use httpx and BeautifulSoup, with Playwright as a fallback for AutoScout24. The charts are a small SVG kit with no chart library.