Skip to content
RED SAGSkills

REST API interview questions and answers

REST API questions test how you design endpoints other teams can rely on: resources, status codes, versioning and safe retries.

Below are 60 REST API questions with the correct answer and a short explanation, grouped from fresher to experienced level. Try each one before you open the answer.

What REST API interviews usually check

  • ✓Resources, methods and status codes
  • ✓Pagination, filtering and sorting
  • ✓Versioning and backward compatibility
  • ✓Idempotency and retries
  • ✓Authentication, rate limits and errors

REST API interview questions for freshers easy

1. 401 or 403

A logged-in user with a valid token calls DELETE /admin/users/7, but they are not an admin. Which status code fits best?

  1. A.401 Unauthorized
  2. B.404 Not Found, always
  3. C.400 Bad Request
  4. D.403 Forbidden
Show answer

Answer: D. 403 Forbidden

401 means the request is not authenticated: the token is missing or invalid. Here the user is authenticated but does not have permission, which is exactly what 403 Forbidden means. Some APIs return 404 to hide that a resource exists, but that is a special choice, not the general rule.

2. Updating only one field

A client wants to change only the email of user 42 and does not want to send the other fields. Which request fits REST conventions best?

  1. A.PUT /users/42 with only the email field in the body, since PUT updates whichever fields are sent
  2. B.PATCH /users/42 with only the email field in the body
  3. C.POST /users with the email field and id 42
  4. D.GET /users/42?email=new@example.com
Show answer

Answer: B. PATCH /users/42 with only the email field in the body

PATCH is meant for partial updates: the body describes only the changes. PUT means "replace the whole resource with this", so sending only the email could wipe other fields. GET must never change data.

3. Status code for too many requests

Your API allows 100 requests per minute per key. A client sends its 101st request in the same minute. Which status code should you return?

  1. A.403 Forbidden
  2. B.503 Service Unavailable
  3. C.429 Too Many Requests
  4. D.400 Bad Request
Show answer

Answer: C. 429 Too Many Requests

429 Too Many Requests is the standard code for rate limiting, and a Retry-After header tells the client when to try again. 403 means the client is not allowed at all, and 503 suggests the whole server is overloaded or down.

4. What HTTPS leaves visible

A user calls your API over HTTPS on public Wi-Fi. Which of these can someone watching the network usually still see?

  1. A.The JSON request body
  2. B.The URL path and query string, because TLS only encrypts the body and headers of the request
  3. C.The Authorization header
  4. D.The server's IP address and often the hostname, via DNS and the TLS SNI field
Show answer

Answer: D. The server's IP address and often the hostname, via DNS and the TLS SNI field

TLS encrypts the whole HTTP message: method, path, query string, headers and body. The IP address is always visible, and the hostname often leaks through DNS lookups and the SNI field unless DNS over HTTPS and Encrypted Client Hello are used. Even so, avoid secrets in URLs because they end up in server logs.

5. Calling DELETE on a read-only route

A client sends DELETE to /reports/2026-q1, which only supports GET. What should the API return?

  1. A.405 Method Not Allowed with an Allow: GET header
  2. B.404 Not Found with an empty body
  3. C.403 Forbidden with an Allow: GET header
  4. D.400 Bad Request with a stack trace
Show answer

Answer: A. 405 Method Not Allowed with an Allow: GET header

405 says the URL exists but does not support this method. The spec requires an Allow header listing the methods that do work, here GET. 404 would hide that the resource exists, and stack traces should never be sent to clients.

6. A client posts XML to a JSON API

A JSON-only endpoint receives a POST with Content-Type: application/xml. Which status code should it return?

  1. A.406 Not Acceptable
  2. B.413 Content Too Large
  3. C.415 Unsupported Media Type
  4. D.422 Unprocessable Content
Show answer

Answer: C. 415 Unsupported Media Type

415 means the server will not accept the format of the request body, as named by Content-Type. 406 is the other direction: the server cannot produce any format listed in the client's Accept header. 413 is for bodies that are too large.

7. Telling the client where the new item lives

POST /invoices creates invoice 981 and returns 201. Which header should point to the new resource?

  1. A.Location: /invoices/981
  2. B.Content-Location: /invoices
  3. C.Link: /invoices?id=981
  4. D.Referer: /invoices/981
Show answer

Answer: A. Location: /invoices/981

With 201 Created, the Location header gives the URL of the new resource, so the client can fetch it without building the URL itself. Referer is a request header sent by clients, and Content-Location describes the body being returned.

8. Route parameter never matches a number

In Express, a route /orders/:id runs this check. For a request to /orders/42 it always says not found. Why?

app.get('/orders/:id', (req, res) => {
  const order = orders.find(o => o.id === req.params.id)
  if (!order) return res.sendStatus(404)
  res.json(order)
})
// orders = [{ id: 42, ... }]
  1. A.Express removes numbers from the URL path
  2. B.req.params.id is the string '42', not the number 42
  3. C.The route must be written as /orders/{id}
  4. D.find only works on arrays of strings
