Spotify Web API: Resources, IDs, Payloads and Paging
Read the Spotify Web API like an API designer: resource map, ID vs URI, status codes, error shapes, track relinking, paging limits, ETags and snapshot_id.
The Spotify Web API as a design case study: common questions
What is the difference between a Spotify ID and a Spotify URI?
The base-62 ID (6rqhFgbbKwnb9MLmUQDhG6) does not say its type; the URI (spotify:track:6rqhFgbbKwnb9MLmUQDhG6) does, so endpoints that take mixed items, such as playlist items or the library, take URIs.
How does paging work in Spotify search?
Results come in a paging object with href, limit, offset, total, next and previous. For development-mode apps since February 2026, search limit defaults to 5 (range 0–10) and offset can go from 0 to 1000.
Related chapters
- Start here in System Design
- How a system design interview is graded
- The delivery framework: five phases and a clock
- Numbers to know: what one machine can really do
- System Design Concepts: CAP, SQL vs NoSQL, Message Queues
- SQL vs NoSQL: How to Choose a Database (System Design)
- Traffic Jumps 10×: What Breaks First? (System Design)
- Proxy vs Load Balancer vs API Gateway (System Design)
- CAP Theorem Explained Simply, with Examples
- Why Use a Message Queue? System Design Explained
- Message Queue vs Pub/Sub vs Kafka: Differences
- Polling vs Long Polling vs SSE vs Webhooks (System Design)
- API design in System Design
- REST vs GraphQL vs gRPC: Choosing an API (System Design)
- API Goals Canvas: Design APIs from the Consumer
- REST API Design: Resources, Methods, Status Codes
- HTTP Semantics, Richardson Maturity Model and HATEOAS
- GraphQL Explained: Over-fetching, Schema, N+1 Problem
- RPC and gRPC Explained: Protobuf, HTTP/2, When to Use
- API Design Best Practices: 9 Principles with Examples
- API Payload Design: Names, Types, Links and Errors
- Mapping Database Tables to API Resources, Step by Step
- API Design for Complex Schemas: Trees, Polymorphism, Bookings
- API Pagination, Idempotency Keys, Errors and Versioning
- Network-Efficient APIs: HTTP Caching, ETags, Field Selection
- Async APIs: 202 Accepted, Webhooks and Bulk Operations
- API Versioning and Evolution Without Breaking Clients
- API Contracts: OpenAPI, Contract Testing and Docs
- API Security: Authentication, Authorization, Rate Limiting
- OAuth 2, OpenID Connect and Scopes for APIs
- OWASP API Top 10 Explained: BOLA, SSRF and More
- Running APIs: Gateways, Mesh, Rate Limits, Canaries
- API Troubleshooting Interview: Slow APIs and Unexpected Errors
- API Design Review: A Staff-Level Capstone
- Spotify Web API: OAuth PKCE, Scopes, Quotas, Rate Limits
- Spotify Web API Changes: Real API Evolution Lessons
- Database-heavy designs in System Design
- Design a Distributed Key-Value Store (System Design Interview)
- Design a Hotel Reservation System (System Design Interview)
- Design a Digital Wallet (System Design Interview)
- Design S3-like Object Storage (System Design Interview)
- Games in System Design
- Shard City: Learn Database Sharding by Playing a Game
- System Design Case Studies: Flash Sale, Chat, ChatGPT
- Flash Sale System Design: Who Gets the Last Item
- Food Delivery App System Design: Which Database?
- Cache Stampede: Why the Database Fell Over and the Fix
- Live Streaming System Design: Scaling for a Final
- ChatGPT System Design: Streaming LLM Responses
- RAG System Design: Product Q&A on Sale Day
- IRCTC Tatkal System Design: Booking Under Extreme Load
- Group Chat System Design: WebSockets and Fan-Out
- Authentication in System Design: Sessions, JWT, OAuth
- Authentication Methods Compared: Sessions, JWT, OAuth
- JWT in System Design: Sessions, Revocation, Refresh Tokens
- JWT Interview Questions and Answers
- Life of a session, step by step
- JWT Structure Explained: Header, Payload, Signature
- Forge lab: why verification must be strict
- JWT vs Session Authentication: Which to Use
- The validation pipeline: where a bad request dies
- How to Revoke a JWT: Blocklists and Short Expiry
- Refresh Tokens and Rotation Explained
- Logout in depth: one device, all devices, SSO
- JWT Security Best Practices
- OAuth 2.0 and OpenID Connect Explained
- Where to Store JWT: localStorage vs HttpOnly Cookie
- JWT in Microservices: API Gateway vs Service Validation
- Microservices, keys, and trust boundaries
- Sender-constrained tokens: making a stolen token useless
- Authentication vs Authorization Middleware
- Failure explorer: what breaks when X fails
- Capacity planner: sessions vs JWT vs hybrid
- Edge cases catalog
- Test it, measure it, migrate to it
- Anti-patterns to reject in a design review
- Design checklist and cheat sheet
Keep learning
Overview
catalogue noun A plain read of one catalogue item. No user data, so no scope: any valid token works. A sub-collection: the albums of one artist, filtered and paged in the query. Not a noun you own. It returns one paging object per requested type. your stuff under /me Creating content inside a playlist is a POST that returns 201 and a new snapshot_id. see reference playlist-modify-public / -private Commands for the user’s device. Premium only. Since February 2026 one library resource takes Spotify URIs, so the URI’s type tells it what you are saving. depends on what you save depends on what you remove depends on the items Lab: open a Reference sidebar section Lab: the same track, asked for with and without a market market parameter not sent What the catalogue has in that market this exact track another copy of it nothing playable Lab: walk a search result with next and previous ← follow previous follow next → jump to offset 990 back to start
Spotify Web API I: resources, IDs, payloads and paging
Twenty-one chapters designed a ticket site that does not exist. Now we read a real, public API with the same eyes: the Spotify Web API. Every idea here is one you have met before, and each section says where.
The Reference sidebar, read as resources
Open the Web API Reference and the sidebar is a list of nouns, not actions. That is the first lesson of starting from the consumer : name the things people care about, then give each one URLs, as in the REST chapter Catalogue nouns Albums, Artists, Tracks, Shows, Episodes, Audiobooks, Chapters. The same for every listener, read with plain GETs. Your own stuff, under /me Library, Playlists, Player, top items, the Users profile. /me means “whoever this token belongs to”. Search, Markets, Genres, Categories. They help you find or filter nouns rather than being the thing you came for. The split shows up in the paths and in the permissions: catalogue reads need no scope, while anything under /me needs the user’s consent for a scope (chapter 20 covers scopes).
ID, URI and URL: three names for one track
The ID: short, but no type The URI: carries its type spotify:track:… says what it is. That is why endpoints that take a mixed list, like adding tracks and episodes to a playlist or saving to the library, take URIs. The lesson is the consistency principle from the nine principles : one ID scheme across every resource, plus a typed form for the places where the type is not in the path. Our ticket site’s evt_8Kq2mZ prefix did the same job inside the ID itself.
Methods and status codes, the way Spotify uses them
Spotify’s own table is the one from the REST chapter : GET retrieves, POST creates, PUT changes or replaces, DELETE deletes. The status codes it documents are the ones the HTTP chapter what it means here 202 Accepted Taken in, not finished yet. 204 No Content Done, nothing to send back. PUT /v1/me/player/play answers this way. 304 Not Modified Your cached copy is still good (section 8). 429 Too Many Requests Slow down. Chapter 20 is about this one. Server side. 503 is described as temporary: retrying after a delay is allowed.
Two error shapes, and problem details
Token endpoint (RFC 6749) Invalid client secret Every other endpoint invalid id Hold that next to the payload chapter , which used RFC 9457 problem details: type, title, status, detail and a list of field errors. What matches One fixed shape per family, the status repeated in the body, and a stable code (reason) for programs next to a message for humans. What is thinner No pointer to the bad field and no list of every problem, so a form can’t highlight all mistakes at once. Why two shapes The token endpoint follows an external standard that clients already parse. Writing your own there would break OAuth libraries. So a client needs two parsers: one keyed on a string error, one on an object error. Check the type of the field before you read it.
Payload lessons from one album
Here is a trimmed GET /v1/albums/4aawyAB9vmqN3uQ7FjRGTy (values cut or illustrative). Read it with the checklist from the payload chapter Dates are honest Timestamps are ISO 8601 in UTC (YYYY-MM-DDTHH:MM:SSZ). An old album may only be known to the month, so "1981-12" comes with release_date_precision: "month" instead of a made-up day. Images are ready to use Each image has its own URL and size, widest first. The client takes images[0] for a hero and the last one for a list row, with no size math. Simplified inside, full by link The album embeds its tracks as simplified track objects inside a paging object, and each has an href to the full track. That is the embed-or-link choice: embed the small version, link to the big one. Restrictions explain “no” restrictions.reason is market, product or explicit, so the app can say why something is unavailable instead of just failing. It also shows how fields age: label, popularity, genres (always empty) and available_markets are marked deprecated on albums but still in the reference. Removing fields is the hard part of evolving an API
Markets and track relinking
Rights differ by country, so the same song can have several catalogue copies. The market parameter (an ISO 3166-1 alpha-2 code like ES, or from_token) tells Spotify which country to answer for. A user token’s country wins over the parameter, and with neither, content is “considered unavailable for the client”. When a market is given, Spotify relinks tracks: if the copy you asked for can’t play there but another copy can, it returns the playable one and says where it came from. This is the “ready-to-use data, not raw codes” idea from the payload chapter : one boolean instead of a list of countries. The trap: after relinking, the id is not the one you asked for. Save and remove using the original linked_from.id, or your library ends up with an ID that may not play elsewhere.
The paging object, and the search caps
Every list in the API, from an album’s tracks to search results, comes wrapped in the same paging object: this page limit · offset its size and start how many match next · previous full URLs, or null the page itself It is offset paging, which the pagination chapter warned can repeat or skip rows when the list shifts under you. Spotify can live with it here: an album’s tracks or an artist’s albums barely change while you read them, search results are ranked rather than a live feed, and a capped offset bounds how far any client can skip. A cursor matters most for feeds that grow at the top. default search limit allowed search limit allowed search offset old default / max, before Feb 2026 Since deep paging is capped, precision comes from the query. q takes field filters such as album:, artist:, track:, year:1955-1960, upc:, isrc:, genre:, tag:new (albums from the past two weeks) and tag:hipster (albums in the lowest 10% of popularity). type can list several types, and you get one paging object per type.
Caching, ETags and the playlist snapshot_id
Two ideas from the efficiency chapter appear almost word for word. Most responses carry Cache-Control, and when a response has an ETag you send it back to ask “has this changed?”: …etag from last time… …new version… Our ticket site: If-Match A stale version is rejected with 412, so the second editor must reload. Safe, but the loser redoes the work. Spotify playlists: snapshot_id You can pass the snapshot you based your change on, and concurrent changes are “automatically merged into the latest playlist version”. That works because adding or moving a track is easy to replay. Two more playlist facts worth knowing: a playlist can’t be both collaborative and public, and “public” is not access control, since anyone with the link can open it. There is no delete endpoint either: an owner unfollows.
The Player API: commands, not documents
Most of the API reads and writes stored things. The Player is different: PUT /v1/me/player/play, pause, seek and add-to-queue are commands to a device the user is holding, and they only work for Premium users. That is why this corner looks like The app sends these in order. The docs say “the order of execution is not guaranteed” across Player endpoints. play album PUT …/play seek 1:30 add to queue device runs them maybe in another order The design lesson comes from beyond request and reply : when commands travel to a device, 204 means “accepted”, not “the speaker is now playing”. So the client should not stack commands and assume the result. Send one, then read the real state with GET /v1/me/player (is_playing, progress_ms, device, item, actions) and decide the next step from that.
In the interview
Next: who may call all this. Spotify Web API II reads Spotify’s OAuth flows, scopes, quotas and the 429 response, building on the OAuth chapter
More courses
- Jobs, linked to lessons — Browse jobs where every requirement links to the chapter that teaches it. See how ready you are, how long the gap takes to close, and which listings look stale.
- Match a job post to courses — Paste a job post. Each skill is matched to a track here and put in learning order, foundations first.
- React from open source with patterns — Patterns extracted from Material UI, Radix, React Aria, React Hook Form, Redux Toolkit, TanStack, and Next.js — not slides about hooks.
- Database in depth — How a query becomes work. Indexes, isolation, locks, and the plans that explain why a page is slow — not an ORM tour.
- Build a real API with NestJS — One ticket-booking API, built in order: a plain Node server first, then NestJS modules, validation, a real database, and the features real clients lean on.
- Microservices — Microservice patterns one diagram at a time: how to split a monolith, how services talk, and how data stays consistent without distributed transactions.
- Kafka — A beginner-to-senior path through event logs, reliable delivery, production operations, and disaster recovery.
- AI engineering for software engineers — Eight free reads, in order, for software engineers moving into AI: when to build an agent, how to build one, how to test it, and how the model works underneath.
- Frontend system design — How a UI is sliced when it is a product, not a page: ownership of state, cache, routes, and what happens when the network lies.
- TypeScript in depth — The type system as a design tool. Inference, narrowing, generics, and the errors that mean the model is wrong — not a catalog of utility types.
- CSS in depth — See the box, then the page. Visual chapters from the box model through Flexbox surprises, Grid, selectors, and units, then backgrounds, shapes, and visual effects, each with a playground.
- Accessibility — Foundations first (WCAG in plain words, semantic HTML, lists, alt text), then hands-on HTML labs, a full pass of WAI-ARIA 1.3, and a final fix-the-page challenge.
- Docker — What an image is, why layers cache, and how containers find each other on a network.
- Redis — Fifteen Redis patterns, one picture each: cache, session, lock, counter, rate limiter, queue, and when Redis is the wrong tool.
- Kubernetes — Kubernetes from zero to operators: why it exists, Pods, Deployments and Services, then configuration, networking, storage, scheduling, scaling and security.