Luma by the numbers
Every figure below is measured directly from the data the app ships with and the code that builds it. These describe the product — catalogue size, programme coverage, translation reach and engineering — not listener activity.
The station catalogue
A merged, de-duplicated catalogue shipped inside the app, so browsing works before a single network call. Snapshot generated 2 August 2026.
Largest country catalogues
The remaining 231 countries make up the rest of the catalogue.
Catalogue completeness
Codec mix across 65,353 streams
The programme guide
Real schedules crawled from each station's own website — not a synthetic filler grid. Every station keeps its own source attribution and freshness stamp. Refreshed 28 July 2026.
Guide coverage by country
How schedules are gathered
Every broadcaster publishes its grid differently, so the generator keeps one adapter per site shape and records the exact source URL, attribution and last fetch result against each station. A failed crawl never deletes the last good schedule.
Discovery and reach
Local genre profiles are computed per country, so a listener in Cyprus sees Cypriot categories rather than a global average — and the interface speaks their language.
Curated local discovery
Category names stay in the local language where that is what listeners expect — Ειδήσεις in Greece, Noticias in Mexico, News in the UK.
Localisation
Greek, Arabic, Hebrew, Russian, Ukrainian, Bulgarian, Macedonian, Serbian, Japanese, Chinese, Korean, Thai, Persian, Hindi and Kazakh are checked for character integrity on every release.
What each listener gets back
Luma now keeps a private picture of how you actually listen, and hands it back to you. Eleven measures, computed for one person at a time, on request.
Over any window you ask for
Plus a personal play history
Station now-playing text is messy — QUEEN/BOHEMIAN RHAPSODY, Queen: Bohemian Rhapsody, ADVERT. It is cleaned, split into artist and title, matched against MusicBrainz for artwork and release, and filed with a confidence score — so “what song was that, an hour ago?” finally has an answer.
The signals behind it
| Listening events understood | 9 |
| Preference dimensions built | 7 |
| Time-of-day buckets | 4 |
| Recency half-life | 21 days |
| History window | 180 days |
| Data tables behind it | 19 |
A favourite counts eight times as much as a play; a skip counts against. Everything decays, so last week outweighs last spring.
The intelligence layer
The backend behind those statistics, built in three phases. 47 endpoints and 2 live channels — every one of them covered by tests. This describes what the API implements, not every capability is wired into the app's UI yet — social features (follow, share, listen together) are live server-side and pending a client screen.
Foundation
- Anonymous device identity — a signed token, no sign-up, no email
- Event ingestion across 9 listening signals
- Playback reporting that scores every stream out of 100
- Server-driven config, cached and revalidated with ETags
- Schedule-source registry feeding the programme guide
- Redis caching with an in-process fallback, plus rate limiting
- Background worker: stream probes, schedule refresh, reminders
User value
- Recommendation engine weighing 7 signals, explaining every result
- 6 home sections that rebuild themselves through the day
- Similar-station lookup from tag overlap plus personal taste
- Cross-device sync of 7 kinds of data, last write wins, deletes stick
- Account linking with a verified sign-in token, never a claimed one
- Programme reminders across 7 subscription types
Differentiation
- Ask in plain words — “relaxing German music without too much talking”
- What's on right now, aggregated across listeners
- Curated collections editable without shipping an app update
- Now-playing clean-up and MusicBrainz matching
- Listening history, statistics and the year in review
- Podcast search, chapters, transcripts, people and funding links
- Episode summaries written on demand and cached
- Follow friends, share what you're playing, listen together
How a recommendation is weighed
Fixed weights, published here in full. Nothing is paid for, and nothing is a black box — each suggestion carries the reason it appeared.
What we refuse to do with it
The same data could power a lot of things nobody asked for. These are the lines drawn in the code itself, not in a policy document.
- No “come back to Luma” notifications — a push must name real content you chose to follow
- Quiet hours, a daily cap and de-duplication gate every message
- Nothing is shared socially without a claimed handle, a follow, and an explicit share
- Trending needs at least 3 separate listeners before a station appears
- Known-broken streams are dropped from recommendations rather than padded out
- Statistics stay yours: they are computed per person and returned to that person
Engineering
What it takes to keep the above working across phones, cars, Bluetooth headsets and the web preview.
Backend service
A FastAPI layer for what a public catalogue cannot provide on its own: personalised recommendations, now/next programme data, stream reliability scoring, reminders and cross-device sync.
| REST endpoints | 47 |
| Operations | 47 |
| Typed schemas | 60 |
| WebSocket channels | 2 |
| Lines of Python | 7,390 |
| Automated tests | 92 |
How a station gets scored
Reliability is deterministic and explainable — no black box. Server probes and real playback outcomes blend into one rolling score out of 100.
Recommendations follow the same principle — fixed, published weights and a stated reason on every result. The full breakdown is above.