Show answer

Answer: B. req.params.id is the string '42', not the number 42

Everything in a URL is text, so route parameters and query values arrive as strings. '42' === 42 is false. Parse and validate the value, for example Number(req.params.id) with a check for NaN, or use a schema library.

9. Response for a queued report job

POST /exports starts a large export that takes several minutes in the background. What should the API return straight away?

  1. A.202 Accepted with a link to check the job status
  2. B.200 OK after waiting for the export to finish
  3. C.201 Created with the finished file
  4. D.204 No Content and nothing else
Show answer

Answer: A. 202 Accepted with a link to check the job status

202 Accepted means the request is valid and queued, but not finished. Return a job id or a status URL such as /exports/77 that the client can poll, or notify it by webhook later. Holding the HTTP request open for minutes leads to timeouts.

10. How many URLs a GraphQL API uses

A team moves from REST to GraphQL. How are different queries and mutations usually sent to the server?

  1. A.To one URL per type, like /graphql/users
  2. B.To one endpoint such as POST /graphql
  3. C.To one URL per field the client needs
  4. D.Over FTP, since GraphQL is not HTTP
Show answer

Answer: B. To one endpoint such as POST /graphql

A GraphQL API usually exposes a single endpoint, and the query document in the request says what data to read or change. Clients normally send it with POST and a JSON body. This is why per-URL tools such as CDN caching and URL-based rate limits need extra work.

11. Stack traces in production errors

A 500 response in production includes the full stack trace, file paths and SQL text. What should it return instead?

  1. A.The same stack trace, but base64-encoded
  2. B.An empty 200 response so clients do not panic
  3. C.A generic message plus an error id to find the logs
  4. D.The stack trace only for requests from mobile apps
Show answer

Answer: C. A generic message plus an error id to find the logs

Stack traces and SQL reveal internal paths, library versions and table names that help attackers. Send a short generic message with an error or request id, and log the full details on the server where the team can look them up. Encoding the trace does not hide it.

12. Telling users about a long job

A report export takes 3 minutes and runs as a background job. What should the API return when the user requests it?

  1. A.200 OK after waiting for the job to finish
  2. B.204 No Content and nothing else
  3. C.500 if the job is not done within 30 seconds
  4. D.202 Accepted with a job ID to check status
Show answer

Answer: D. 202 Accepted with a job ID to check status

202 Accepted means the request was taken but the work is not finished. Returning a job ID (and a status URL) lets the client poll, or you can notify by email, webhook or WebSocket when done. Holding the HTTP request open for minutes ties up connections and hits proxy timeouts.

13. Which method is idempotent

Which HTTP method is defined as idempotent, meaning sending the same request twice has the same effect on the server as sending it once?

  1. A.PUT
  2. B.POST
  3. C.Any method, as long as it returns JSON
  4. D.Only methods sent over HTTPS
Show answer

Answer: A. PUT

PUT replaces a resource with the given state, so repeating it leaves the same result. GET, HEAD, PUT, DELETE and OPTIONS are idempotent by definition. POST is not, which is why retrying a POST can create duplicates. Response format and HTTPS do not affect idempotency.

14. PATCH vs PUT for one field

On a settings page the user changes only their display name. The client wants to send just that one field. Which request fits REST conventions best?

  1. A.PUT /api/users/42 with { "name": "Asha" }
  2. B.PATCH /api/users/42 with { "name": "Asha" }
  3. C.GET /api/users/42?name=Asha
  4. D.POST /api/users/42/name/Asha
Show answer

Answer: B. PATCH /api/users/42 with { "name": "Asha" }

PATCH applies a partial change, so sending only the changed field is exactly its purpose. PUT means "replace the whole resource with this body", so fields you leave out may be cleared. GET must never change data, and putting the value in the path is not resource-oriented.

15. URL for a user’s orders

Which endpoint best follows REST conventions for listing the orders of user 42?

  1. A.GET /getUserOrders?userId=42
  2. B.POST /users/42/orders
  3. C.GET /orders/list/users/42
  4. D.GET /users/42/orders
Show answer

Answer: D. GET /users/42/orders

REST URLs name resources with nouns and use the HTTP method for the action, so GET /users/42/orders reads naturally as "the orders of user 42". POST to that URL would create an order instead. Verb-style names like getUserOrders work but break the convention.

16. What triggers a preflight

Your frontend at app.example.com calls api.example.com. Which request makes the browser send an OPTIONS preflight first?

  1. A.A GET with no custom headers
  2. B.A POST with an application/x-www-form-urlencoded body
  3. C.A request to the same origin
  4. D.A POST with Content-Type: application/json
Show answer

Answer: D. A POST with Content-Type: application/json

