← All projects
Personal πŸ“… 2026 GitHub

Concert Tickets Telegram Tracker

Telegram bot that monitors ticket availability on FanSale (resale) and TicketOne (primary) and notifies the user as soon as a ticket for the tracked date becomes available. Three decoupled components: scraping API, bot and async worker.

✦ Per-artist caching to minimise requests✦ Two-level anti-bot fallback✦ Documented reverse engineering of the sites

Context

Buying concert tickets on resale sites like FanSale is a race against time: tickets appear and disappear within minutes. The project is a Telegram bot that lets users set up β€œtrackers” on specific dates and notifies them as soon as a ticket becomes available.

The system had to be efficient (few requests to the sites, low risk of anti-bot blocks) and maintainable (sites change their HTML structure and APIs constantly).

Architecture

The system is designed as three decoupled components, each with a clear responsibility:

flowchart LR
  U["Telegram user"] <--> B["Telegram bot<br/>python-telegram-bot"]
  B -->|"user choices"| API["FastAPI<br/>centralised scraping"]
  API -->|"scrapling / Playwright"| FAN["FanSale"]
  API -->|"official API"| T1["TicketOne"]
  W["Async worker<br/>(asyncio)"] -->|"periodic updates"| API
  W <--> DB[("SQLite<br/>artists / events / trackers")]
  B <--> DB

API, bot and worker: three components sharing the database

  1. API layer (FastAPI). Centralised RESTful interface for all scraping. The bot contains no scraping logic: if a site changes its structure, only the endpoint needs updating. Scraping uses scrapling (httpx + selectolax) with a Playwright fallback for JavaScript-loaded content.

  2. Bot layer (python-telegram-bot). ConversationHandler for guided stateful flows (adding/removing trackers). The bot is β€œdumb”: it presents information and collects choices, all business logic lives in the API and helpers.

  3. Worker layer (asyncio). Runs periodic background tasks: updates FanSale data at short intervals, TicketOne data every 24h (new dates or cancellations), and processes notifications by comparing fresh data with user trackers.

Key optimisation: database caching

The problem: if 100 users track 10 different concerts by the same artist, a naive approach would make 100 requests to the same FanSale page. The solution:

  • The worker makes a single request per artist and stores the full ticket list as JSON in the fansale_tickets_body field of the artists table.
  • When checking notifications, the system reads the cached data from the DB for all users, without contacting FanSale again.

This drastically reduces the number of requests and the risk of being blocked, at the cost of an update latency equal to the worker interval (configurable).

Anti-bot strategy

The reverse engineering of the sites is documented in the repository: on FanSale, headless Playwright only intercepts the Akamai challenge and then Access Denied; curl_cffi with safari17_0 impersonation instead receives the real HTML. The scraper therefore tries the lightweight path first and uses Playwright only as a fallback. Two discovery scripts (tools/) let you inspect hidden endpoints without touching the production bot.

Design choices

  • API separated from the bot: decoupling that makes scraping reusable (future dashboard/mobile) and isolated from site changes.
  • Batch updates: first the mass update of artists/events, then notification processing β€” not a tracker-by-tracker check.
  • Efficient queries: get_all_active_trackers_efficiently() loads artist data (with cached bodies) and merges them in Python with trackers, instead of complex per-user JOINs.

Results

A working end-to-end system: the user searches the artist/tour through the TicketOne API, selects the event and date, and receives instant Telegram notifications when the ticket appears on FanSale. Docker Compose for starting the whole stack.

Links