Spotify
0-day streakReview

Your progress

Level 1

0 XP100 XP to level 2
0day streak
0best streak
0topics started
0cards reviewed
    Review cards

    Progress is stored only in this browser. No account is required.

    Layer 4 · Full breakdown · 30 min · 11 figures

    Design Spotify

    Design a music streaming service like Spotify: search over 100M+ tracks, near-instant playback from CDNs and device caches, playlists followed by millions, offline downloads with expiring licences, royalty-grade play counting and recommendations.

    A music streaming service lets you type three letters, tap a song and hear it before your thumb has left the glass, then keeps the music going through a two-hour playlist on a patchy train connection. Spotify serves over 100 million tracks to 788 million users in 184 markets. Unlike video, the bytes are small — a 3½-minute song at 160 kbps is about 4 MB — so storing the catalogue is easy. The interesting problems are elsewhere: making playback start in a few hundred milliseconds, every time; counting plays accurately enough to pay royalties, including plays made offline and uploaded weeks later, and plays faked by bot farms; and serving playlists, search and personalised recommendations to hundreds of millions of people with rights that differ by country. This walkthrough designs that service, borrowing from what Spotify itself has published.

    Understand the problem

    It is tempting to treat this as “YouTube, but audio”. Resist it. Video design is dominated by huge files, transcoding farms and adaptive bitrate in the middle of a stream (see design YouTube). Here a whole track downloads in a few seconds, so the problems shift to latency per tap — people skip songs constantly — and to the business correctness of play counts, which decide how billions in royalties are split.

    Functional requirements

    1. Users should be able to search the catalogue for tracks, artists, albums and playlists, with typo-tolerant, as-you-type results.
    2. Users should be able to play any track with near-instant start, and play through an album or playlist with no gaps between songs.
    3. Users should be able to create, follow and collaboratively edit playlists.
    4. Users should be able to download music for offline listening on their phone or laptop.
    5. Users should be able to see their play history and personalised recommendations — a home screen and a weekly discovery playlist built from what they play.

    Non-functional requirements

    • Playback start. Under 250 ms at p50 from tap to sound, under 1 s at p99. Within a playlist, the next track starts with no audible gap.
    • Smooth playback. Fewer than 1% of plays should stutter — the rate Spotify measured in 2010 — even on mobile networks.
    • Availability. 99.99% for playback. Search, recommendations and even the playlist service may degrade, but a user who presses play on a downloaded or cached song must hear it.
    • Scale. 700 million monthly and 300 million daily users, 100 million tracks, about 7.5 billion plays a day and up to 36 million concurrent streams (worked out below).
    • Counting correctness. Every play of 30 seconds or more is counted exactly once in royalty reports, including offline plays uploaded up to 30 days later, with fraudulent plays removed. Reports may lag by a day; they may not be wrong.
    • Rights. A track plays only in markets where it is licensed, appears at the release time (often midnight local time on a Friday), and disappears within hours of a takedown.
    • Consistency. A playlist editor reads their own writes immediately; followers may see changes seconds later.

    In the interview

    Three questions change this design. “Are play counts just analytics, or do they drive payments?” — if they drive payments, you are building a ledger pipeline, not a dashboard. “Do we need offline and lossless?” — that brings in DRM, device limits and a bigger encoding ladder. “Can we assume a native client?” — a native app can keep a warm connection, a large cache and its own prefetch logic; a web page cannot.

    Out of scope

    • Podcasts, audiobooks and video. Different ingestion, ads and players.
    • Ads and subscriptions billing. The free tier’s ad insertion is a separate design.
    • Ingestion from labels in detail. We assume labels deliver lossless masters with metadata and rights, and only sketch the pipeline.
    • Social features such as friend activity, and lyrics.

    The set-up

    Plan the approach

    Name three planes early. The catalogue plane — tracks, artists, rights, search — is read-heavy metadata. The audio plane — encoded files, keys and the CDN — moves bytes and must be fast. The event plane — every play, skip and save — is write-heavy and feeds royalties, history and recommendations. Satisfy each requirement simply, then go deep on playback latency, encoding, counting, offline licences, playlists, search and recommendations.

    Core entities

    • Track. ID, ISRC (the industry’s recording code), title, artists, album, duration, loudness, and a list of audio files.
    • Audio file. One encoding of one track: codec, bitrate, size, content-key ID, plus the encoder’s delay and padding in samples (for gapless playback).
    • Rights. Per track and market: available or not, from when (release time), until when.
    • Playlist. ID, owner, collaborators, version, follower count, and items — each with its own item ID, track ID, who added it and when, and a position key.
    • Play event. Play ID (a random ID minted by the client when playback starts), user, device, track, context (which playlist or album), start time, milliseconds played, whether it was offline, and a per-device sequence number.
    • Offline licence. Device, track keys wrapped for that device, and an expiry.

    API

    Search. As-you-type, filtered to the caller’s market.

    GET /v1/search?q=tum%20hi&types=track,artist&market=IN&limit=10

    Start playback. Returns the file to fetch and a short-lived key lease, so the client can go straight to the CDN.

    POST /v1/playback/t_7Hq2
    Content-Type: application/json
    
    { "device_id": "d_91", "quality": "high", "context": "playlist:p_88" }
    {
      "file": { "id": "f_7Hq2_160", "codec": "vorbis", "kbps": 160, "bytes": 4201344,
                "url": "https://audio.cdn.example.com/f_7Hq2_160?exp=…&sig=…" },
      "key": { "id": "k_7Hq2", "wrapped": "…", "lease_expires_in_s": 86400 },
      "gapless": { "encoder_delay_samples": 1024, "padding_samples": 1764 },
      "loudness_lufs": -7.9
    }

    Edit a playlist. Operations, not whole lists, against the version the client last saw.

    POST /v1/playlists/p_88/ops
    Content-Type: application/json
    
    { "base_version": 57,
      "ops": [ { "op": "insert", "item_id": "i_k3", "track": "t_X1", "pos_key": "h3" } ] }
    { "version": 58 }

    Report plays. Batched, retried until acknowledged.

    POST /v1/events
    Content-Type: application/json
    
    { "device_id": "d_91", "events": [
      { "seq": 4411, "type": "play_end", "play_id": "pl_5c1e", "track": "t_7Hq2",
        "started_at": "2026-10-03T07:14:09Z", "ms_played": 187340, "offline": true } ] }

    Other calls — GET /v1/me/home, GET /v1/playlists/{id}?version=58, POST /v1/offline/licences — follow the same patterns.

    Back-of-envelope estimates

    Assume 700 million monthly and 300 million daily active users, each playing 25 tracks a day, with an average track of 3½ minutes (210 s). (For the method, see back-of-envelope estimation.)

    Plays. 300M × 25 = 7.5B plays a day. 7.5B ÷ 86,400 ≈ 87k play starts/s, about 175k/s at the evening peak.

    Concurrent streams. 7.5B × 210 s ÷ 86,400 s ≈ 18M listening at any moment on average, about 36M at peak.

    Bandwidth. At an average of 160 kbps, 18M × 160 kbps ≈ 2.9 Tbps. If 30% of plays come from the device cache or downloads, the network carries about 2 Tbps on average and 4 Tbps at peak.

    Track sizes. 160 kbps ÷ 8 = 20 KB/s, × 210 s = 4.2 MB. A 320 kbps file is 8.4 MB.

    Catalogue storage. A lossy ladder of 24 + 96 + 160 + 320 = 600 kbps is 600 × 210 ÷ 8 ≈ 15.75 MB per track. Web AAC at 128 + 256 kbps adds about 10 MB. Lossless FLAC varies with the music; assume about 1,100 kbps, or 29 MB. Total ≈ 55 MB per track, and 100M × 55 MB ≈ 5.5 PB — about 16 PB with three copies, plus the label masters.

    Hot set. Plays are heavily skewed towards popular tracks. The top 100,000 tracks at 160 kbps take 100,000 × 4.2 MB = 420 GB: one CDN server can keep them all on SSD.

    Events. Four playback events per play (start, progress, end, sometimes skip): 7.5B × 4 = 30B a day ≈ 350k events/s, about 700k/s at peak — before UI and search events. Spotify reported pushing more than 700,000 events per second as far back as 2016, and load-tested its new pipeline at 2 million per second.

    Search. 300M × 5 searches × 4 keystroke requests = 6B a day ≈ 70k queries/s, about 150k at peak.

    Playlists. Assume 5 billion playlists averaging 60 tracks: 300B items at about 40 bytes ≈ 12 TB. Small, but read on every app open.

    The numbers point the design. Audio is small, immutable and skewed, so a CDN plus device cache does almost all the work and storing every format everywhere is cheap. 175k play starts a second each need a key and a file URL, so that check must be one fast, cacheable call. And 700k events a second that decide money need a durable queue and de-duplication, not a fire-and-forget log.

    Tip

    Size audio in kilobytes per second: kbps ÷ 8. 160 kbps is 20 KB/s, so one minute is 1.2 MB and a typical song is about 4 MB. Then most capacity questions answer themselves.

    High-level design

    Users should be able to search the catalogue

    Labels deliver lossless masters, metadata and rights. An ingestion pipeline validates them, encodes every format, encrypts the files into an audio origin (blob storage), and writes tracks, artists, albums and per-market rights to the catalogue database. Changes stream from the catalogue — change data capture — into an indexer that builds search documents, so search never reads the database on the hot path.

    Step 1: ingestion fills the catalogue and the audio origin; search queries hit a separate index, filtered by the listener’s market.Notice The search path never touches the catalogue database except to hydrate a page of results, and never touches audio at all.

    A query flows App → API gateway (which authenticates and attaches the user’s market) → search service → index. The service retrieves a few hundred candidates by text match, drops anything not licensed in the user’s market, ranks the rest, and hydrates the top 10 with artwork and artist names from a cache in front of the catalogue. The deep dive covers typos, transliteration and ranking.

    Users should be able to play any track with near-instant start

    Playing a track is two small conversations. The app asks the playback service for the file to fetch and a key: the service checks rights (market, plan, release time), picks the encoding for the requested quality, and returns a signed CDN URL plus the content key, wrapped for this device, from the key service. The app then fetches the encrypted file from the nearest CDN edge with HTTP byte-range requests, decrypts and decodes it, and starts playing.

    Step 2: the playback path. One small metadata call, then bytes straight from the CDN edge.Notice The first request asks only for the first 64 KB — about three seconds of audio — so sound starts while the rest of the track is still arriving.

    Audio files are immutable and named by file ID, so every CDN edge can cache them forever; only the URL signature and the key are per user. Because a whole 4 MB track downloads in a few seconds on a decent mobile connection, the client keeps fetching ahead — on metered data only some 30 seconds ahead, since a skip wastes the rest — and caches what it plays on disk. Spotify’s 2010 desktop client used a cache of up to 10% of free disk space (at most 10 GB by default), evicted least-recently-used.

    Users should be able to create, follow and collaboratively edit playlists

    A playlist service owns playlists. Edits arrive as small operations — insert, remove, move — against the version the client last saw; the service orders them, assigns version 58, and appends them to the playlist store along with periodic snapshots. Reading a playlist means reading a snapshot by (playlist ID, version), which never changes once written, so it can sit in a snapshot cache or even on the CDN.

    Step 3: playlist edits go through one service that orders them; reads come from immutable, versioned snapshots.Notice The asymmetry: a hit playlist might change twice a week but be opened millions of times a day, so reads must never touch the write path.

    Following a playlist only adds its ID to your library; nothing is copied. When your app syncs your library it learns each followed playlist’s latest version and fetches that snapshot if its cached copy is older — fan-out on read, which is right when one list has millions of followers.

    Users should be able to download music for offline listening

    Downloading is playback without the network at play time. The app asks a licence service for offline licences — the content keys for each track, wrapped to a key that lives in the device’s secure hardware, with an expiry — then downloads the encrypted files from the CDN. Spotify’s rules are public: up to 10,000 tracks on each of up to 5 devices, and the device must go online at least once every 30 days to keep them, “so Spotify can collect play data to compensate artists”.

    Step 4: an offline download, nine days without signal, and the check-in that keeps it alive.Notice Offline plays are not lost, only delayed: they wait in the phone’s outbox until the check-in, which also renews the licences.

    Users should be able to see their play history and recommendations

    Every play produces events. The app writes them to a local outbox and sends them in batches to an event gateway, which writes them to a durable event queue and only then acknowledges. Stream jobs keep “recently played” fresh in a history store within seconds. The queue also lands in a data lake in hourly partitions, where batch jobs compute royalty reports, charts and recommendations, writing per-user lists to a recs store that the home screen reads.

    Step 5: one event pipeline feeds play history in seconds and royalties and recommendations in batch.Notice The gateway acknowledges only after the queue has the event — the phone keeps it until then, which is what makes delivery at-least-once.

    The system now works end to end: search, playback from the CDN, playlists, offline downloads and a pipeline that turns plays into history and recommendations. Now make it fast, correct and safe.

    Deep dives

    How do we make playback start in under 250 ms?

    Start with where the time goes. Between the tap and the first sound the client must (1) learn which file to fetch and get a key, (2) get the first bytes, and (3) decrypt, decode and fill the audio output buffer. On a mobile network one round trip is 30–100 ms, and a fresh TCP plus TLS connection costs two or three of them before any data moves — so the first rule is never open a connection on the tap. Keep one warm HTTP/2 or QUIC connection to the API and one to the CDN.

    Spotify’s 2010 paper by Gunnar Kreitz and Fredrik Niemelä is the best public evidence of what matters. Its desktop client achieved a median playback latency of 265 ms, with the 75th percentile at 515 ms and the 90th at 1,047 ms. On a fresh pick the client asked the server for roughly the first 15 seconds of audio over an already-open TCP connection while it searched for peers. Even cached tracks needed one network round trip for DRM, so the median mostly measured that round trip plus local processing. Two small details moved the numbers: turning off TCP’s “slow start after idle” on the servers cut average latency by about 50 ms, and a bug that disabled next-track prefetching for a week pushed the median to 390 ms and stutter from 1.0% to 1.8% of plays.

    Tap to first sound in three cases, using the design’s own targets, against Spotify’s 2010 median.Notice A prefetched or cached track needs no network at all once its key is held as a lease — that is the case to make common.

    The design attacks each slice:

    • Make the common case local. 61% of plays in 2010 were predictable — the previous track ended or the user pressed next — and only 39% were fresh picks. Prefetch for the predictable ones, and keep a large on-device cache for repeats: in 2010, 55.4% of the bytes played came from the cache.
    • Hold keys as leases. Instead of a DRM round trip on every play, give the device a key lease for a day; a cached or prefetched track then plays with zero network calls. Fetch keys for the tracks visible on screen while the user scrolls.
    • Ask for a small first range — 64 KB, about 3 seconds at 160 kbps — so the first response is one round trip plus about 25 ms of transfer on a 20 Mbps link. Fetch the rest of the track immediately after.
    • Keep the hot set at the edge. The top 100,000 tracks fit on one edge server (420 GB at 160 kbps), so cold taps on popular music are edge hits; the long tail goes through a regional tier to the origin, which is what sets the p99.
    Prefetching the next track while the current one plays, so the transition costs nothing.Notice The two thresholds: fetch early enough to finish on a slow network, late enough that a skip does not waste much.

    Spotify’s 2010 client began fetching the next track when 30 seconds or less remained and made sure its beginning was local by the 10-second mark; users went on to the scheduled next track in 92–94% of those cases, so little prefetch was wasted. Gapless playback needs one more piece: lossy encoders add a few milliseconds of silence at the start (encoder delay) and pad the last frame. Store both numbers in samples with each file, decode the next track’s first frames ahead of time, and trim them, so a live album or a DJ mix joins sample-perfectly.

    What about peer-to-peer? In 2010 most of Spotify’s data came from peers and caches: 8.8% of bytes from servers, 35.8% from the P2P network and 55.4% from local caches. Even then the latency-critical first request went to a server, and mobile clients did not take part in P2P at all.

    P2P + servers CDN + device cache
    Server egress Low: 8.8% of bytes in 2010 All non-cached bytes, paid per GB
    Mobile Phones did not participate Works the same on every device
    First bytes Still from a server, for latency From the nearest edge
    Moving parts Trackers, NAT traversal, peer selection, users’ upload bandwidth Cache fill, edge capacity planning
    Wins when Bandwidth is expensive and users sit on desktops Bandwidth is cheap and listening is mobile-first
    Peer-to-peer plus servers (Spotify 2010) versus CDN plus device cache

    With listening now mostly on phones and CDN bandwidth cheap, the CDN plus a big device cache is the right default; mention P2P as the historical answer to a bandwidth bill, not as today’s design. The CDN mechanics are covered in content delivery network.

    Which formats do we store, and how does quality adapt?

    Spotify’s support pages list today’s ladder: on apps, about 24, 96, 160 and 320 kbit/s; on the web player, AAC at 128 and 256 kbit/s; and lossless up to 24-bit/44.1 kHz FLAC for Premium. In 2010 the files were Ogg Vorbis at quality 5 (about 160 kbps) and quality 9 (about 320 kbps).

    Format Bitrate One 3½-minute track Used for
    Low ~24 kbps ~0.6 MB Very poor networks, data saver
    Normal ~96 kbps ~2.5 MB Default on mobile data
    High ~160 kbps ~4.2 MB Default on Wi-Fi
    Very high ~320 kbps ~8.4 MB Premium
    Web AAC 128 / 256 kbps ~3.4 / 6.7 MB Browsers
    Lossless FLAC ~1,100 kbps (varies) ~29 MB Premium, chosen explicitly

    Encode once, store everything. At about 55 MB per track the full catalogue is around 5.5 PB, so there is no reason to transcode on demand. Keep the label masters forever: adding a codec — the 2010 catalogue was Ogg Vorbis only, today’s includes AAC and FLAC — means re-encoding 100 million tracks from masters, a big but embarrassingly parallel batch job.

    Adapt per track, not per segment. Video players switch bitrate every few seconds because a film is gigabytes. A song is a few megabytes: even on a 2 Mbps connection, a 160 kbps track downloads 12 times faster than it plays. So the client measures recent throughput and picks the quality when a track starts, then downloads the whole file ahead. It drops mid-track only in an emergency — Spotify’s 2010 client had an “emergency mode” below 3 seconds of buffered audio. This is much simpler than video’s adaptive bitrate, and is a good point of contrast with design Netflix.

    Two cheap extras: store each track’s measured loudness so the player can normalise volume between a quiet acoustic song and a loud pop single without re-encoding anything, and keep a hash of every file so a corrupted CDN object is detected and refetched.

    How do we count plays reliably enough to pay royalties?

    Royalties are split by stream counts, so counting is a correctness problem, not an analytics one. The rules come from Spotify’s artist documentation: a song stream counts after at least 30 seconds of play, and plays of downloaded music are counted when the listener goes online, which must happen at least every 30 days.

    Bad: increment a counter when playback starts. It counts skips, misses every offline play, double-counts whenever a request is retried, and gives a bot farm a direct lever on the payouts.

    Good: client events, delivered at least once, de-duplicated on the server. The client mints a random play ID when a track starts and reports play_end with the milliseconds actually played. Events go into an outbox on the device that survives the app being killed and are retried until the gateway acknowledges. The gateway acknowledges only after the event is durably in the queue. That is at-least-once delivery: nothing is lost, and some events arrive twice. Spotify’s own redesign made the same choices — hand events off close to the producer with a fast acknowledgement, use a queue with at-least-once semantics that keeps undelivered data for 7 days, and de-duplicate in the export job.

    Great: two speeds, one immutable log.

    The royalty path: at-least-once in, de-duplicated by play ID, qualified, fraud-filtered and written to a ledger. Below, a late offline play.Notice A play is counted on the day it was played, not the day it arrived — so a month’s report can only close after the 30-day offline window.
    • Fast path, approximate. A stream job de-duplicates within a one-hour window to update “recently played”, live dashboards and trending charts. One hour of play IDs is 7.5B ÷ 24 ≈ 312M IDs × 16 bytes ≈ 5 GB of state — affordable in a keyed state store.
    • Slow path, exact. Royalties come from the raw, immutable event log in the data lake. A daily batch groups each event-date partition by play ID (35 days of IDs would be 4 TB of state — a batch job, not a stream), keeps plays of at least 30,000 ms, joins the rights that applied in that market on that date and the user’s plan, runs fraud filtering, and writes counts to the royalty ledger keyed by track, market, day and report version. Re-running a day produces the same rows, so the job is idempotent and auditable.
    • Two clocks. Spotify’s pipeline stamps each event with the gateway’s time and partitions by arrival hour, because it cannot trust client clocks, and it must know an hour is complete before downstream jobs read it. Royalties, though, need the time of play. Keep both: arrival time drives the pipeline; play time, corrected by the offset between the device clock and the server at upload, decides which day is credited. Offline plays arrive up to 30 days late, so a month’s report closes only after that window, and later stragglers go into an adjustment.

    Fraud. Stream farms run thousands of accounts on cloud IP ranges, each playing the same handful of tracks for 31–35 seconds, around the clock, with no searches, no skips and no saves. Score plays on account, device, network and track features, and cluster accounts that behave identically. Spotify’s public policy shows the operational side: public stream counts are cleaned daily, royalties for artificial streams are withheld, and since April 2024 labels and distributors are charged per track when flagrant artificial streaming is detected. Remove fraud before the ledger, and keep the raw events so a later, better model can re-score old months.

    In the interview

    Say it in one line: “Plays are at-least-once into a durable queue, exactly-once in the ledger. A random play ID makes duplicates harmless, the 30-second rule and rights are applied in batch from the raw log, and each play is credited to the day it happened — so offline plays and late fraud decisions just produce adjustments.”

    How do offline downloads stay licensed?

    Labels license music for streaming to paying subscribers, so a download must stop working when the subscription, the market or the licence ends — without the phone being online at play time.

    • Content keys. Each encoded file is encrypted once with its own content key. The CDN only ever stores ciphertext, so the files can be cached anywhere.
    • Device keys. On first launch the app creates a key pair inside the phone’s secure hardware (the keystore or secure enclave); the private key never leaves it. The device registers its public key with the licence service.
    • Licences. For each downloaded track the licence service returns the content key encrypted to that device’s public key, with an expiry 30 days out, signed by the server. Playback checks the licence and expiry locally and needs no network.
    • Check-in. At least once every 30 days the app uploads queued play events, then renews licences. This is where revocations land: a track taken down, a market change after you moved countries, a lapsed subscription — the licence is simply not renewed.
    • Limits. The server tracks devices with downloads: up to 5 devices and 10,000 tracks each. Downloading on a sixth device removes downloads from the device used least recently.

    The device cost is real: 10,000 tracks at 320 kbps is 10,000 × 8.4 MB = 84 GB, or 42 GB at 160 kbps, so the app must show storage use and let users choose download quality. Two failure modes to name: a user who winds the phone’s clock back to stretch a licence (keep a monotonic counter and the last server time seen; a clock that goes backwards voids the licence at the next check), and a phone that never checks in (its plays are never counted, and after 30 days its downloads stop). DRM is never perfect — audio can always be re-recorded through the speaker — so the goal is to keep honest users honest and meet the licence contract, not to be uncrackable. See encryption and key management.

    How do playlists handle millions of followers and collaborative edits?

    Two very different loads hit the playlist service. Editorial hit playlists change a few times a week and are opened millions of times a day. Collaborative playlists — a trip, a wedding, a party — are edited by several people, sometimes at the same moment, sometimes offline.

    Ordering. Users care about order, and they insert anywhere.

    • Bad: integer positions. Inserting at the top of a 10,000-track playlist renumbers 10,000 rows, and two people inserting at position 3 at once collide.
    • Good: a linked list of previous and next pointers. Inserts touch two rows, but two concurrent inserts at the same spot corrupt the chain, and reading the list means walking it.
    • Great: fractional position keys. Each item carries a string key that sorts between its neighbours: between c and m you can always mint h, and between h and m you can mint k. Add a short random or client-specific suffix so two people inserting at the same spot get different keys, and break exact ties by item ID. An insert writes one row, a move rewrites one key, a delete leaves a tombstone, and nothing is ever renumbered. Rebalance keys in the rare case they grow long.
    Two collaborators insert into the same gap of version 57 at the same moment; both inserts survive.Notice Neither edit had to know about the other: the server orders them, and the keys order the tracks.

    Concurrency. Partition playlists by ID so every op for p_88 reaches one leader, which applies ops in arrival order and bumps the version. Because position keys make inserts commute, most concurrent edits need no conflict handling at all. The few that do get simple rules: a remove beats a concurrent move of the same item, and the same track may appear twice because each item has its own ID. Offline edits queue on the device and replay on reconnect under the same rules. You do not need a full CRDT, because a server orders every op; that machinery is for true peer-to-peer editing, as in design Google Docs.

    Followers. Never push a change to 30 million followers. The library stores only playlist IDs; on sync, the app sends the versions it holds and fetches newer snapshots, each cached by (ID, version) and paged 100 items at a time. Follower counts are sharded counters, shown approximately. Cap playlist size (say 10,000 items) so a snapshot stays bounded.

    How does search handle typos, transliteration and market rights?

    Spotify has described its search as mostly term matching in Elasticsearch, made forgiving with “fuzzy matching, normalization, and even manual aliases”. Build the same layers (how search engines work covers the inverted index itself):

    • Normalise at index and query time: lowercase, strip accents (“Beyoncé” → “beyonce”), and transliterate scripts, so “तुम ही हो” and “tum hi ho” meet in the same tokens — essential in markets like India where people type Hindi, Tamil or Punjabi titles in Latin letters.
    • Prefixes for as-you-type: index edge n-grams so “tum h” matches “tum hi ho” without a scan, and cache the hottest prefixes, which repeat endlessly.
    • Typos: allow edit distance 1 for medium words and 2 for long ones (“arjit singh” → “arijit singh”), never on very short tokens, where it matches nonsense.
    • Aliases: curated synonyms such as “ARR” → “A. R. Rahman”.

    Ranking is two-phase. Retrieve a few hundred candidates by text score multiplied by a popularity prior — plays in that market over recent weeks, decayed, updated hourly as partial document updates. Then re-rank the top 50 with a model that knows the user’s affinity for artists and genres. Without the popularity prior, karaoke covers and tributes can outrank the original recording.

    Rights are a filter, not a ranking signal. Each document carries the markets where it is available and per-market release times; the search service adds market = IN and “available now” to every query, so an unlicensed track never comes back. Index Friday’s releases days early with an embargo time rather than indexing millions of tracks at midnight.

    Scale. 70k queries a second on average and 150k at peak. Tracks, albums, artists and public playlists — assume 500 million documents at about 1 KB, around 500 GB — split into 20 shards of 25 GB, replicated within each region until the replicas carry the QPS. In 2022 Spotify added dense, vector-based retrieval for podcast episodes as an additional source next to Elasticsearch, re-ranked with popularity, because vectors miss exact matches that term search gets right. Typeahead specifics are in design typeahead.

    How do we build a weekly discovery playlist for 300 million people?

    Keep this proportionate: in a Spotify interview, recommendations are one deep dive, not the whole hour.

    • Signals. Plays of 30 seconds or more are positive; skips are weak negatives; saves and playlist adds are strong positives. Billions of human-made playlists are a gift: tracks that sit together in many playlists probably belong together.
    • Embeddings. Weekly, factorise the implicit user × track matrix into vectors — a few dozen to a hundred dimensions — so a user’s taste and a track’s character live in one space. Spotify’s Annoy library was built for exactly these vectors. New tracks have no plays, so derive vectors for them from audio analysis and text written about the artist.
    • Candidates. Exact search would compare each of 300M users with each of 100M tracks: 3 × 10¹⁶ dot products a week. Approximate nearest neighbours cut that to milliseconds per user. Annoy builds a forest of random-projection trees into a static file that many processes memory-map and share; it cannot add items after building, so rebuild it with each weekly batch.
    • Filter and rank. Drop tracks the user already knows or that are not licensed in their market, cap two tracks per artist, rank with a model, keep 30.
    • Serve. Write the result as an ordinary playlist. Serving it is just reading a playlist snapshot.
    A weekly discovery pipeline: signals become vectors, nearest neighbours become candidates, and a ranker writes a playlist.Notice Nothing here runs on the request path: by Monday morning, each user’s list is just another cached playlist.

    The home screen is a separate, online problem: Spotify describes it as shelves of cards chosen by a multi-armed bandit that balances exploitation (what you already play) with exploration (things you might like), evaluated counterfactually from logged data rather than only by A/B tests. See design a recommendation feed for the general pattern and vector databases for ANN serving.

    The final design

    Bring the three planes together. The catalogue plane answers “what exists and where may it play”; the audio plane moves bytes; the event plane turns plays into history, royalties and recommendations.

    The complete design: four request paths from the client, with audio bytes on their own path through the CDN.Notice The only per-play calls to Spotify’s own servers are one small playback request and a batched event upload; the megabytes come from the CDN or the device.

    Follow a listener in Mumbai through an evening:

    1. Search. She types “tum hi”. The search service normalises and transliterates the query, retrieves candidates from the index with market = IN, re-ranks them by popularity and her taste, and hydrates the top 10 from a cache in front of the catalogue.
    2. Play. She taps a track. The playback service checks rights for India and her plan, picks the 160 kbps file, and returns a signed CDN URL with a key lease from the key service — one small call.
    3. Bytes. The app fetches the first 64 KB from the nearest CDN edge, where the track is almost certainly on SSD, starts playing in about 140 ms, fetches the rest, and caches it. Thirty seconds before the end it prefetches the next track; the join is gapless.
    4. Playlists. She adds the song to a trip playlist her friends also edit. The insert carries a fractional position key and base version 57; the playlist service orders it, writes version 58, and friends pick up the new snapshot on their next sync.
    5. Events. Each play’s events, with a play ID, sit in the app’s outbox until the event gateway confirms they are in the queue.
    6. Pipelines. Stream jobs update her recently played list within seconds. The nightly batch de-duplicates by play ID, applies the 30-second rule and the rights in force on that day, removes fraud, and writes the royalty ledger.
    7. Outputs. Labels receive reports from the ledger; on Monday her home screen shows a fresh weekly mix built from the same events. If she downloaded the playlist before a flight, her offline plays join this path at the next check-in.

    Against the requirements: start latency is about 140 ms for an edge hit, 40 ms for a prefetched or cached track and roughly 300 ms for a long-tail miss, inside 250 ms at p50 and 1 s at p99. Smoothness comes from whole-track fetch-ahead and per-track quality choice. Availability: playback depends only on the playback call and the CDN, and cached or downloaded music needs neither. Scale: 2–4 Tbps of small immutable files is a CDN workload, and the catalogue is a few petabytes. Counting is at-least-once in and exactly-once in the ledger, credited by play date. Rights are filters applied at search, at playback and at every licence renewal.

    What interviewers look for by level

    Area Mid-level Senior Staff
    Requirements Search, play, playlists Names start latency, gapless and rights per market as requirements Separates analytics counts from royalty counts; asks about offline and lossless up front
    Playback latency “Use a CDN” Latency budget, warm connections, prefetching the next track, device cache Key leases, first-range sizing, edge hot set, P2P versus CDN trade-off with evidence
    Audio and formats One bitrate Encoding ladder, storage math Per-track quality choice versus video ABR, gapless metadata, keeping masters for re-encodes
    Play counting Counter in a database Events through a queue Play IDs, outbox, at-least-once in and exactly-once in the ledger, event time versus arrival time, fraud before payout
    Offline Download the files Encrypted files, expiring licence Device keys in secure hardware, check-in as revocation point, device limits, clock tampering
    Playlists A table of positions Versioned lists, followers read snapshots Fractional keys, single ordering point, fan-out on read for huge followings
    Search and recs Full-text search; “use ML” Market filters, typo tolerance, popularity; collaborative filtering Transliteration, two-phase ranking, embeddings with ANN, cold start, batch serving as playlists

    Follow-up questions

    What if a superstar releases an album at midnight?

    Push the encrypted files to CDN edges days before release; without keys they are useless, so pre-positioning leaks nothing. Index the tracks early with an embargo time. At midnight local time in each market, the rights check flips, so the spike rolls around the world time zone by time zone rather than arriving at once. The hottest keys are the same 15 tracks requested by millions: keep their rights and key metadata in memory on every playback-service instance rather than hitting a database, and let the event queue absorb a 10× burst of play events while the pipelines catch up. See hot keys and cache stampedes.

    What if every user switched to lossless?

    CDN egress grows by about 1,100 ÷ 160 ≈ 7×, from roughly 2 Tbps to 14 Tbps on average, and each track costs about 29 MB to stream instead of 4.2 MB. Storage barely changes, because the lossless files are already in the 5.5 PB. Offline gets awkward: 10,000 lossless tracks would need about 290 GB. So default lossless to Wi-Fi only, step down on mobile data, and cap or warn on lossless downloads. The real cost is the bandwidth bill, one good reason to keep it a Premium option.

    What if a CDN has a regional outage?

    Use more than one CDN. The playback service returns a primary and a secondary URL for the same file; if the first range does not arrive within about a second, the client retries it on the other. A mid-tier cache (an origin shield) in front of the audio origin stops the refill storm from flattening the origin when traffic shifts. Meanwhile the device cache and downloads keep many plays working with no network at all — the 2010 data had more than half of all bytes played coming from local caches.

    What if a label pulls its catalogue from one market tomorrow?

    Rights change in the catalogue and flow out through change data capture: the search index filters the tracks out, the playback service’s rights cache (with a TTL of a few minutes, or an invalidation event) starts refusing them, and playlists keep the items but show them as unavailable. Offline copies keep playing until the device’s next check-in, when their licences are not renewed — so the 30-day window bounds how long a withdrawn track can keep playing offline. Play counts for the track simply stop accruing in that market.

    That belongs on the fast path, not the royalty path. Count de-duplicated play events per track per city in sliding windows on the stream processor, apply cheap fraud rules (drop accounts already flagged, cap plays per account per track), and keep the top 50 per city. Exactness is not required, so approximate structures are fine at scale — see count-min sketch and HyperLogLog and design top-K trending. The nightly batch remains the source of truth for anything that pays money.

    How to say it in the interview

    “I’ll design a Spotify-style service: about 300 million daily listeners, 100 million tracks, 7.5 billion plays a day, playback that starts in under 250 milliseconds, and play counts accurate enough to pay royalties.

    I split it into three planes. The catalogue plane holds tracks, artists and per-market rights in a sharded database, streamed by change data capture into an Elasticsearch-style index, with every query filtered by the user’s market.

    The audio plane is surprisingly small: a 3½-minute song at 160 kbps is about 4 megabytes, and every format of every track is around 5 petabytes. Files are encrypted once and served from CDN edges. To start fast, the app keeps warm connections, makes one small playback call that returns a signed URL and a key lease, fetches a 64-kilobyte first range, and prefetches the next track 30 seconds before the current one ends, trimming encoder padding so albums play gaplessly. A large device cache makes repeats free. Spotify’s 2010 client hit a 265-millisecond median this way, with peer-to-peer then; today a CDN plus device cache is the better trade.

    The event plane decides money. The client mints a play ID per play and keeps events in an outbox until the gateway confirms they are in a durable queue — at least once. A batch job de-duplicates by play ID, applies the 30-second rule and the rights on the day of the play, filters stream farms, and writes an idempotent royalty ledger. Offline plays arrive up to 30 days late and are credited to the day they happened.

    Offline downloads use content keys wrapped to a device key, with 30-day licences renewed at check-in. Playlists are versioned with fractional position keys, so concurrent inserts both survive, and followers read immutable snapshots. Recommendations are weekly embeddings plus nearest-neighbour candidates, written out as ordinary playlists.”

    Concepts used in this design

    References and further reading

    1. Spotify — Large Scale, Low Latency, P2P Music-on-Demand Streaming (Kreitz and Niemelä, IEEE P2P 2010)web.archive.orgMedian playback latency 265 ms (p75 515 ms, p90 1,047 ms), the 15-second first request, prefetch at 30 s and 10 s before the end, 8.8% of bytes from servers, LRU caches and Ogg Vorbis q5/q9.
    2. Audio quality (Spotify Support)support.spotify.comToday’s ladder: about 24, 96, 160 and 320 kbit/s on apps, AAC 128/256 kbit/s on the web player, lossless up to 24-bit/44.1 kHz FLAC, and auto-adjust.
    3. How your streams are counted (Spotify for Artists)support.spotify.comA stream counts after at least 30 seconds of play; downloaded plays are counted when the listener goes online, at least once every 30 days.
    4. Artificial streaming (Spotify for Artists)artists.spotify.comDaily cleaning of public counts, withheld royalties for artificial streams, and per-track charges to labels and distributors since April 2024.
    5. Spotify’s Event Delivery – The Road to the Cloud, Part I (Spotify Engineering, 2016)engineering.atspotify.comClient events timestamped at the gateway, hourly buckets that must be complete before jobs read them, and more than 700,000 events per second.
    6. Spotify’s Event Delivery – The Road to the Cloud, Part II (Spotify Engineering, 2016)engineering.atspotify.comHand-off to a local service with fast acks, one topic per event type, an at-least-once queue keeping data for 7 days, and an ETL job that de-duplicates.
    7. Spotify company info (Spotify Newsroom)newsroom.spotify.comScale today: over 100 million tracks, 788 million users including 306 million subscribers, in 184 markets.
    8. For Your Ears Only: Personalizing Spotify Home with Machine Learning (Spotify Engineering, 2020)engineering.atspotify.comHome as shelves of cards, chosen by a multi-armed bandit balancing exploitation and exploration, plus counterfactual evaluation.
    9. Annoy: Approximate Nearest Neighbors Oh Yeah (Spotify, GitHub)github.comMatrix-factorisation vectors for users and tracks, searched with static, memory-mapped forest-of-trees indexes shared across processes.
    10. Introducing Natural Language Search for Podcast Episodes (Spotify Engineering, 2022)engineering.atspotify.comSearch built mostly on Elasticsearch term matching with fuzzy matching, normalisation and aliases; dense retrieval in Vespa added as an extra source, re-ranked with popularity.