Cross-origin "simple" requests (GET, HEAD or POST with form-urlencoded, multipart or text/plain bodies and no custom headers) skip the preflight. A JSON content type, an Authorization header or methods like PUT and DELETE need the browser to ask permission with OPTIONS first. Your API must answer that OPTIONS request with the right CORS headers.

17. Dates sent through JSON

An API returns this object as JSON and the client parses it. What does the last line print?

const sent = { createdAt: new Date('2026-01-05T10:00:00Z') }
const received = JSON.parse(JSON.stringify(sent))
console.log(typeof received.createdAt)
  1. A.string
  2. B.object
  3. C.number
  4. D.undefined
Show answer

Answer: A. string

JSON has no date type, so JSON.stringify turns a Date into an ISO string and JSON.parse leaves it as a string. The client must convert it back, for example with new Date(value) or a schema that coerces dates. TypeScript types saying Date will not change this at runtime.

18. Permanent vs temporary redirect

You have moved your blog permanently from /blog to a new domain. Which HTTP status should the old URLs return?

  1. A.302 Found
  2. B.304 Not Modified
  3. C.301 Moved Permanently
  4. D.307 Temporary Redirect
Show answer

Answer: C. 301 Moved Permanently

301 tells browsers and search engines the move is permanent, so they update links and pass ranking signals. 302 and 307 are temporary. 304 is a caching response meaning the resource has not changed.

19. Idempotent requests

Which operation is idempotent, so it is safe to retry after a timeout?

  1. A.POST /orders that creates a new order each time
  2. B.PUT /users/42/email setting the email to a fixed value
  3. C.POST /payments that charges a card
  4. D.A call that adds 10 to an account balance
Show answer

Answer: B. PUT /users/42/email setting the email to a fixed value

An idempotent operation has the same effect whether it runs once or many times. Setting a field to a fixed value is idempotent, while creating an order, charging a card or adding to a balance would repeat the effect. Non-idempotent calls need an idempotency key before they can be retried safely.

20. Turning on text compression

A JSON API returns 800 KB responses as plain text. What does enabling gzip or Brotli compression mainly improve?

  1. A.The database runs its queries faster
  2. B.The server needs no memory for requests
  3. C.Less data is sent, so transfer time and bandwidth drop
  4. D.The number of users allowed doubles automatically
Show answer

Answer: C. Less data is sent, so transfer time and bandwidth drop

Text formats like JSON, HTML and CSS compress very well, often by 70–90%. Smaller responses travel faster, especially on mobile networks, and cut bandwidth cost. Compression uses some CPU, so measure the trade-off under load.

REST API intermediate interview questions medium

21. Token bucket rate limiter

You rate-limit an API with a token bucket: capacity 10 tokens, refilled at 1 token per second. A client has been idle for a minute and then sends 15 requests at once. What happens?

  1. A.About 10 requests are allowed immediately and the rest are rejected until tokens refill
  2. B.All 15 are allowed because the client was idle for 60 seconds
  3. C.Only 1 request is allowed per second, so 14 are rejected
  4. D.All 15 are rejected because the burst is larger than the capacity
Show answer

Answer: A. About 10 requests are allowed immediately and the rest are rejected until tokens refill

The bucket never holds more than its capacity, so after a long idle time it has 10 tokens. Each request uses one token, so 10 pass as a burst and the other 5 get 429 Too Many Requests until new tokens arrive. This allows short bursts while keeping the long-run average rate.

22. CDN serves one user's profile to another

After putting a CDN in front of your API, user B sometimes receives user A's data from GET /me. Which response header change fixes this?

  1. A.Send Cache-Control: private (or no-store) on per-user responses so shared caches do not store them
  2. B.Send Cache-Control: public, max-age=3600
  3. C.Add an ETag header so the CDN revalidates every response with the origin before serving it to any user
  4. D.Switch the CDN to HTTP/3
Show answer

Answer: A. Send Cache-Control: private (or no-store) on per-user responses so shared caches do not store them

Shared caches key responses mostly by URL, and /me is the same URL for every user. private allows only the user's own browser to cache it, and no-store forbids caching at all, which is right for sensitive data. Keep CDN caching for truly public responses.

23. 400 versus 422

Many APIs use both 400 and 422 for bad input. Which split between them is the common convention?

  1. A.400 for missing auth, 422 for missing permissions
  2. B.400 for bad GET requests, 422 for bad POST requests
  3. C.400 for malformed syntax, 422 for well-formed invalid values
  4. D.400 for server-side bugs, 422 for database write errors
Show answer

Answer: C. 400 for malformed syntax, 422 for well-formed invalid values

400 Bad Request suits a body that cannot be parsed, such as broken JSON. 422 Unprocessable Content suits a body that parses fine but breaks the rules, such as an invalid email. Whichever you choose, use it consistently and return field-level error details.

24. Redirecting a POST endpoint

POST /v1/payments has moved permanently to /v2/payments. Some clients follow the redirect but send a GET with no body. Which status code keeps the method and body?

  1. A.301 Moved Permanently
  2. B.302 Found
  3. C.303 See Other
  4. D.308 Permanent Redirect
