Self-hosted Telegram bot for tracking parcels across 24+ couriers worldwide, with a plugin architecture so you can add national couriers without forking the project.
Built for power users who already run their own infrastructure and want a small, auditable tracking bot — no cloud, no SaaS, no third-party data sharing.
Status notifications ship with a route map — rendered by the bot itself, no geocoding API, no keys.
- 24 built-in couriers — DHL, UPS, FedEx, USPS, Royal Mail, La Poste, Deutsche Post, Aramex, Australia Post, Canada Post, Correos, Correios, DPD, GLS Europe, Yodel, Evri, Bpost, PostNL, Österreichische Post, Swiss Post, Amazon Logistics, China Post, EMS, Singapore Post, Japan Post — plus a universal 17track fallback.
- Plugin extension — drop a
Trackersubclass inplugins/and the bot picks it up at startup. - Auto-detect carrier — paste a tracking number and the bot resolves the carrier by regex priority.
- Tracker health & auto-quarantine — broken couriers get sidelined automatically (3/6/12 fail → 1 h/6 h/24 h).
- Fine-grained notifications — toggle per status (delivered, in transit, exception, …) per user.
- Self-hosted route maps — notifications can attach a map of the parcel's journey, with the
transport icon (plane/ship/train/truck) inferred from the checkpoint. Offline GeoNames geocoder
- public map tiles: no geocoding API, no accounts.
- Observability — Prometheus exporter on
:9090/metrics+ structured JSON logs (structlog). - i18n — English and Italian shipped, more via PR. Per-user language via
/lang. - Privacy —
/forgetmeerases a user's data; removed/archived parcels are deleted afterDATA_RETENTION_DAYS(default 180). - Hardened container — read-only rootfs, no-new-privileges, dropped capabilities, resource limits.
git clone https://github.com/bernalli/parcel-tracker-bot.git
cd parcel-tracker-bot
cp .env.example .env
# Edit .env: TELEGRAM_BOT_TOKEN (required), OWNER_ID (required), optional API keys.
docker compose up -d
docker compose logs -fTalk to your bot on Telegram and send /start.
| Tier | Couriers | Setup |
|---|---|---|
| Tier S — Direct scrapers | UPS, USPS, Royal Mail, La Poste, Deutsche Post, Aramex, Australia Post, Canada Post, Correos, Correios, FedEx (with TNT), DPD, GLS Europe, Yodel, Evri, Bpost, PostNL, Österreichische Post, Swiss Post, DHL Express | Zero config |
| Tier D — Track17-backed detection | Amazon Logistics, China Post, EMS, Singapore Post, Japan Post | Set TRACK17_API_KEY |
| Universal fallback | 17track | Set TRACK17_API_KEY |
See docs/trackers.md for the full table with regex patterns and priorities.
Status updates can carry a rendered map: the checkpoint route as a polyline, with a transport icon inferred from the courier status (plane / ship / train / truck / parcel).
| In transit (air) | Out for delivery |
|---|---|
Geocoding is offline — a bundled GeoNames cities1000 index resolves both English and
local city names ("Milan" and "Milano") with zero network calls, down to towns of a few
thousand inhabitants. Tiles default to CARTO's
voyager basemap (English labels, retina @2x); any XYZ server works via OSM_TILE_URL +
MAP_TILE_SIZE. No accounts, no API keys, no third-party geocoding service seeing your parcel
data. Opt out with MAPS_ENABLED=false.
Map tiles © OpenStreetMap contributors, © CARTO.
- Architecture — core/plugin design, data flow
- Plugin tutorial — write your own tracker in 50 lines
- Courier API keys — DHL, UPS, FedEx, 17track tier-free options
- Observability — Prometheus + Grafana setup
- i18n — add a new language via
.pofile - Troubleshooting — common errors & fixes
- CHANGELOG
Contributions welcome — bug reports, new couriers, translations, doc fixes. See CONTRIBUTING.md for the development setup and PR checklist.
Found a vulnerability? Please do not open a public issue. Read SECURITY.md for the disclosure procedure.
MIT — see LICENSE. Copyright © 2026 Samuele Martinalli and contributors.