
Senior REST API developer interview questions rarely test whether you can name the HTTP verbs or explain what a status code is. At senior level, the questions shift to design judgment: how you version an API without breaking every existing client, what actually makes a request safe to retry, how you structure an error response so a caller can act on it programmatically, and when REST is the wrong choice entirely. The questions below focus on that design-judgment layer, with a worked answer for each.
A mid-level candidate can build CRUD endpoints, return the right status codes, and explain what idempotent means in the dictionary sense. A senior candidate is expected to make real trade-off calls, URI versioning versus header versioning, cursor pagination versus offset, when to add HATEOAS versus when it is not worth the complexity, and to know which parts of "REST best practice" are settled convention versus still-evolving standards.
The scenario: a client sends a payment or order-creation request, the server processes it successfully, but the network drops before the response reaches the client. The client, seeing no response, retries. Without protection, that retry creates a second charge or a second order.
This is where idempotency actually matters in production, not as a dictionary definition. PUT and DELETE are idempotent by the HTTP spec: calling them multiple times with the same input produces the same end state as calling them once. POST is not, by definition, each call is generally treated as creating a new resource or triggering a new action. PATCH's idempotency depends entirely on how it is implemented, a PATCH that sets a field to an absolute value is idempotent, one that increments a counter is not. Knowing this classification is table stakes. The senior-level answer is what you do about the POST case specifically: the established pattern, popularized by Stripe, is an idempotency key: the client generates a unique key per logical operation and sends it in a header, the server stores the key alongside the result of the first request once processing begins, and subsequent retries with the same key return that stored result instead of executing the operation again. Depending on the implementation, this can include failed responses as well as successful ones, which prevents a retry from accidentally performing the side effect twice.
POST /v1/charges HTTP/1.1Host: api.example.comIdempotency-Key: 8f14e45f-ceea-4d7a-9c3f-2a4b1e3d5c6aContent-Type: application/json{ "amount": 2000, "currency": "usd", "customer": "cus_9s6XKzkNRiz8i3" }
Idempotency-Key, the server recognizes the key, skips reprocessing, and returns the original response instead of creating a second charge. This pattern has also been discussed by the IETF through draft-ietf-httpapi-idempotency-key-header, which proposed a standardized Idempotency-Key header field. As of this guide's publication, that Internet-Draft has expired without becoming a finalized RFC, so candidates should treat idempotency keys as an established industry pattern rather than a standardized HTTP header defined by a final RFC.There is no single correct answer here, which is exactly why it is a senior-level question. URI versioning (/v1/users, /v2/users) remains a common pragmatic choice for public APIs because it is simple to route, easy for a client to pin, and visible directly in the URL. Twilio is one example of an API that includes version information in its request paths, while GitHub takes a different approach and versions its REST API through the X-GitHub-Api-Version request header. Header or media-type versioning (an Accept header naming a version) keeps URLs stable but is harder for a client to discover and test from a browser, and adds a routing layer that has to inspect headers rather than the path. Stripe uses date-based API versions, with accounts pinned to a particular API version unless a request explicitly overrides it. Its current versioning model also groups versions into named major releases, allowing backwards-compatible changes within a release while reserving breaking changes for new major releases.
A fourth option, increasingly discussed for internal APIs, is not versioning at all: designing every change to be strictly additive (new optional fields, new endpoints) so existing clients never break, and reserving versioning for genuinely breaking changes. The senior-level answer names the trade-off explicitly: URI versioning is simplest to reason about but forces a visible major-version jump for every breaking change, header versioning is more flexible but less discoverable, and an additive-only strategy minimizes version churn but requires real discipline about what counts as a breaking change.
HATEOAS, hypermedia as the engine of application state, is the top level of the Richardson Maturity Model: a response includes links describing what the client can do next, so the client does not need to hardcode URL structures. It is level 3 of 3, sitting above using proper HTTP verbs (level 2) and having distinct resource URIs at all (level 1). Being able to define it is the mid-level version of this answer.
The senior-level answer is naming the actual cost that makes teams skip it: adding hypermedia links generally increases payload size and client complexity without a proportional benefit for most internal or mobile-first APIs, where the client and server are usually deployed and versioned together anyway, so the coupling HATEOAS is designed to avoid was never really a problem for them. HATEOAS is asked about specifically to check whether a candidate understands REST's original design intent, and a strong answer states both halves: what it actually is and why most real systems do not implement it, rather than either dismissing it as irrelevant trivia or treating it as something every REST API should have.
Returning a bare string message or an inconsistent shape per endpoint is the most common mistake here. The current standard is RFC 9457, "Problem Details for HTTP APIs," which supersedes RFC 7807. It defines a common set of members that problem responses can use: type identifies the problem type, title gives a short human-readable summary, status can repeat the HTTP status code, detail describes this particular occurrence, and instance can identify the specific problem occurrence. These members are not all mandatory; for example, omitting type gives it the default value about:blank.
{"type": "https://api.example.com/errors/insufficient-funds","title": "Insufficient Funds","status": 400,"detail": "Account balance of 30 is not sufficient to cover the charge of 50.","instance": "/v1/charges/ch_1a2b3c","balance": 30,"requested": 50}
application/problem+json media type so clients know that the body follows the Problem Details format. The HTTP status code still carries the response's HTTP semantics, while the problem document provides structured, machine-readable details about the error. Note the extra balance and requested fields, RFC 9457 explicitly allows extension members beyond the core five, which is what makes this format useful for a specific error rather than just a generic wrapper.The senior-level point is not memorizing the field names, it is explaining why a structured, machine-parseable error format matters: a client integrating against the API can branch on type programmatically instead of pattern-matching a message string that might change wording between releases.
The mechanism most commonly discussed is the token bucket: each client has a bucket that refills at a fixed rate, and a request is allowed if a token is available, rejected otherwise. A sliding window log or counter is the alternative, trading a small amount of memory overhead for smoother enforcement at the boundary between windows, since a fixed window can allow a burst of nearly double the limit right at the window edge.
On the wire, a rate-limited request returns 429 Too Many Requests with a Retry-After header telling the client how long to wait, a stable, long-established convention:
HTTP/1.1 429 Too Many RequestsRetry-After: 30RateLimit-Policy: "default";q=100;w=60RateLimit: "default";r=0;t=30Content-Type: application/problem+json{ "type": "https://api.example.com/errors/rate-limited", "title": "Too Many Requests", "status": 429 }
RateLimit and RateLimit-Policy headers above come from the active IETF draft-ietf-httpapi-ratelimit-headers, which is working toward standardized rate-limit metadata that clients can use to understand quota policy and remaining capacity. It is still an Internet-Draft rather than a finalized RFC as of this guide's publication. This differs from the proposed Idempotency-Key specification discussed earlier, whose Internet-Draft has expired.For a public or third-party-facing API, OAuth 2.0 is the framework most commonly reached for, and the senior-level distinction is knowing which grant type fits which client. The authorization code flow (with PKCE for public clients like mobile or single-page apps) is for a user delegating access to a third-party application. The client credentials flow is for service-to-service access with no user in the loop. Naming the resource owner password credentials grant as a good modern choice is a red flag. It requires the client to handle the user's credentials directly, and current OAuth security guidance says the grant must not be used. Existing legacy systems may still contain it, but it should not be selected when designing a new OAuth integration.
The second half of this question is how token state is validated. A self-contained token such as a signed JWT can often be validated locally by the resource server without contacting the authorization server on every request, which reduces lookup overhead but makes immediate revocation harder unless additional server-side state or checks are introduced. Opaque tokens are commonly validated against centralized token state or through token introspection, which makes revocation easier to enforce but can introduce lookup overhead, although those results may also be cached. A senior answer states this trade-off rather than treating JWT as a strictly superior default, and separately addresses refresh token rotation: issuing a new refresh token on every use and invalidating the previous one, so reuse of an older token can signal theft and trigger revocation of the token family.
Offset-based pagination (?page=3&limit=20 or ?offset=40&limit=20) is simple but breaks under concurrent writes, an item inserted before the current offset shifts every subsequent page, producing duplicates or skipped items depending on the direction of the shift. Cursor-based pagination can avoid this offset-shift problem when the cursor is based on a stable, deterministic ordering. Instead of paging from a numeric position that moves as rows are inserted or removed, the next request continues relative to a specific item or ordering value, at the cost of not naturally supporting a direct jump to an arbitrary page number.
A cursor-based response typically returns the cursor for the next page alongside the data, rather than expecting the client to compute it:
{"data": [{ "id": "post_881" }, { "id": "post_880" }],"next_cursor": "eyJpZCI6InBvc3RfODgwIn0="}
Link header defined in RFC 8288 lets a response advertise rel="next" and rel="prev" URLs directly:Link: <https://api.example.com/posts?cursor=eyJpZCI6InBvc3RfODgwIn0%3D>; rel="next"
Current guidance across API design teams has converged on a hybrid-by-default answer rather than declaring one winner. REST remains the pragmatic default for public and partner-facing APIs, its tooling, caching behavior, and the fact that nearly every developer already understands HTTP semantics make it the lowest-friction choice for external consumers who do not control both ends of the integration. GraphQL earns its complexity as an aggregation layer in front of multiple services when a frontend genuinely needs to shape its own queries and avoid over-fetching or under-fetching across many resource types. gRPC is often a strong choice for internal service-to-service calls where both ends are controlled, thanks to schema-first Protobuf contracts, generated clients, compact binary serialization, and streaming support. Native gRPC is not directly exposed through standard browser networking APIs in the same way as it is to backend clients, although browser applications can communicate with gRPC services through gRPC-Web or similar gateways.
The trap in this question is presenting it as one-size-fits-all. A senior answer names the actual constraint each format solves and explains why a system frequently runs all three at different layers rather than standardizing on one.
Presenting HATEOAS as something every REST API should implement, without weighing the actual payload and complexity cost, is a common miss. Citing either proposal as a finalized RFC is a precision mistake: the RateLimit headers are still being developed through an active IETF Internet-Draft, while the proposed Idempotency-Key Internet-Draft has expired without becoming a final RFC. Treating JWT as strictly better than an opaque token without naming the revocation trade-off, and recommending the OAuth resource owner password grant for a normal third-party integration, are both red flags at this level.
Is RFC 7807 still the correct RFC to cite for API error format? RFC 7807 has been superseded by RFC 9457, which carries the same core field structure. Citing the newer number is the more current, precise answer, though the content is materially unchanged.
Do I need to implement idempotency keys for every POST endpoint? No. It matters most for operations with a real cost of duplication, payments, order creation, anything triggering a side effect that should not happen twice. A read-only or freely repeatable POST does not need one.
Is GraphQL replacing REST for public APIs? No, and current guidance treats this as a settled non-question. REST remains the dominant choice for public, partner-facing APIs specifically because of its caching and tooling ecosystem; GraphQL is more commonly an internal aggregation layer than a public API replacement.
How current does my knowledge of these IETF efforts need to be for an interview? Knowing their current status matters more than memorizing draft revision numbers: the standardized RateLimit headers are still being developed through an active IETF Internet-Draft, while the proposed Idempotency-Key draft has expired without becoming a final RFC. The idempotency-key pattern itself remains widely used in production APIs.
Work through the mechanics above with a real scenario attached: given a public API serving both a mobile app and third-party integrators, how would you version it, and what would change if it only served an internal team. Practicing the design-trade-off questions, versioning, pagination, rate limiting, by actually writing out the response shapes is worth more than reading about the concepts, since a senior interviewer is more likely to hand you a scenario and ask you to design the response than to ask you to define a term.
If you are also preparing GraphQL-specific rounds, GreatFrontEnd's GraphQL interview questions guide covers the equivalent data-layer fundamentals, and the versioning and pagination trade-off reasoning here carries over directly.
Senior REST API developer interview questions test whether you can make real design trade-offs, not whether you can recite HTTP verbs. Idempotency and retry safety, versioning strategy, structured error responses, rate limiting, and the judgment to pick REST, GraphQL, or gRPC for the right layer are the actual differentiators. What separates a senior answer is naming the trade-off explicitly and knowing which parts of current REST practice are settled convention versus still-evolving standards.
Senior CSS developer interview questions and answers: cascade layers, design tokens, layout thrashing, and the modern CSS a mid-level round never covers
TypeScript interview questions for freshers: the basics interviewers actually ask, what is out of scope at this level, and a practical way to prepare for them.