Show answer

Answer: D. 308 Permanent Redirect

For historical reasons, clients may change POST to GET when following 301 or 302, and 303 always means 'fetch with GET'. 308 (permanent) and 307 (temporary) require the client to repeat the same method and body. For APIs, it is often better to keep the old route working.

25. no-cache or no-store for account data

A bank balance response must never be written to disk by browsers or proxies. Which header does that?

  1. A.Cache-Control: no-cache
  2. B.Cache-Control: max-age=0
  3. C.Cache-Control: no-store
  4. D.Cache-Control: must-revalidate
Show answer

Answer: C. Cache-Control: no-store

no-store tells every cache not to keep a copy at all. no-cache, despite its name, allows storing but requires checking with the server before each reuse. max-age=0 and must-revalidate are also about freshness, not about storing.

26. Arabic page served to English users

An API returns English or Arabic text based on the Accept-Language header. After adding a CDN, some English users get Arabic. What header is missing?

  1. A.Content-Language: en
  2. B.Vary: Accept-Language
  3. C.Accept-Ranges: bytes
  4. D.Cache-Control: public
Show answer

Answer: B. Vary: Accept-Language

Vary tells caches which request headers change the response, so they store a separate copy for each value. Without Vary: Accept-Language, the CDN reuses the first cached copy for everyone. Content-Language only labels the response.

27. Telling clients when to retry

Your API returns 429 Too Many Requests. Which response header tells the client how long to wait?

  1. A.Retry-After
  2. B.Expires
  3. C.Keep-Alive
  4. D.Age
Show answer

Answer: A. Retry-After

Retry-After gives a number of seconds or an HTTP date, and is used with 429 and 503. Good clients wait at least that long before retrying. Expires and Age are caching headers, and Keep-Alive is about connections.

28. Webhooks retried though they worked

Your webhook handler sends emails and generates a PDF before replying, taking 25 seconds. The provider marks deliveries as failed and retries. What should you change?

  1. A.Raise your own server timeout to 60 seconds
  2. B.Save the event, reply 2xx, do the work in a queue
  3. C.Reply 202 only once the PDF has been made
  4. D.Block the provider's retries in your firewall
Show answer

Answer: B. Save the event, reply 2xx, do the work in a queue

Providers wait only a few seconds for a response before treating the delivery as failed. Verify the signature, save the event, respond quickly with 2xx and do the slow work in a background job. Combine it with event-id deduplication in case of retries.

29. Paging through a GraphQL list

In the common GraphQL 'connection' pagination style, how does a client ask for the next page of orders?

query {
  orders(first: 20, after: "Y3Vyc29yOjQw") {
    edges { node { id total } cursor }
    pageInfo { hasNextPage endCursor }
  }
}
  1. A.Pass the last page's pageInfo.endCursor as after
  2. B.Increase first to 40 and drop the duplicates
  3. C.Pass the page number in the cursor field
  4. D.Call a separate nextPage mutation
Show answer

Answer: A. Pass the last page's pageInfo.endCursor as after

Connections use opaque cursors: each response gives pageInfo.endCursor and hasNextPage, and the client sends after: endCursor to continue. Cursors stay stable when rows are added, unlike page numbers. The server decides what the cursor encodes, often the sort key and id.

30. Printing each message at most once per 10 seconds

A logger should print a message only if the same message was not printed in the last 10 seconds. What does the map store?

  1. A.Message to the timestamp it was last printed
  2. B.Timestamp to the list of messages
  3. C.Message to the number of times it was seen
  4. D.A Set of all messages ever printed
Show answer

Answer: A. Message to the timestamp it was last printed

On each call, print if the message is new or now - last[msg] >= 10, and then update last[msg] = now. Old entries can be purged periodically so the map does not grow without limit.

31. One bad item in a batch call

A bulk API accepts 500 contacts. One has an invalid email. Currently the whole batch returns 400 and the client retries all 500 forever. What is a better design?

  1. A.Process valid items and return a per-item result list
  2. B.Return 200 and silently skip the invalid contact
  3. C.Reject the batch and ask clients to send one at a time
  4. D.Return 500 so the client knows to retry later
Show answer

Answer: A. Process valid items and return a per-item result list

Per-item results (for example HTTP 207 or a 200 with a status for each item) let clients fix and resend only failures. All-or-nothing on large batches makes one bad record block everything. Silent skipping hides data loss, and 500 wrongly signals a server fault.

32. Offset vs cursor pagination

An infinite-scroll feed uses LIMIT 20 OFFSET n. New posts are added every few seconds and users report seeing duplicate posts. What is the better design?

  1. A.Cursor (keyset) pagination: return items WHERE created_at, id are older than the last item seen
  2. B.Increase the page size to 200
  3. C.Use OFFSET but sort randomly
  4. D.Load the entire table once and paginate in the browser
