Practical Coding Rounds/API & Data Integration Exercises

Working From Docs: Auth, Pagination, Retries, and Idempotent Calls

Reading unfamiliar API docs fast enough to find the right endpoint the first time, and the four things every production-shaped integration exercise checks for: authentication, pagination, retry-with-backoff, and not double-applying a retried write.

!!!3/5Theory: 25m2 problems

What this round actually measures

Stripe's own description of its Integration round is blunt about the target: not an algorithms test, a working-engineer test. You're given documentation for an API (real or mocked), a concrete goal, and 45-120 minutes, with full internet access to read docs — the skill being measured is reading documentation accurately and translating it into correct, defensive code, not memorized API trivia. The specific things a well-run version of this round checks for, in the order they usually bite:

Read the docs before you touch the keyboard

The single most cited mistake: reaching for the first endpoint that looks plausible instead of confirming it's the right one for the stated goal. Docs pages routinely offer several endpoints that return overlapping data shaped for different use cases (a summary endpoint vs. a detailed one, a v1 vs. v2 version) — a wrong-but-plausible choice early on can cost far more time downstream than the extra two minutes of careful reading would have. If an interviewer redirects you toward a different endpoint mid-round, that's useful signal being handed to you, not a sign you're failing — take it and move on rather than defending the original choice.

Authentication: read the exact mechanism, don't assume

APIs authenticate in several genuinely different ways — a static API key in a header, a Bearer token, HMAC request signing, OAuth token exchange — and assuming last time's mechanism applies here is a fast way to burn ten minutes on confusing 401s. Read the specific auth section for this API before writing the request code, and check whether the credential goes in a header, a query parameter, or the request body — all three are common, and getting it wrong usually produces an error message that doesn't obviously point at "wrong location," just "unauthorized."

Pagination: don't assume a fixed page count

List endpoints that can return more data than fits in one response almost always paginate, and there are a few common conventions worth recognizing on sight: a Link response header with rel="next" (GitHub, GitLab), an explicit next_cursor/next_page_token field in the response body, or simple page/offset query parameters you increment yourself. The robust pattern is the same regardless of convention: loop until the API itself tells you there's no more data (an absent next link, a null cursor, an empty page) rather than guessing a fixed number of pages — a dataset that happens to fit in the number of pages you hardcoded during testing will silently under-fetch the moment the real dataset is one page larger.

Retries: only for the failures a retry can actually fix

Not every failure should trigger a retry, and treating them uniformly is a common, subtly wrong instinct. A rough, broadly correct split:

  • 5xx server errors and network-level failures (timeouts, connection resets) — usually transient, worth retrying.
  • 4xx client errors (bad auth, malformed request, not found) — usually not worth retrying as-is; retrying the exact same malformed request will just fail the same way again, and blindly retrying a 401 can trip rate limits or account lockouts for no benefit.
  • 429 rate-limited — a special case worth its own handling: read the Retry-After header if present and wait at least that long, rather than backing off blindly.

When you do retry, use exponential backoff with jitter — each successive attempt waits roughly twice as long as the last, plus a small random amount — rather than a fixed delay or a tight loop. The jitter specifically matters at scale: without it, many clients that failed at the same moment (a brief outage) all retry at the same moment too, which is the "thundering herd" problem — the random jitter spreads retries out so a recovering server isn't immediately hit with a synchronized second wave.

Idempotency: the concern one layer past retries

Retrying a failed read is free — asking the same question twice changes nothing. Retrying a failed write (a charge, an order creation, a state-changing POST) is not free if the first attempt actually succeeded and only the response got lost — a naive retry can duplicate the operation. The standard fix, used by Stripe and widely copied, is an idempotency key: the client generates a unique ID once per logical operation and attaches it to every attempt (including retries); the server recognizes a repeated key and returns the original result instead of repeating the side effect. If a prompt in this round involves any mutating call and the interviewer asks "what happens if this request times out right as the server processes it, and your client retries?" — that's the idempotency question, and having an actual mechanism in mind (not just "well, hopefully it doesn't happen") is exactly the signal being probed for.

Reference implementations in:

A Paginated, Auth'd, Retrying Client Against a Real API

Three concerns stacked in the order a docs page usually presents them: auth header, following pagination until exhausted, and retrying only the failures worth retrying — with backoff and jitter, not a tight loop.

import time, random, requests def fetch_all_repos(username: str, token: str) -> list[dict]: repos, url = [], f"https://api.github.com/users/{username}/repos?per_page=100" headers = {"Authorization": f"Bearer {token}"} while url: response = _get_with_retry(url, headers) repos.extend(response.json()) url = response.links.get("next", {}).get("url") # None once pagination is exhausted return repos def _get_with_retry(url, headers, max_attempts=4): for attempt in range(max_attempts): response = requests.get(url, headers=headers) if response.status_code < 500: # 4xx are usually not worth retrying blindly return response backoff = (2 ** attempt) + random.uniform(0, 0.5) # exponential backoff + jitter time.sleep(backoff) response.raise_for_status()

response.links parses the standard Link header GitHub (and many REST APIs) use for pagination — following it until it's absent is more robust than assuming a fixed page count. Retrying only 5xx (not 4xx) avoids blindly hammering a request that's failing for a reason a retry can't fix, like bad auth.

Further Resources (Optional)

Practice Problems

Interview relevancy:!!!Critical — must-have!!Important — highly recommended!Good to have~Niche — rarely asked