Lesson 13 of 17
24. APIs and Idempotency

Practice: five levels

3 min read+10 XP

Assignments

Five levels. Do them in order. Answers are in "Solutions" at the end, so try first.

Level 1: Think (no code)

A. Which status code? One line each.

  1. The request body is missing the required amount field.
  2. The access token has expired.
  3. User 7 asks for user 9's order.
  4. The order was already paid by another request.
  5. The client has sent 200 requests in the last minute and the limit is 100.
  6. The payment service can't reach the bank right now.

B. Safe to retry automatically? One line each.

  1. GET /orders/881 timed out.
  2. POST /charges timed out, with no idempotency key.
  3. POST /charges returned 503, with an idempotency key.
  4. PUT /books/42 returned 502.
  5. POST /orders returned 422.

Level 2: Calculate

  1. Checkout makes 1,000 payment calls a minute; 0.5% of replies are lost and the client retries once without an idempotency key. How many double charges a day?
  2. A client pages through orders with offset pagination, 50 per page. How many rows does the database read and discard to serve page 2,000?
  3. The payment API stores each idempotency key with a 1 KB response for 24 hours, at 2 million payments a day. Roughly how much storage?
  4. Retries use full-jitter backoff capped at 0.5, 1, 2, 4 and 8 seconds. What's the longest a client could wait in total across those five retries?
  5. Four layers each retry a failing call 3 times (4 attempts each). How many calls reach the bottom service for one user action?

Level 3: Build

In Python:

  1. A Payments API with charge (no key) and charge_with_key that claims a key first, replays stored responses, returns 409 while in progress and 422 on a mismatched body, and expires keys after 24 hours.
  2. offset_page and cursor_page, and a test that inserts a new row between pages.
  3. problem(status, title, detail) returning an RFC 9457-style body, and should_retry(method, status, has_key).
  4. issue_key() and check_key() that store only a hash and compare in constant time.
  5. A webhook sign/verify with a timestamp tolerance, and a receiver that de-duplicates by event ID.

Level 4: Debug

Despite idempotency keys, a few customers were still charged twice, always when the mobile app retried quickly after a slow network. The payment service:

python
  1. Explain exactly how two requests with the same key both charge.
  2. Fix it.

Level 5: Design

Design the bookstore's public Orders API for partner apps and the bookstore's own mobile app: list a user's orders, get one order, create an order (which charges the card), and notify partners when an order ships.

Deliver:

  1. The endpoints, methods and main fields, and which style (REST, gRPC, GraphQL) for which client.
  2. Pagination for "list orders".
  3. Error format and the main status codes.
  4. How "create order" is made safe to retry end to end (app → bookstore → payment provider).
  5. How partners are notified of shipments, and how they verify and de-duplicate those notifications.