Show answer

Answer: A. Cursor (keyset) pagination: return items WHERE created_at, id are older than the last item seen

With OFFSET, new rows inserted at the top shift every page, causing duplicates or skipped items, and large offsets get slow. Keyset pagination uses the last seen key (with a tie-breaker like id) and an index, so it is stable and fast. Loading the whole table does not scale.

33. Users see each other’s profile

GET /api/me returns the logged-in user’s profile with Cache-Control: public, max-age=300. After putting a CDN in front, some users see someone else’s name. What is the fix?

  1. A.Lower max-age to 30 seconds
  2. B.Add a random query string on the client
  3. C.Move the endpoint to a separate subdomain so the CDN keeps a separate cache per user
  4. D.Send Cache-Control: private (or no-store) for personalized responses
Show answer

Answer: D. Send Cache-Control: private (or no-store) for personalized responses

public allows any shared cache, including the CDN, to store the response and serve it to the next visitor of the same URL. private limits caching to the user’s own browser, and no-store prevents caching entirely. A shorter TTL only shrinks the leak window, and query-string tricks are easy to miss.

34. GraphQL author lookups

A GraphQL query asks for 50 posts with { author { name } }. Logs show 51 SQL queries. What is the standard fix?

  1. A.Switch the API from GraphQL to REST
  2. B.Cache every author forever in Redis so the resolver never has to touch the database
  3. C.Limit the query to 10 posts
  4. D.Use a per-request DataLoader so author lookups are batched into one query
Show answer

Answer: D. Use a per-request DataLoader so author lookups are batched into one query

The author resolver runs once per post, which is the N+1 pattern. DataLoader collects all author IDs requested in the same tick and loads them with one WHERE id IN (...) query, caching per request. Creating it per request keeps one user’s data from leaking into another’s.

35. Rate limit keyed on X-Forwarded-For

Your login rate limiter reads the client IP from the first X-Forwarded-For value. Attackers bypass it easily. Why?

  1. A.X-Forwarded-For is always empty over HTTPS
  2. B.Rate limits only work with IPv6
  3. C.Browsers rotate IPs on every request
  4. D.Clients can put any value in that header
Show answer

Answer: D. Clients can put any value in that header

Each proxy appends to X-Forwarded-For, but the first entries come from the client and can be forged. Trust only the entries added by your own proxies (e.g. Express trust proxy set to the right hop count) or the address your load balancer reports.

36. Many login attempts in one request

Your login rate limit counts HTTP requests. An attacker sends one GraphQL request with 500 aliased login mutations. What happened?

  1. A.GraphQL ignores aliases
  2. B.The server ran only the first mutation
  3. C.TLS rejected the large request
  4. D.Aliases bypassed a per-request rate limit
Show answer

Answer: D. Aliases bypassed a per-request rate limit

Aliases let one operation call the same field many times, so 500 password guesses fit in one HTTP request. Rate-limit per operation or per login attempt, and cap the number of aliases or root fields.

37. Every request comes from the load balancer

Behind an HTTP load balancer, your app logs show every request coming from 10.0.x.x addresses. How do you get the real client IP?

  1. A.Read X-Forwarded-For set by the load balancer
  2. B.Read the TCP source address more carefully
  3. C.Ask the client to send its IP in the body
  4. D.Disable TLS on the load balancer
Show answer

Answer: A. Read X-Forwarded-For set by the load balancer

A layer 7 load balancer opens a new connection to the backend, so the TCP source is the balancer. It adds the client IP to the X-Forwarded-For header. Only trust the value added by your own proxies, since clients can forge earlier entries.

38. Serving stale while refreshing

A response has "Cache-Control: max-age=60, stale-while-revalidate=300". What happens at 2 minutes old?

  1. A.The cache blocks the user until origin replies
  2. B.The object is deleted and returns 404
  3. C.The cache serves it only to logged-in users
  4. D.The stale copy is served while a background refresh runs
Show answer

Answer: D. The stale copy is served while a background refresh runs

For 60 seconds it is fresh. For the next 300 seconds a cache may serve the stale copy immediately while fetching a new one in the background, so users rarely wait on origin. After that it must revalidate first.

39. New connection for every request

A script sets k6 option noConnectionReuse: true. Latency rises and server CPU goes up. Why?

  1. A.k6 stops sending HTTP headers
  2. B.The server starts compressing every response twice
  3. C.Connection reuse only affects DNS
  4. D.Each request pays for a new TCP and TLS handshake
Show answer

Answer: D. Each request pays for a new TCP and TLS handshake

With connection reuse turned off, every request opens a new TCP connection and does a TLS handshake, which adds round trips and costs server CPU. By default k6 VUs keep connections alive like browsers do. Turn reuse off only if you deliberately want to test connection-heavy clients.

40. The strongest signal of demand

