REST API Interview Questions: 10 Answers with HTTP Examples

Quick Overview
Practice ten REST API interview questions with worked HTTP examples on methods, status codes, authentication, pagination, ETags, versioning, and safe retries.
Strong REST API interview answers connect a rule to observable behavior. Explain what the client sends, what the server commits, and what happens when a request is repeated, rejected, or interrupted. A definition alone does not establish that the API handles those cases correctly.
These ten questions use an invented orders service to make the trade-offs concrete. The HTTP blocks are message excerpts, with routine framing headers omitted; they are not live PracHub endpoints or a complete server implementation. Names, identifiers, and outcomes are illustrative. Use the examples to reason about a contract, then test that contract against durable state.
The ten questions at a glance
| Question | Evidence a strong answer includes | Mistake to avoid |
|---|---|---|
| What makes an API RESTful? | Resource boundaries and a consistent interface | Equating REST with JSON |
| How does REST differ from SOAP? | Architectural style versus a messaging framework | Claiming either is always faster |
| What do safe and idempotent mean? | The effect of a repeated request | Requiring identical responses |
| When should you use PUT or PATCH? | A replacement or a defined patch document | Treating every PATCH as retry-safe |
| Which status code fits the outcome? | Creation, validation, conflict, and retry behavior | Returning success for a failed operation |
| How do authentication and authorization differ? | Valid credentials plus an object-level permission check | Trusting a resource ID from the client |
| How should collection pagination work? | Ordering, continuation, limits, and consistency | Promising a snapshot from a cursor alone |
| When is a new API version necessary? | The client behavior a change would break | Assuming every added field is harmless |
| How do validators and caching interact? | Freshness, revalidation, and conditional writes | Using a cache validator as a lock |
| How do you handle a retried POST? | A durable operation identity and recovery policy | Treating a header as duplicate protection |
For a broader practice sequence, see API design questions for backend candidates.
1. What makes an API RESTful?
A concise answer starts with the architectural constraints in Fielding's REST dissertation: separate client and server concerns, make messages understandable without a server-held conversational session, label cache behavior, use a uniform interface, and allow layers. Code sent to execute on a client is an optional constraint.
The uniform interface includes identifying resources, exchanging representations, self-descriptive messages, and hypermedia that offers application transitions. JSON over HTTP is not enough to demonstrate all of that.
For an order resource, a representation might expose a related collection:
{
"id": "ord_1042",
"status": "pending",
"links": {"items": "/orders/ord_1042/items"}
}
Explain what that link means and how the client discovers permitted actions. Do not label a collection of procedural JSON endpoints fully RESTful without discussing the missing constraints.
Stateless communication does not prohibit an orders database, authorization policy, or durable retry record. It concerns the context needed to interpret each request. Practice choosing an API boundary in the REST abstraction-layer design question.
2. How does REST differ from SOAP?
REST describes architectural constraints. SOAP 1.2 defines a messaging framework with an XML-based information model, processing rules, envelopes, and protocol bindings. HTTP can carry SOAP messages; using HTTP does not make SOAP a REST API.
A useful answer compares the actual integration requirements. An existing partner may require SOAP envelopes and its established tooling. A new resource-oriented service may benefit from ordinary HTTP methods, representations, and cache behavior. Neither choice establishes payload size, latency, reliability, or security by itself.
For the invented order service, describe the partner's required contract before choosing an interface. An operation named CreateOrder and a resource endpoint named /orders still need the same business decisions: who can create an order, which fields are valid, and how uncertain outcomes are recovered. Choose with those requirements in view rather than a slogan about one approach being lighter.
3. What is the difference between a safe and an idempotent method?
Under HTTP semantics, RFC 9110 section 9.2, a safe method does not request a state-changing operation. Incidental effects such as access logs can still occur. GET must not become a delete operation because a query parameter says action=delete.
Idempotence concerns the intended effect of repeating an identical request. GET, HEAD, PUT, and DELETE are idempotent by their method definitions; POST and PATCH have no general guarantee. An application can define an idempotent operation using either of the latter methods.
Suppose the first DELETE /orders/ord_1042 removes the order and returns 204. A repeat may return 404. The response changed, but the intended effect remains that the resource is absent. Idempotence does not require identical status codes or response bodies.
Retry decisions also depend on the application's contract and current state. Being idempotent does not make overwriting a newer value desirable. Conditional writes can address that separate problem.
4. When should you use PUT instead of PATCH?
For this fictional profile resource, PUT supplies the complete writable representation defined by the contract:
PUT /profiles/usr_42 HTTP/1.1
Host: api.example.test
Content-Type: application/json
{"display_name":"Mira","email":"mira@example.test"}
PATCH supplies changes in an explicitly supported format, as specified by RFC 5789. Here the API accepts JSON Merge Patch:
PATCH /profiles/usr_42 HTTP/1.1
Host: api.example.test
Content-Type: application/merge-patch+json
{"email":"new@example.test"}
In this example, the patch leaves display_name unchanged. JSON Merge Patch, RFC 7396, gives null a removal meaning for object members; omitting a member is different. The API must decide whether removing a required field is valid and reject an invalid resulting resource.
Document how PUT handles missing writable fields and which fields the server manages. Do not silently merge a replacement as though it were a patch. A PATCH document must be applied atomically, and its retry behavior depends on its operations: setting an email and appending another list entry are different contracts.
A practical follow-up is to review a handler, not just name a method. Try the multi-file HTTP API code-review question.
5. Which HTTP status code should an API return?
Tie the code to the result, using the HTTP status reference rather than error-message wording.
| Outcome | Code | Client implication |
|---|---|---|
| Resource created | 201 | Identify the new resource, commonly with Location |
| Work accepted but unfinished | 202 | Follow the documented status mechanism |
| Success with no response body | 204 | Do not attempt to parse a JSON body |
| Malformed request | 400 | Correct the request |
| Missing or invalid credentials | 401 | Follow the authentication challenge |
| Operation forbidden by policy | 403 | Do not blindly retry the same operation |
| Conflict with current resource state | 409 | Resolve the documented conflict |
| Failed request precondition | 412 | Reconcile with current state before writing |
| Understood content that cannot be processed semantically | 422 | Correct the semantic validation problem |
| Too many requests | 429 | Respect retry guidance and a bounded retry policy |
A fictional successful creation can return:
HTTP/1.1 201 Created
Location: /orders/ord_1042
Content-Type: application/json
{"id":"ord_1042","status":"pending"}
Distinguish creating a pending order from merely accepting a request to create one later. For asynchronous work, define a status resource and terminal outcomes. A timeout also leaves the client uncertain about whether an operation committed; it does not prove failure.
Keep error shapes predictable. Include a stable application error code and enough safe detail for a caller to act, while keeping secrets and internal stack traces out of responses. Extend the exercise with API boundary, idempotency, and authorization test cases.
6. How do authentication and authorization differ?
Authentication establishes the caller's identity or credential validity. Authorization determines whether that caller can perform this operation on this resource. Both matter for every protected request.
For a bearer-token API, RFC 6750 describes sending the token in the authorization header over TLS and returning appropriate challenges. An expired token can produce this excerpt:
HTTP/1.1 401 Unauthorized
WWW-Authenticate: Bearer realm="orders", error="invalid_token"
Cache-Control: no-store
If the token is a JWT, decoding its JSON is not validation. Use a maintained verifier, restrict accepted algorithms, verify the signature against trusted issuer keys, and enforce the expected issuer, audience, token type, and time claims. JWT Best Current Practices, RFC 8725, discusses these validation boundaries. A signed token is not automatically encrypted or immediately revocable; the deployment needs an explicit lifetime and revocation policy.
Then check that the caller can access ord_1042 in the relevant tenant and perform the requested action. A valid token does not authorize every identifier. Enforce authorization when reading a list, reading one object, and changing it. A pagination cursor or hard-to-guess ID does not replace that check.
7. How should offset and cursor pagination work?
Offset pagination is convenient when users need numbered pages, but insertions and deletions can shift later offsets. For a sequential order feed, a keyset cursor can instead remember an ordering boundary.
Consider rows ordered by descending (created_at, id). The ID breaks timestamp ties. A continuation query can use:
SELECT id, created_at
FROM orders
WHERE tenant_id = :tenant
AND (created_at, id) < (:last_time, :last_id)
ORDER BY created_at DESC, id DESC
LIMIT :page_size;
Bound the page size. Bind values and derive the tenant from authorized request context. Check that the index supports the filter and ordering; benchmark it on representative data rather than asserting that any cursor implementation is fast.
A response might carry an opaque continuation token:
{
"data": [{"id":"ord_92"},{"id":"ord_91"}],
"next_cursor":"c_7d3f92"
}
Google's pagination guidance explains token opacity, continuation, and consistency of other query parameters. Bind a cursor to its filter and ordering contract, validate it, and document expiry and the end-of-results signal. Encoding a raw offset with Base64 does not make it a keyset cursor or grant access.
A cursor alone is not a snapshot. Newer rows may fall before the saved boundary, and changing a row's sort key can move it across pages. If the use case requires a fixed collection view, design an explicit snapshot or equivalent consistency mechanism. Work through cursor pagination for a changing dataset.
8. When should you introduce a new API version?
Version a contract when a change would break a supported client's behavior and cannot be introduced compatibly. URL prefixes, headers, and media types are routing choices; none removes the need to manage compatibility.
For the orders example, changing total_cents: 1250 into a decimal string measured in dollars changes both type and meaning. Removing a field or requiring a previously optional input can also break clients. Adding an optional field is often easier, but strict decoders and exhaustive handling of enum values can still matter.
Google's compatibility guidance distinguishes wire, source, and semantic compatibility. Use that distinction to review a proposed change: can an old client still parse the response, call the service, and interpret the result correctly?
State the migration plan, supported period, client communication, and retirement criteria. Run contract fixtures for old and new clients during the transition. A new /v2 route is only one step in that plan.
9. How do ETags, caching, and conditional writes interact?
HTTP caching, RFC 9111, separates storage policy from freshness and validation. For an intentionally public catalog response, a contract might include Cache-Control: public, max-age=60 and ETag: "catalog-v7".
On revalidation, a client can send If-None-Match: "catalog-v7". If the representation remains valid for that request, the server can answer 304 without a representation body. The client reuses its stored body according to the cache rules.
no-cache requires validation before reuse; it does not mean “never store.” no-store directs caches not to store the response. private prevents shared-cache storage but can allow a browser's private cache. Choose an explicit policy for personal data and ensure representation variants are not mixed between callers.
Conditional writes solve a different problem. After reading order version "order-v7", a client might send:
PATCH /orders/ord_1042 HTTP/1.1
Host: api.example.test
If-Match: "order-v7"
Content-Type: application/merge-patch+json
{"note":"Leave at reception"}
If another writer has advanced the version, If-Match fails with 412 rather than replacing the newer state. It uses strong comparison; a weak ETag is unsuitable for this check.
The implementation must combine version checking and mutation atomically, using a transaction or equivalent compare-and-update. Checking first and writing later creates a race. On a conflict, refetch and reconcile; repeatedly sending an unconditional write defeats the protection.
10. How do you prevent duplicate effects when a POST is retried?
A dropped response creates uncertainty: the server may have created the order even though the client saw no success. A retry contract can associate the logical operation with a key:
POST /orders HTTP/1.1
Host: api.example.test
Idempotency-Key: b35c9dcc-3d33-4c57-8de5-932da4633280
Content-Type: application/json
{"sku":"book-7","quantity":1}
The header works only if the server implements a documented policy. One design scopes a key to the authenticated caller and operation, records a canonical request fingerprint, reserves the key atomically, and stores the operation's durable outcome. Reuse with a different payload must be rejected. Concurrent duplicates need an explicit in-progress or replay behavior.
Stripe's idempotency documentation is a concrete provider example with documented behavior for replay, parameter mismatch, and key retention. Its API versions differ; do not assume another service follows the same rules or that every error should be replayed identically.
For the invented service, test a response lost after commit, two simultaneous requests, a changed payload, and a retry after key expiry. If an external dependency has an uncertain result, use its operation identity or reconciliation before initiating another effect. A local key record alone cannot guarantee exactly-once behavior across that boundary.
Continue with request fingerprints, expiration, and concurrent retries, then practice payment-processing idempotency and reconciliation.
Turn a sample answer into a testable contract
Choose one of the examples and write down the starting state, request, permitted caller, expected response, and final durable state. Then change one assumption: repeat the request, expire a credential, change the resource version, delete a row between pages, or lose a downstream response.
A useful answer explains which observations distinguish success, rejection, and uncertainty. For example, a failed precondition should leave the resource unchanged, an unauthorized list should not reveal another tenant's rows, and a protected retry should not create another order within its documented guarantee. Measure those outcomes directly rather than treating a status code as proof of the underlying behavior.
Comments (0)