The 50 REST API questions interviewers actually ask, with direct answers, real HTTP examples, and what the interviewer is listening for. Grouped for freshers, intermediate, and experienced rounds.
50 questions with answersKey Takeaways
REST (Representational State Transfer) is an architectural style for building APIs over HTTP. Instead of a protocol or a framework, it's a set of constraints that Roy Fielding described in 2000: a client and server exchange representations of resources, the server holds no client session between requests, and the interface stays uniform so the same rules apply everywhere. In practice a REST API exposes resources at URLs (like /users/42), uses HTTP methods to act on them (GET to read, POST to create, PUT or PATCH to update, DELETE to remove), and answers with a status code plus a body, usually JSON. The MDN glossary defines REST as this style of API that uses HTTP requests to access and manipulate data, and it's the canonical plain-language reference for the term. In interviews, REST API questions probe how you reason about the design: which status code fits a failed validation, why a PUT should be idempotent, how you'd paginate a large collection, and where REST stops fitting and GraphQL or gRPC start. This page collects the 50 questions that come up most, each with a direct answer plus real request and response examples. Pair this bank with our AI interview preparation guides for the live-interview format, since the first technical round is increasingly an AI-driven screen.
Watch: What Is A RESTful API? Explanation of REST & HTTP
Video: What Is A RESTful API? Explanation of REST & HTTP (Traversy Media, YouTube)
Test yourself and earn a certificate
10 quick questions. Score 70%+ to download your REST API certificate.
The fundamentals every entry-level round checks: what REST is, HTTP methods, status codes, statelessness, and the JSON basics underneath. If any answer here surprises you, that's your study list.
A REST API is an interface that lets a client and server exchange data over HTTP following the REST style. It models data as resources, each with its own URL, and uses HTTP methods to act on them: GET to read, POST to create, PUT or PATCH to update, DELETE to remove.
REST stands for Representational State Transfer. The client asks for a resource, the server returns a representation of it (usually JSON) plus a status code, and no session is held between requests. It's the most common style for web and mobile APIs because it's simple, uses plain HTTP, and works with any client.
Key point: Lead with 'resources, URLs, and HTTP methods'. Interviewers open with this to hear whether you understand REST as a style, not just a buzzword.
Watch a deeper explanation
Video: What is a REST API? (IBM Technology, YouTube)
REST is the architectural style: the set of constraints Roy Fielding defined. RESTful is just the adjective for an API that follows those constraints. Saying an API is 'RESTful' means it obeys REST's rules like statelessness, a uniform interface, and resource-based URLs.
In everyday use people treat them as the same thing, and that's fine. If pushed, the honest note is that many APIs called RESTful only follow some constraints (they skip HATEOAS, for example), so 'RESTful' in practice is a spectrum, not a strict pass or fail.
Key point: Don't overclaim a hard line. Saying 'RESTful just means it follows REST constraints, and most real APIs follow them partially' indicates honest and informed.
The core methods map to CRUD: GET reads a resource, POST creates one, PUT replaces one, PATCH updates part of one, and DELETE removes one. GET is safe and doesn't change anything; the others change server state (except a well-designed PATCH or PUT that lands on the same result).
Two more come up: HEAD is like GET but returns only headers, no body, handy for checks; OPTIONS reports which methods a resource supports and drives CORS preflight. Knowing which method fits which action is the everyday core of REST design.
| Method | Action | Safe? | Idempotent? |
|---|---|---|---|
| GET | Read a resource | Yes | Yes |
| POST | Create a resource | No | No |
| PUT | Replace a resource | No | Yes |
| PATCH | Partially update a resource | No | No (usually) |
| DELETE | Remove a resource | No | Yes |
Key point: Get the safe/idempotent columns right. Mixing up which methods are idempotent is the most common slip on this question.
Watch a deeper explanation
Video: What is a REST API? (Programming with Mosh, YouTube)
GET retrieves data and shouldn't change anything on the server. Its parameters go in the URL and query string, it can be cached and bookmarked, and repeating it is safe. Because the data sits in the URL, GET isn't for sensitive input.
POST sends data in the request body to create a resource or trigger an action. It changes server state, isn't cached by default, and repeating it can create duplicates, which is why forms warn you before resubmitting. Use GET to read, POST to create or submit.
Key point: Mention that GET params are visible in the URL while POST uses the body. That practical difference shows you've actually sent both, not just read definitions.
A resource is any thing your API exposes: a user, an order, a product. Each resource has a URL that identifies it, and REST design uses nouns, not verbs, for those URLs. So it's /users and /users/42, not /getUser or /createUser, because the HTTP method already says what you're doing.
Collections are plural nouns (/users), a single item nests under it (/users/42), and sub-resources chain (/users/42/orders). The method carries the verb: GET /users/42 reads, DELETE /users/42 removes. Consistent, noun-based, hierarchical URLs are what make an API feel RESTful.
GET /users # list users
POST /users # create a user
GET /users/42 # read one user
PATCH /users/42 # update part of a user
DELETE /users/42 # remove a user
GET /users/42/orders # that user's ordersKey point: Say 'nouns in URLs, verbs in HTTP methods'. Suggesting /getUser is an instant tell that a candidate hasn't designed a real REST API.
Status codes come in five families keyed by the first digit. 1xx is informational, 2xx means success, 3xx means redirection, 4xx means the client made an error, and 5xx means the server failed. Knowing the family tells you at a glance who's at fault and what to do.
The distinction that matters most in interviews is 4xx versus 5xx: a 404 or 400 means fix the request, while a 500 means the server broke and the client can retry later. Getting that split right is the base of good API error handling.
Key point: Anchor on 'first digit tells you the family'. Interviewers care that you can reason about codes, not that you memorized all of them.
Read them off the action: 200 for a successful GET or update with a body, 201 when you create a resource (with a Location header to it), 204 for a success with nothing to return like a DELETE. On errors, 400 for a malformed request, 401 when auth is missing, 403 when auth is present but not allowed, 404 when the resource doesn't exist.
A few more separate strong candidates: 409 for a conflict like a duplicate, 422 when the body parses but fails validation, 429 when the client is rate-limited, and 500 for an unexpected server error. Returning the right code, not 200 with an error message inside, is what makes an API usable.
| Code | Meaning | Use it when |
|---|---|---|
| 200 | OK | Successful GET, or update returning a body |
| 201 | Created | A POST created a new resource |
| 204 | No Content | Success with no body, e.g. a DELETE |
| 400 | Bad Request | Malformed request the client must fix |
| 404 | Not Found | The resource doesn't exist |
| 500 | Internal Server Error | The server hit an unexpected failure |
Key point: The anti-pattern to call out is returning 200 with an error in the body. Interviewers love hearing you reject that explicitly.
All three mean success, but they differ in what happened and what comes back. 200 OK is the general success with a response body, used for a GET or an update that returns the changed resource. 201 Created means a new resource was made, typically after a POST, and usually includes a Location header pointing to the new resource.
204 No Content means the request succeeded but there's nothing to send back, which fits a DELETE or an update where you don't return the body. Picking 201 for creates and 204 for empty-body successes, instead of 200 for everything, is a small detail that signals care.
Key point: Volunteering that 201 should carry a Location header is the detail that separates careful candidates from ones who just say '200 for success'.
401 Unauthorized means you're not authenticated: the server doesn't know who you are, so you need to send valid credentials. Despite the name 'Unauthorized', it's really about authentication being missing or invalid, like a missing or expired token, rather than about permissions on a resource.
403 Forbidden means you are authenticated but not allowed: the server knows who you are and is refusing this action, like a regular user hitting an admin-only endpoint. The quick rule is 401 = who are you, 403 = you can't do that.
Key point: The names are misleading, so The rule plainly: 401 is authentication, 403 is authorization. That one sentence is exactly what's being tested.
JSON (JavaScript Object Notation) is a lightweight text format of key-value pairs and arrays that's easy for both humans and machines to read. It's smaller and simpler than XML, maps cleanly to objects in almost every language, and is native to JavaScript, which made it the default for web and mobile APIs.
REST doesn't require JSON, the style is format-agnostic, but JSON won on ergonomics. The client sends and receives JSON, sets Content-Type: application/json, and both sides parse it with built-in tools. XML still appears in older or enterprise APIs, but JSON is the everyday choice.
{
"id": 42,
"name": "Ada Lovelace",
"roles": ["admin", "editor"],
"active": true
}Key point: Note that REST isn't tied to JSON, it just usually uses it. That nuance shows you understand REST as a style separate from any format.
A request has a method and URL (GET /users/42), headers (metadata like Content-Type, Authorization, Accept), and an optional body carrying data for POST, PUT, or PATCH. Together they tell the server what you want, in what format, and who you are.
A response has a status code (200, 404), headers (Content-Type, caching, rate-limit info), and a body with the representation, usually JSON. Reading both sides fluently, especially headers, is what lets you debug an API instead of guessing.
POST /users HTTP/1.1
Host: api.example.com
Content-Type: application/json
Authorization: Bearer <token>
{ "name": "Ada" }
--- response ---
HTTP/1.1 201 Created
Location: /users/42
Content-Type: application/json
{ "id": 42, "name": "Ada" }Key point: Mention headers, not just method and body. Candidates who forget headers exist tend to struggle the moment auth or content negotiation comes up.
Headers are key-value metadata sent alongside the body on both requests and responses. They describe the request or response without being part of the data itself: what format the body is in, who's calling, and how the response can be cached. The client and server both read them to decide how to handle a message.
The ones you'll name in most interviews: Content-Type (the format of the body being sent), Accept (the format the client wants back), Authorization (credentials, often a bearer token), and caching headers like Cache-Control and ETag. Knowing Content-Type versus Accept, request-side versus what-you-want-back, is a common quick check.
Key point: Being able to explain Content-Type vs Accept cleanly is the small test hiding inside this question. Get that pair right and you've answered it.
Path parameters identify a specific resource and live in the URL path: /users/42, where 42 is the path param. Query parameters filter, sort, or paginate a collection and come after the ?: /users?role=admin&page=2. They're optional modifiers on a request, not the resource identity.
Body parameters carry the payload for methods that send data (POST, PUT, PATCH), usually as JSON. The rule of thumb: path for which resource, query for how to filter or shape the result, body for the data you're sending. Putting a filter in the path or an ID in the body is a common design smell.
GET /users/42 # 42 is a path param (which user)
GET /users?role=admin&page=2 # query params (filter + paginate)
PATCH /users/42
{ "email": "new@example.com" } # body param (the update)Key point: The clean rule 'path for identity, query for filtering, body for data' is what this question wants. Reciting it shows you've designed endpoints deliberately.
An operation is idempotent if making the same request many times leaves the server in the same end state as making it once. GET, PUT, and DELETE are idempotent: reading twice changes nothing, replacing a resource twice lands on the same result, deleting twice leaves it gone.
POST is not idempotent: two identical POSTs usually create two resources. This matters because networks retry: if a client resends a request after a timeout, an idempotent method is safe to retry, while a naive POST can double-charge or duplicate. It's a core concept interviewers return to.
Key point: idempotency connects to safe retries on flaky networks. That practical 'why it matters' beats just labeling which methods are idempotent.
A safe method doesn't change server state: it only reads. GET, HEAD, and OPTIONS are safe. Calling them has no side effects, so caches, search-engine crawlers, and browser prefetchers can call them freely without altering data. That guarantee is what makes those methods safe to automate.
It matters because tools rely on it. A search engine or a browser prefetch will happily send GET requests, and if a GET secretly deleted or modified data, that automation would cause chaos. So a link should never trigger a state change; that belongs behind POST, PUT, PATCH, or DELETE.
Key point: Mention that GET must never change state because crawlers and prefetchers call it freely. That real-world consequence is what the question is checking.
CRUD is Create, Read, Update, Delete, the four basic data operations, and REST maps HTTP methods onto them. Create is POST, Read is GET, Update is PUT (full) or PATCH (partial), and Delete is DELETE. So a resource like /users supports the whole lifecycle through standard methods.
The mapping isn't perfectly one-to-one, some actions don't fit CRUD neatly, but it's the mental model most APIs start from. When an interviewer asks you to design endpoints, walking the CRUD table for each resource is a clean, fast way to structure the answer.
| CRUD | HTTP method | Example |
|---|---|---|
| Create | POST | POST /users |
| Read | GET | GET /users/42 |
| Update | PUT / PATCH | PATCH /users/42 |
| Delete | DELETE | DELETE /users/42 |
Key point: Use the CRUD table as your scaffold for design questions. Interviewers like seeing you map each operation to a method deliberately.
An API (Application Programming Interface) is any contract that lets two pieces of software talk: a set of rules for requests and responses. That's a broad term covering library functions, SOAP services, GraphQL, gRPC, and more. A REST API is one specific kind of web API that follows REST's constraints over HTTP.
So every REST API is an API, but not every API is REST. When someone says 'the API', in a web context they usually mean a REST API, but naming REST as one style among several (alongside GraphQL and gRPC) shows you understand the wider category.
Key point: Frame REST as one style of API, not a synonym for it. That precision is exactly what this seemingly simple question is checking.
An endpoint is a specific URL where a resource or action lives, combined with the HTTP method you use on it. GET /users/42 and DELETE /users/42 hit the same URL but are two different endpoints because the method differs. Each endpoint does one thing: read a user, delete a user, list orders.
The base URL plus the path plus the method together define an endpoint. When designing an API you're really deciding your set of endpoints: which resources exist, which methods each supports, and what they return. Clear, consistent endpoints are what make an API easy to consume.
Key point: Note that method + URL together make an endpoint, so one URL can be several endpoints. That detail shows you think in terms of method plus path.
For candidates with working experience: statelessness, versioning, pagination, caching, auth, and the design judgment that separates endpoint writers from API designers.
Statelessness means the server keeps no client session between requests. Every request carries all the context the server needs, credentials, IDs, parameters, so the server doesn't remember anything about you from the last call. There's no server-side session tied to a specific client.
It matters for scaling and reliability: because no request depends on server memory, any instance behind a load balancer can handle any request, and you can add or remove servers freely. The trade-off is the client resends context each time (a token on every request), which is a deliberate cost for horizontal scaling.
Key point: statelessness maps to horizontal scaling: any server can handle any request. That connection is the production insight the key signal is here.
Watch a deeper explanation
Video: RESTful APIs in 100 Seconds // Build an API from Scratch with Node.js Express (Fireship, YouTube)
Use PUT when you're replacing the whole resource: the client sends the complete new representation, and the server overwrites everything. Use PATCH when you're changing only some fields and want to leave the rest as they are. Sending a partial body with PUT is a classic bug, because a strict PUT treats missing fields as 'clear them'.
The gotcha is idempotency: PUT is idempotent (replacing twice with the same body is the same result), but PATCH may not be, depending on the operation (an 'increment' PATCH isn't). Also, PATCH has more than one format (JSON Merge Patch vs JSON Patch), so agreeing on the format matters.
PUT /users/42 # send the FULL object, replaces everything
{ "name": "Ada", "email": "ada@x.com", "active": true }
PATCH /users/42 # send ONLY what changes
{ "email": "new@x.com" }Key point: Warn that a partial PUT can wipe unset fields. Naming that failure mode shows you've been bitten by it in real code.
The three common approaches are URL versioning (/v1/users), header versioning (a custom Accept or version header), and query-param versioning (?version=1). URL versioning is the most visible and simplest to route and cache, which is why most public APIs use it despite purists preferring header-based.
Whatever you choose, the point is the same: let existing clients keep working when you make a breaking change by shipping the change under a new version. The real skill is minimizing breaking changes in the first place, adding fields is backwards-compatible, removing or renaming them isn't, so many changes never need a new version.
| Approach | Example | Trade-off |
|---|---|---|
| URL path | /v1/users | Simple, visible, cache-friendly; purists dislike it |
| Header | Accept: application/vnd.api.v1+json | Cleaner URLs; harder to test and cache |
| Query param | /users?version=1 | Easy to add; clutters every request |
Key point: Say that the best versioning is avoiding breaking changes at all. Framing versioning as a last resort reads more senior than picking a favorite scheme.
The two main strategies are offset-based (?page=2&limit=20 or ?offset=40&limit=20) and cursor-based (?limit=20&cursor=abc123). Offset is simple and lets users jump to any page, but it gets slow on huge tables and can skip or repeat rows when data changes between requests.
Cursor-based pagination passes a pointer to the last item seen and asks for the next batch. It's stable under inserts and deletes and stays fast at scale, which is why large APIs prefer it, but you can't jump to an arbitrary page. Pick offset for small admin lists, cursor for large or fast-changing feeds.
# offset-based
GET /users?page=2&limit=20
# cursor-based (stable for large, changing data)
GET /users?limit=20&cursor=eyJpZCI6MTAwfQ==Key point: Explaining why cursor pagination beats offset at scale (stability and speed) is the intermediate signal. Just saying 'use page and limit' stays junior.
REST leans on HTTP caching. GET responses can be cached, and headers control it: Cache-Control sets how long and where a response can be stored, ETag gives a response a version tag, and Last-Modified gives a timestamp. On the next request the client sends If-None-Match with the ETag, and the server answers 304 Not Modified if nothing changed, saving bandwidth.
This is why REST's safe, cacheable GETs are an advantage: a CDN or browser can serve repeat reads without hitting your server. The care needed is invalidation, setting the right freshness and busting caches when data changes, so clients don't serve stale results.
# first response
HTTP/1.1 200 OK
ETag: "abc123"
Cache-Control: max-age=60
# client revalidates
GET /users/42
If-None-Match: "abc123"
# nothing changed
HTTP/1.1 304 Not ModifiedKey point: Naming ETag plus If-None-Match plus the 304 flow proves you understand HTTP caching, not just that 'GETs can be cached'.
Because REST is stateless, credentials travel on each request rather than in a server session. Common options: API keys for server-to-server or simple cases, bearer tokens (often JWTs) sent in the Authorization header for user auth, and OAuth 2.0 when a third party needs delegated access without the user's password.
A JWT carries claims (who the user is, what they can do) signed by the server, so any instance can verify it without a lookup, which fits statelessness well. The trade-offs to know: JWTs are hard to revoke before they expire, so keep them short-lived and pair them with refresh tokens.
Key point: Mention that JWTs are hard to revoke early, so keep them short-lived. That caveat separates people who've run token auth from people who've only added a header.
Authentication is verifying who you are: checking a token or credentials to establish identity. Authorization is deciding what you're allowed to do once identified: whether this user can read, edit, or delete a given resource. Auth-n comes first, then auth-z.
In status-code terms this maps directly: a failed authentication is 401 (we don't know who you are), and a failed authorization is 403 (we know you, but you can't do this). Keeping the two ideas separate is what lets you design permissions cleanly.
Key point: Link the two to 401 and 403 respectively. Interviewers love when the conceptual answer and the status-code answer line up in one breath.
Rate limiting caps how many requests a client can make in a window (say 100 per minute) to protect the API from abuse, runaway clients, and cost spikes. When a client exceeds the limit, the server returns 429 Too Many Requests instead of processing the call.
Good APIs signal the limits so clients can adapt: headers like X-RateLimit-Limit, X-RateLimit-Remaining, and X-RateLimit-Reset tell the client its budget, and a Retry-After header on a 429 says how long to wait. Common algorithms are token bucket and sliding window; naming one shows depth.
HTTP/1.1 429 Too Many Requests
Retry-After: 30
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1720000000Key point: Pair 429 with Retry-After and the rate-limit headers. Knowing a client should read those and back off is the practical detail that lands here.
CORS (Cross-Origin Resource Sharing) is the mechanism that lets a browser call an API on a different origin (domain, protocol, or port) than the page it's running on. By default the same-origin policy blocks those calls; the server opts in by sending Access-Control-Allow-Origin and related headers.
For non-simple requests the browser first sends an OPTIONS preflight asking whether the real request is allowed, and only proceeds if the server approves. The key point interviewers check: CORS is a browser security feature enforced by the browser, not by your server-to-server calls, and it's the server's response headers that grant access.
Key point: Clarify that CORS is enforced by the browser, not the server, and only affects browser clients. That distinction is the exact thing this question tests.
Return the right status code and a consistent, structured body so clients can handle failures programmatically. A good error response has a machine-readable code, a human-readable message, and often a field pointing at what was wrong, all in the same shape every time so clients don't parse each error differently.
The anti-patterns to avoid: returning 200 with an error inside, leaking stack traces or internal details to the client, and using a different error shape per endpoint. Many teams adopt the Problem Details format (RFC 9457) for a standard JSON error structure.
{
"error": {
"code": "validation_failed",
"message": "Email is not valid",
"field": "email"
}
}How a good API handles a bad request
The client-facing error must be actionable; the internal log holds the detail. Never leak stack traces to the client.
Key point: Push a consistent error shape across every endpoint. Interviewers care that clients can handle errors uniformly, not that each route is clever.
Content negotiation is how a client and server agree on the format of a response. The client sends an Accept header saying what it wants (application/json, application/xml), and the server responds in the best format it supports, setting Content-Type to what it actually sent.
It lets one endpoint serve multiple formats, or multiple languages via Accept-Language, without separate URLs. In practice most JSON APIs only speak JSON, so negotiation is light, but knowing the Accept/Content-Type handshake explains how the same resource can be represented differently.
Key point: Frame it as the Accept (what I want) vs Content-Type (what you got) handshake. That framing answers the question cleanly in one line.
HATEOAS (Hypermedia As The Engine Of Application State) is the REST constraint where responses include links to the actions a client can take next, so the client discovers what's possible by following links instead of hardcoding URLs. A user response might include links to update it, delete it, or fetch its orders.
It's the least-followed REST constraint. Most APIs called RESTful skip it because it adds complexity and few clients actually use the links. Being honest that HATEOAS is part of 'true REST' but rare in practice is a better answer than pretending every API does it.
{
"id": 42,
"name": "Ada",
"_links": {
"self": { "href": "/users/42" },
"orders": { "href": "/users/42/orders" }
}
}Key point: Say plainly that HATEOAS is 'true REST' but rarely implemented. That honesty works better than claiming every API is fully RESTful.
Over-fetching is when an endpoint returns more data than the client needs, wasting bandwidth: you call /users/42 and get fifty fields to show a name. Under-fetching is the opposite, when one call isn't enough and the client must make several round trips to assemble what it needs, like fetching a user, then their orders, then each order's items.
These are the classic REST pain points, and they're the main reason GraphQL exists, since it lets the client request exactly the fields and relations it wants in one query. In REST you soften them with sparse fieldsets (?fields=name,email), embedding related resources, and well-designed endpoints.
Key point: Naming over- and under-fetching as REST's weak spots, and how GraphQL targets them, sets you up perfectly for the inevitable REST-vs-GraphQL follow-up.
REST fits public APIs, simple CRUD, and cases where HTTP caching matters, since each resource has its own cacheable URL and any HTTP client can call it. It shines when the data needs are predictable and you want the operational simplicity of standard methods and status codes.
GraphQL fits clients with varied, nested data needs, mobile apps that want to avoid over-fetching, and product teams iterating fast on the shape of data, because the client asks for exactly the fields it wants in one request. The costs: caching is harder, and you must guard against expensive deep queries. Pick REST for simplicity and caching, GraphQL for flexible client-driven queries.
Key point: Answer with trade-offs, not tribalism. 'REST for caching and simplicity, GraphQL for flexible queries' shows you can pick a tool for the job.
Watch a deeper explanation
Video: APIs for Beginners - How to use an API (Full Course / Tutorial) (freeCodeCamp.org, YouTube)
It's a way to grade how RESTful an API is, in four levels. Level 0 is a single endpoint doing everything (RPC over HTTP). Level 1 introduces separate resources with their own URLs. Level 2 uses HTTP methods and status codes correctly, which is where most real REST APIs sit. Level 3 adds HATEOAS, hypermedia links driving the client.
The model is useful as a checklist and a shared vocabulary: when someone says an API is 'Level 2', you know it uses proper resources, methods, and codes but not hypermedia. It's not a strict standard to chase to Level 3, but it frames what 'more RESTful' actually means.
Key point: Knowing most production APIs sit at Level 2, and that Level 3 is HATEOAS, shows you understand REST maturity as a spectrum rather than pass/fail.
Put all of it in query parameters on the collection endpoint, since these shape how you read a list without changing the resource itself. Filtering uses field params (/products?category=books&in_stock=true), sorting uses a sort param with a direction (/products?sort=-price for descending), and search uses a query param (/products?q=laptop). Keep the names consistent across endpoints so clients learn the pattern once.
The design calls to make: decide which fields are filterable to avoid unbounded queries, define how you express ranges (price_min, price_max) and multiple values, and combine filtering with pagination cleanly. Documenting the allowed params matters, because an open-ended filter that hits any column is both a performance and a security risk.
GET /products?category=books&in_stock=true&sort=-price&q=react&page=2&limit=20
# filter (category, in_stock), sort (-price = desc), search (q), paginateKey point: Keeping filtering, sorting, and search in query params with consistent names is the answer. Inventing a new scheme per endpoint is the anti-pattern to avoid.
advanced rounds probe design, security, reliability, and production scars. Expect every answer here to draw a follow-up about trade-offs and failure modes.
Clarify first: who are the clients, what's the scale, is auth needed, and are there compliance limits. Then model resources as nouns: /posts, /posts/{id}, /posts/{id}/comments, /users/{id}, /tags. Map methods onto CRUD: GET to list and read, POST to create, PATCH to edit, DELETE to remove, each returning the right status code (201 with a Location on create, 204 on delete).
Then layer the cross-cutting concerns: cursor pagination and filtering on the collections (/posts?tag=rest&limit=20), versioning (/v1/), auth via bearer tokens with role-based checks (401 vs 403), rate limiting with 429, consistent structured errors, and caching headers on reads. The structure that scores is clarify, model resources, map methods and codes, then pagination, auth, versioning, errors, each justified by the requirements you asked for.
Key point: Open by asking clarifying questions before designing. Jumping straight to endpoints without requirements is the most common way to underperform on a design prompt.
Watch a deeper explanation
Video: What is REST API? | Web Service (Telusko, YouTube)
POST isn't idempotent by default, so a client that retries after a timeout can create a duplicate (charge a card twice, place two orders). The fix is an idempotency key: the client generates a unique key and sends it in a header on the request, and the server records the result for that key.
If the same key arrives again, the server returns the stored result instead of doing the work a second time, so the retry is safe. You store keys with a TTL and scope them per operation. Payment APIs like Stripe popularized this exact pattern, and naming it signals you've built reliable write paths.
POST /payments
Idempotency-Key: 8f1a-4c02-...
Content-Type: application/json
{ "amount": 5000, "currency": "usd" }
# a retry with the same key returns the SAME result, no double chargeKey point: Naming the idempotency-key pattern (and that Stripe uses it) is The production-ready answer. 'Just retry the POST' fails this question hard.
the basics that catch most issues: HTTPS everywhere so tokens and data aren't sent in the clear, authentication on every non-public endpoint, and authorization checks on each resource so a user can't read or edit another's data (broken object-level authorization is a top API vulnerability) comes first. Validate and sanitize all input to block injection.
Then add depth: rate limiting to stop abuse, least-privilege scopes on tokens, no sensitive data in URLs (they get logged), strict CORS, and security headers. Return generic errors that don't leak internals, and log with a correlation ID. The OWASP API Security Top 10 is the checklist to name, with broken authorization and excessive data exposure being the most common real failures.
Key point: Naming OWASP API Top 10 and broken object-level authorization specifically shows current API-security literacy, which is exactly what advanced rounds probe.
PUT should replace a resource so that applying the same body twice lands on the same state. It breaks when the server does something non-deterministic on each call: setting updated_at to now, auto-incrementing a version on every write, or appending to a list instead of replacing it. Now two identical PUTs produce different results.
It also breaks with race conditions: two clients PUT different bodies and the last write wins silently, losing an update. The fixes are conditional requests with If-Match and an ETag (reject the write if the resource changed since the client read it) and keeping side effects deterministic. Optimistic concurrency is the pattern to name.
GET /users/42 -> ETag: "v7"
PUT /users/42
If-Match: "v7" # only apply if it's still version 7
{ ... }
# if it changed: 412 Precondition FailedKey point: Bringing up If-Match/ETag optimistic concurrency for lost updates is the senior depth here. Interviewers push on 'what if two clients write at once'.
Don't block the request until the work finishes. Accept the request and return 202 Accepted immediately with a URL to a status resource, then do the work in the background. The client polls that status URL (or you notify it via a webhook) until it reports done, at which point the status resource links to the finished result.
This keeps the API responsive, avoids client timeouts on slow work, and lets you retry the job independently. The pattern is: POST to start, 202 plus a Location to /jobs/{id}, GET /jobs/{id} returns pending or completed, and the completed response points at the result. Webhooks replace polling when the client can receive callbacks.
POST /reports
HTTP/1.1 202 Accepted
Location: /jobs/8123
GET /jobs/8123
{ "status": "pending" }
# later
{ "status": "done", "result": "/reports/551" }Key point: Reaching for 202 plus a status resource (not a blocking request) is the pattern the question needs. Blocking the connection on slow work is the wrong instinct.
REST's one-resource-per-request model is awkward for bulk work, so you extend it deliberately. Common approaches: accept an array in a POST to a batch endpoint (POST /users/batch with a list), or expose an action endpoint (POST /orders/bulk-cancel). Return a per-item result so the client knows which succeeded and which failed, since a batch can partially succeed.
The design decisions are: is the batch all-or-nothing (transactional) or best-effort per item, and what status code fits a partial success (often 207 Multi-Status, or 200 with a per-item results array). Being explicit about partial failure is what separates a usable bulk API from one that hides which items failed.
Key point: Raising partial-failure handling (which items succeeded) is the senior angle. A bulk endpoint that returns one blanket status is a design smell to call out.
Caching cuts latency and load: a CDN or client serves repeat GETs without touching your origin, and conditional requests with ETags save bandwidth even on revalidation. For read-heavy APIs this is one of REST's biggest strengths, since safe GETs are cacheable by design.
The cost is staleness and invalidation. Longer max-age means faster responses but a bigger window where clients see old data, and busting caches across a CDN when data changes is genuinely hard. You tune per resource: long TTLs for rarely-changing reference data, short or no caching for volatile or user-specific data, and ETags where you want freshness checks without full re-downloads.
Key point: Framing it as a latency-vs-staleness trade tuned per resource, plus 'invalidation is the hard part', is the mature answer. Blanket 'cache everything' fails.
Polling means the client repeatedly calls an endpoint asking 'has anything changed', which wastes requests when nothing has and adds latency when something has. It's simple and works everywhere because the client drives it, but it scales badly under many clients and always lags behind real events by the poll interval.
Webhooks flip it: the server calls a client-provided URL when an event happens, so the client learns immediately and makes no wasted calls. Use webhooks for event-driven needs (a payment cleared, a job finished) where the client can receive HTTP. The costs to name: you must secure webhooks (signature verification), handle retries and duplicates (so consumers must be idempotent), and give clients a way to catch up if they miss one.
Key point: webhook consumers must be idempotent and verify signatures.
For it: hypermedia decouples clients from hardcoded URLs, so the server can move or rename endpoints and evolve the API without breaking clients that just follow links. It makes the API self-describing and discoverable, which is the original vision of REST.
Against it: almost no clients actually consume the links; they read the docs and hardcode paths anyway, so the extra payload and server complexity buy little. That's why the vast majority of APIs stop at Richardson Level 2. The honest senior take is that HATEOAS is theoretically nice but rarely pays off, and you'd only invest in it for a long-lived public API with many independent clients.
Key point: Arguing both sides and landing on 'rarely worth it in practice' shows real judgment. Dogmatically insisting on full HATEOAS indicates academic, not experienced.
The N+1 problem is when a client fetches a list (one request) and then makes one request per item to get related data (N more), turning a screen into dozens of round trips. In REST it comes from strictly separating resources without a way to fetch related data together.
Fixes: support embedding or expansion (GET /posts?expand=author returns each post with its author inline), offer sparse fieldsets to keep the expanded payload lean, or add a purpose-built endpoint that returns the composed view a screen needs. On the server side, batch the underlying database queries so one API call doesn't fan out into N queries either. If clients constantly compose data, that's often the signal that GraphQL fits better.
Key point: Offering resource expansion and batched DB queries, and noting N+1 pressure is a sign GraphQL might fit, is the well-rounded production-ready answer.
Track the signals that reflect user experience: request rate, error rate (especially the 5xx and 4xx split), and latency percentiles (p50, p95, p99, not just the average, because tail latency is what users feel). Break these down per endpoint so a slow route doesn't hide inside an overall-healthy average.
Add distributed tracing with a correlation ID propagated across services so you can follow one request end to end, structured logs tied to that ID, and alerts on symptoms like a rising error rate or p99 latency rather than raw CPU. Health and readiness endpoints let orchestrators route traffic only to instances that can serve. The goal is to detect and localize a problem fast, not just know 'the API is down'.
Key point: Pushing p95/p99 latency over averages, plus a correlation ID across services, signals you've run APIs in production rather than only shipped them.
Favor additive, backwards-compatible changes: adding a new optional field, a new endpoint, or a new optional parameter doesn't break clients that ignore it. What breaks clients is removing or renaming fields, changing a field's type, making an optional field required, or changing status-code behavior, so treat those as breaking and defer them.
When a breaking change is unavoidable, ship it under a new version and run the old version in parallel with a deprecation window: announce it, add a Deprecation or Sunset header, monitor who's still calling the old version, and give clients time to migrate. The discipline of 'additive by default, versioned only when forced' is what keeps integrations alive.
Key point: Listing exactly which changes are breaking vs safe, and the deprecation-window process, is the answer. Vague 'just make a v2' skips the real skill.
gRPC uses HTTP/2 and binary Protocol Buffers, giving low latency, small payloads, streaming, and a strongly-typed contract generated into client and server code. That makes it a strong fit for internal service-to-service communication in a microservices system where performance and a shared schema matter.
REST wins for public-facing APIs, browser clients (gRPC needs a proxy like grpc-web to reach browsers), and anywhere human-readable JSON and standard HTTP tooling help. The typical split: gRPC between your backend services, REST at the edge that external clients and browsers hit. Naming that boundary is the mature answer.
| REST | gRPC | |
|---|---|---|
| Format | JSON (text) | Protocol Buffers (binary) |
| Best fit | Public APIs, browsers | Internal service-to-service |
| Streaming | Limited | Built-in (bidirectional) |
Key point: Landing on 'gRPC inside, REST at the edge' shows you place tools by boundary. Declaring one universally better is the answer to avoid.
The failure to prevent is the lost update: two clients read a resource, both edit it, and the second write silently overwrites the first. The standard fix is optimistic concurrency control: give each resource a version (an ETag or version field) and require the client to send it back with the update. If the stored version differs, the resource changed since they read it, so you reject the write.
In status-code terms, a stale version means you return 409 Conflict or 412 Precondition Failed. This is optimistic because it assumes conflicts are rare and only checks at write time, which scales better than pessimistic locking that holds a lock across the whole edit. Pessimistic locking fits when conflicts are frequent and expensive to redo, but it hurts throughput. For most REST APIs, ETag-based optimistic concurrency is the right default.
PATCH /docs/9
If-Match: "rev-12" # I read revision 12
{ "title": "Updated" }
# someone else already wrote rev-13:
HTTP/1.1 412 Precondition FailedKey point: Naming optimistic concurrency with ETags and 412/409, and contrasting it with pessimistic locking, is exactly the depth this question is screening for.
REST defines six constraints: client-server separation, statelessness, cacheability, a uniform interface, a layered system, and optional code-on-demand. Most well-built APIs honor client-server, statelessness, cacheability, and layered systems, since those four are what give REST the horizontal scaling and operational simplicity it's known for in production.
The uniform interface is partly followed (proper methods, status codes, resource URLs) but its HATEOAS part is usually skipped, and code-on-demand (sending executable code to the client) is rare. So in practice a typical 'RESTful' API satisfies most constraints but not hypermedia. Knowing which are load-bearing (statelessness, cacheability) versus commonly dropped (HATEOAS) is the depth here.
Key point: Listing all six but flagging HATEOAS and code-on-demand as the commonly-skipped ones shows textbook knowledge grounded in what production APIs actually do.
A hard delete removes the row for good, which is clean but loses history and can break references from other records. When data needs to be recoverable, auditable, or referenced after removal, you soft-delete instead: mark the resource as deleted (a deleted_at timestamp or a status flag) and filter it out of normal reads, so DELETE /users/42 hides the user rather than erasing it.
The design decisions are: does DELETE do a soft or hard delete by default, how do you expose recovery (a restore action, or an admin view of deleted items), and how do you eventually purge for real, since regulations like a right-to-erasure request may require a genuine hard delete. Being explicit about which semantics your DELETE has, and why, is what this question checks.
Key point: Raising soft delete plus a real-purge path for erasure requests shows you've dealt with data lifecycle, not just wired up a DELETE that drops a row.
REST isn't the only way to build an API, and interviewers like to hear where each option fits. REST uses many resource URLs and standard HTTP methods, so it's simple, cacheable, and works with any HTTP client. GraphQL exposes one endpoint and lets the client ask for exactly the fields it wants, which cuts over-fetching but moves caching and complexity onto you. gRPC uses HTTP/2 and binary Protocol Buffers for fast service-to-service calls, at the cost of browser friendliness. SOAP is the older XML-based standard with strict contracts, still seen in enterprise and finance. Naming the trade-off out loud is itself an interview signal.
| Style | Transport / format | Best at | Watch out for |
|---|---|---|---|
| REST | HTTP + JSON, many URLs | Public APIs, caching, wide client support | Over-fetching, multiple round trips for related data |
| GraphQL | HTTP + one endpoint, typed schema | Client-driven queries, avoiding over-fetch | Caching is harder; query cost and depth need limits |
| gRPC | HTTP/2 + Protocol Buffers | Fast internal service-to-service calls | Not native to browsers; needs a proxy for the web |
| SOAP | XML over HTTP (or other transports) | Strict contracts, formal enterprise standards | Verbose, heavier, and mostly legacy now |
Prepare in layers, and design choices out loud is the explanation path. Most REST rounds move from definition questions to a hands-on design or debugging task to a trade-off discussion, so rehearse each stage rather than only reading answers.
The typical REST API interview flow
Earlier rounds increasingly run as AI-driven technical interviews. The concepts on this page are what gets probed at every stage.
10 questions, about 6 minutes. Score 70% or higher to earn a shareable certificate.
Hyring builds the AI Coding Interviewer that runs live technical rounds in 20+ languages for 5,000+ hiring teams. These REST API questions and scoring notes reflect what actually gets asked and evaluated inside the interviews we host.
See how the AI Coding Interviewer works