After a demo, four prospects react differently. Whose reaction is the strongest evidence of real demand?

  1. A.A dentist says "This is amazing, I'd definitely use it"
  2. B.A clinic owner pays ₹15,000 upfront for a three-month pilot
  3. C.A hospital manager asks you to email the brochure
  4. D.A doctor likes your LinkedIn post about it
Show answer

Answer: B. A clinic owner pays ₹15,000 upfront for a three-month pilot

Compliments and polite requests cost nothing. Real signals involve giving up something valuable: money, time, reputation or access (for example, an intro to the decision-maker). Paying upfront is the clearest commitment of the four.

REST API interview questions for experienced candidates hard

41. Safe retries for payments

A mobile app calls POST /payments. The network times out, so the app retries, and the customer is charged twice. What is the most robust fix?

  1. A.Client sends a unique Idempotency-Key; the server stores it with the result under a unique constraint and returns the saved result for repeats
  2. B.Disable retries in the mobile app completely
  3. C.Change the endpoint from POST to GET so it becomes idempotent
  4. D.Check if a payment with the same amount was made in the last 10 seconds and skip it
Show answer

Answer: A. Client sends a unique Idempotency-Key; the server stores it with the result under a unique constraint and returns the saved result for repeats

An idempotency key lets the server recognise a retry of the same logical request. Storing the key atomically (unique index) before or with the charge, and returning the stored response for duplicates, makes retries safe. Disabling retries hurts reliability, GET must not change state, and amount-based checks block real repeat purchases.

42. Retries at every layer

A mobile app, the gateway and the order service each retry a failed call up to 3 attempts in total. When the payment service is down, how many calls can one tap cause at the payment service?

  1. A.27
  2. B.9
  3. C.3
  4. D.6
Show answer

Answer: A. 27

Each layer makes up to 3 attempts, and each attempt from the layer above triggers that, so it multiplies: 3 × 3 × 3 = 27. This turns a small outage into a retry storm. Retry in one layer only, use backoff with jitter and a retry budget.

43. Users uploading 2 GB videos

Users upload videos up to 2 GB, and routing them through your API servers ties up memory and bandwidth. What is the standard design?

  1. A.Raise the JSON body size limit to 2 GB
  2. B.Let clients upload to storage with a presigned URL
  3. C.Base64-encode each video inside a JSON field
  4. D.Have users email their large files to support
Show answer

Answer: B. Let clients upload to storage with a presigned URL

The API checks the user and returns a presigned URL valid for a few minutes, and the client uploads straight to object storage, often in multiple parts. The API then records or processes the file via a callback or event. Base64 makes the payload about 33% larger.

44. Rate limiter blocks everyone together

Behind a load balancer, an Express rate limiter keyed on req.ip starts blocking all users at once. What is the likely cause?

  1. A.req.ip is the load balancer's IP; set 'trust proxy' correctly
  2. B.The limiter counts requests per URL, not per IP
  3. C.Express caches req.ip for every user
  4. D.Load balancers strip all headers from requests
Show answer

Answer: A. req.ip is the load balancer's IP; set 'trust proxy' correctly

Without 'trust proxy', Express reports the address of the direct connection, which is the load balancer, so every user shares one counter. Setting app.set('trust proxy', 1) (the number of trusted hops) makes req.ip use X-Forwarded-For. Trusting it blindly would let clients fake their IP.

45. Every page load counts 80 million rows

GET /events?limit=50 also returns total, computed with COUNT(*) on an 80-million-row table, and it is the slowest part of the request. What is a common API design change?

  1. A.Cache the count in the browser forever
  2. B.Count only the rows on the current page
  3. C.Return has_more or a next cursor instead of an exact total
  4. D.Return the total in a response header instead
Show answer

Answer: C. Return has_more or a next cursor instead of an exact total

An exact count must scan a large index or table for every request, even for page one. Many APIs return only whether more pages exist (fetch limit + 1 rows) or a next cursor, and offer an approximate or separately cached total if a UI really needs it. Moving the same count to a header costs the same.

46. Search filters too long for a URL

A search endpoint takes dozens of filters, nested conditions and a list of up to 500 SKUs. Long GET URLs fail at some proxies. What is a reasonable design?

  1. A.Split the SKU list across many GET requests
  2. B.POST /products/search with the filters in a JSON body
  3. C.Send the filters in a custom request header
  4. D.Use GET with a body, since HTTP allows it
Show answer

Answer: B. POST /products/search with the filters in a JSON body

Proxies and servers often limit URLs to around 8 KB. A dedicated search resource with POST and a JSON body is a common pattern, accepting that it is not cached by default. A GET body has no defined meaning and is often dropped, and large headers hit similar limits.

47. A PUT wipes the phone number

A client sends PUT /customers/7 with only {"email": "new@example.ae"}. The server follows PUT semantics strictly. What happens to the customer's phone number?

  1. A.It is kept, because PUT only changes sent fields
  2. B.It is cleared, because PUT replaces the whole resource
  3. C.It is copied into the email field
  4. D.The server must reject PUT bodies with one field
Show answer

Answer: B. It is cleared, because PUT replaces the whole resource

PUT means 'make the resource equal to this representation', so fields left out are removed or reset to defaults. For changing one field, use PATCH, for example JSON Merge Patch. Many bugs come from clients that treat PUT like PATCH, so document the behaviour clearly.

48. Locking accounts after 5 failures

Accounts are locked for 24 hours after 5 wrong passwords. Attackers start locking out real users on purpose. What is a better approach?

  1. A.Lock accounts for good after 3 failures
  2. B.Remove every limit on login attempts
  3. C.Growing delays and risk checks, not hard locks
  4. D.Let anyone unlock by typing the email
Show answer

Answer: C. Growing delays and risk checks, not hard locks

Hard lockouts let anyone who knows an email deny service to that user. Growing delays, per-account and per-IP limits, CAPTCHA after failures and step-up checks slow attackers while the real user can still get in. Alert users about unusual failed attempts.

49. Preventing double charges on retry

The checkout page sends POST /api/payments. The request times out on a slow network, the client retries, and the customer is charged twice. What is the most robust fix?

  1. A.Disable the Pay button for 2 seconds after a click
  2. B.Increase the server timeout to 60 seconds
  3. C.Send a client-generated idempotency key; the server stores it with a unique constraint and returns the saved result for any repeat with the same key
  4. D.Change the endpoint from POST to GET
Show answer

Answer: C. Send a client-generated idempotency key; the server stores it with a unique constraint and returns the saved result for any repeat with the same key

Timeouts mean the client cannot know whether the first request succeeded, so retries must be safe. An idempotency key stored atomically (unique index) lets the server detect repeats and return the first response instead of charging again. Disabling buttons and longer timeouts reduce but do not remove the race; GET must not have side effects.

50. Large IDs change in the browser

The database uses BIGINT ids, and the API serializes them as JSON numbers. What does the client print here?

const body = '{"id": 9007199254740993}'
console.log(JSON.parse(body).id)
  1. A.9007199254740993
  2. B.9007199254740992
  3. C.An exception: number too large
  4. D.9007199254740993n
Show answer

Answer: B. 9007199254740992

JavaScript numbers are 64-bit floats and are exact only up to 2^53 - 1 (9007199254740991), so larger integers are silently rounded. The client then requests or updates the wrong record. Send 64-bit IDs as strings in JSON, which is what many large APIs do.

51. Items missing between pages

An activity feed uses this cursor query. After a bulk import gave 300 rows the same created_at, users report items missing between pages. What is the fix?

SELECT * FROM events
WHERE created_at < $1  -- created_at of last row on previous page
ORDER BY created_at DESC
LIMIT 50;
  1. A.Switch to OFFSET pagination for imported data
  2. B.Use <= instead of < in the WHERE clause
  3. C.Order by (created_at, id) and use both as the cursor: WHERE (created_at, id) < ($1, $2) ORDER BY created_at DESC, id DESC
  4. D.Increase LIMIT to 500 so ties fit on one page
Show answer

Answer: C. Order by (created_at, id) and use both as the cursor: WHERE (created_at, id) < ($1, $2) ORDER BY created_at DESC, id DESC

When many rows share the boundary timestamp, < skips the rest of them, while <= would repeat rows instead. Adding a unique tie-breaker such as id gives a strict total order, and row comparison makes the cursor exact. Back it with an index on (created_at, id).

52. Choosing an API contract approach

Your API will be used by the Next.js web app, native iOS and Android apps and outside partners. The team wants end-to-end type safety. Which approach fits best?

  1. A.An OpenAPI contract with generated typed clients for each platform
  2. B.tRPC only, because it gives every client full end-to-end types without writing any schema
  3. C.Share the TypeScript types folder with partners by email
  4. D.Skip a contract and document endpoints in a README
Show answer

Answer: A. An OpenAPI contract with generated typed clients for each platform

OpenAPI is language-neutral, so Swift, Kotlin, partner code and your TypeScript app can all generate clients from one contract and catch breaking changes in CI. tRPC is excellent when the client and server are TypeScript in the same codebase, but it does not give outside or native clients a stable, documented contract. Some teams use tRPC internally and OpenAPI for the public surface.

53. Duplicate orders from offline retries

An offline queue retries a "place order" POST after a timeout. The server had actually processed the first attempt, so the customer gets two orders. What is the right fix?

  1. A.Retry less often, waiting at least ten minutes between attempts, so the server has time to finish and its own duplicate check can catch up
  2. B.Create an idempotency key on the device, keep it with the queued request and send it on every retry, so the server returns the first result
  3. C.Send the order as a GET request
  4. D.Delete the request from the queue after the first attempt, whatever the outcome
Show answer

Answer: B. Create an idempotency key on the device, keep it with the queued request and send it on every retry, so the server returns the first result

A timeout does not tell the client whether the server received the request. A client-generated idempotency key that stays the same across retries lets the server detect repeats and return the original response. Dropping the request after one try would lose orders, and fewer retries only make duplicates rarer.

54. Renaming a field in the API

The backend team wants to rename `price` to `amount` in the orders API this week. Why must mobile apps be considered differently from the web app?

  1. A.Mobile apps download the API schema daily from the stores
  2. B.Old app versions stay installed for months and still expect price
  3. C.Mobile apps cannot parse JSON fields named amount
  4. D.Mobile releases always ship on the same day as the backend
Show answer

Answer: B. Old app versions stay installed for months and still expect price

Web clients update on the next page load, but many users run app versions that are months or years old. Breaking changes need versioned endpoints or additive changes: add amount, keep price until usage by old versions is negligible, then remove it. Tracking app versions in API logs shows when it is safe.

55. HTTP/3 on a network that blocks UDP

A corporate firewall blocks outbound UDP 443. What happens when a browser tries a site that supports HTTP/3?

  1. A.The site fails to load at all
  2. B.The browser tunnels QUIC inside DNS
  3. C.It falls back to HTTP/2 or HTTP/1.1 over TCP
  4. D.It switches to plain HTTP on port 80
Show answer

Answer: C. It falls back to HTTP/2 or HTTP/1.1 over TCP

QUIC runs over UDP. Browsers discover HTTP/3 via Alt-Svc or DNS HTTPS records and race or fall back to TCP if QUIC does not work, so pages still load over HTTP/2. Servers must keep TCP support for such networks.

56. Static pages never cached

Your marketing pages have long Cache-Control headers, yet the CDN reports 0% hits. Every response includes a Set-Cookie for an analytics session. What is the likely cause?

  1. A.CDNs never cache HTML pages
  2. B.Cache-Control is ignored over HTTPS
  3. C.Many CDNs skip caching responses that set cookies
  4. D.Analytics cookies make pages too large to cache
Show answer

Answer: C. Many CDNs skip caching responses that set cookies

Caching a response with Set-Cookie could hand one user's cookie to others, so many CDNs skip caching by default. Remove Set-Cookie from cacheable responses, set cookies from client-side scripts, or configure the CDN to strip them deliberately.

57. HTTP/3 on lossy mobile networks

Your app's users on congested mobile networks see long stalls with HTTP/2. Why might HTTP/3 help most in this case?

  1. A.QUIC recovers loss per stream, avoiding TCP blocking
  2. B.HTTP/3 compresses images better than HTTP/2
  3. C.HTTP/3 needs no encryption, so it is faster
  4. D.HTTP/3 uses larger packets to beat congestion
Show answer

Answer: A. QUIC recovers loss per stream, avoiding TCP blocking

With HTTP/2 all streams share one TCP connection, so one lost packet stalls every stream. QUIC handles each stream independently and also handles network changes better, which matters most where loss is common.

58. The gateway throttles the test

The API gateway allows 100 rps per client IP. Your test sends 1,000 rps from one load generator IP and gets mostly 429s. The goal is to test the services behind the gateway. What should you do?

  1. A.Agree a test exemption or spread load like real clients
  2. B.Report that the services can only handle 100 rps
  3. C.Remove all error checks from the script
  4. D.Retry every 429 immediately until it succeeds
Show answer

Answer: A. Agree a test exemption or spread load like real clients

Real traffic comes from many clients, each under its own limit, so one IP at 1,000 rps is not realistic. Work with the platform team to allow-list the test IPs, use many source addresses, or test the services directly. Immediate retries only make the throttling worse.

59. Infinite scroll category pages

A category page loads more products by infinite scroll with no change in URL. Products beyond the first 24 never get indexed. What is the fix?

  1. A.Increase the scroll speed so Googlebot sees more
  2. B.Put all 2,000 products into the very first page load
  3. C.Give each chunk its own URL, linked with <a href>
  4. D.Add the products only to the meta description
Show answer

Answer: C. Give each chunk its own URL, linked with <a href>

Googlebot does not scroll or click "load more" like a user. Google recommends a paginated series with unique URLs such as ?page=2, linked with normal anchors, while infinite scroll can still update the URL for users. Then every product is reachable.

60. A concierge MVP that customers love

You validated demand by manually doing the work for 10 customers (a concierge MVP). They love it. What must you test before scaling?

  1. A.Whether customers like your team personally
  2. B.Whether 10 customers is enough for an IPO
  3. C.Whether software can deliver the value at viable cost
  4. D.Nothing; love proves the business works
Show answer

Answer: C. Whether software can deliver the value at viable cost

A concierge MVP proves people want the outcome, but humans may be delivering hidden value that software cannot copy cheaply. Test whether an automated version keeps satisfaction and unit economics before investing in scale.

Ready to test yourself?

Take a 15-question REST API MCQ test with a score at the end, or practise the full REST API question bank and earn points on the leaderboard.