# The API design interview

> An API design interview tests whether you can define a clear contract between clients and a system, including what happens during retries, concurrent updates, and failures. Start with user workflows, specify a small set of endpoints, and explain the guarantees behind them rather than stopping at URL naming.

Source: https://sysround.com/guides/api-design-interview
Updated: 2026-11-05


## Start with workflows and guarantees, not endpoints

Clarify who calls the API, what they need to accomplish, and which outcomes must be correct. A public payment API, a mobile feed API, and an internal service API have different requirements for compatibility, latency, authorization, and retry behavior.

Choose one important workflow and follow it from request to final outcome. For a payment API, that might be creating a payment, checking its status after a timeout, and requesting a refund. Establish the invariant first: retrying a request must not create another charge.

- Who are the clients: browsers, mobile apps, external developers, or internal services?
- Which operations are required, and which are out of scope?
- Does success mean the request was accepted, stored durably, or completed?
- Which reads must immediately reflect a successful write?
- What are the expected request volume, payload size, and latency requirements?
- Are there tenant boundaries, sensitive fields, or audit requirements?

## Model resources and choose a small endpoint set

Use resources that match the domain, with stable identifiers and explicit lifecycle states. For payments, distinguish a payment from its refunds. A refund has its own amount, status, and failure history, so it deserves an addressable resource rather than an ambiguous update to the original payment.

Explain HTTP semantics without turning the interview into a naming debate. GET should not request business mutations. PUT usually replaces the representation at a known URI, while PATCH applies a partial change. POST can create a resource or submit a command, but is not automatically safe to retry.

- POST /v1/payments creates a payment resource.
- GET /v1/payments/{payment_id} retrieves its current state.
- GET /v1/payments?limit=50&cursor=... lists payments visible to the caller.
- POST /v1/payments/{payment_id}/refunds creates a refund resource.
- GET /v1/payments/{payment_id}/refunds/{refund_id} retrieves refund status.
- Use explicit operations for domain transitions when a generic PATCH would allow invalid state changes.

## Specify request, response, and error contracts

Show one concrete request and response. For example, POST /v1/payments takes an Authorization header, an Idempotency-Key header, and a body such as {"amount_minor":2500,"currency":"USD","payment_method_id":"pm_123"}. Define amount_minor as an integer in the currency's minor unit, validate currency-specific rules, and avoid floating-point money.

A response might be 201 Created with Location: /v1/payments/pay_123 and a body such as {"id":"pay_123","status":"processing","amount_minor":2500,"currency":"USD"}. Here, 201 means the payment resource exists, not that funds have moved. Document required fields, nullability, timestamps, identifier types, and which fields clients may change.

Use a consistent error shape, for example {"error":{"code":"invalid_amount","message":"Amount must be positive","field":"amount_minor"},"request_id":"req_123"}. Clients should branch on stable error codes rather than human-readable messages.

- 400: malformed requests or invalid parameters, with field-level details where useful.
- 401: missing or invalid authentication; 403: an authenticated caller lacks permission.
- 404: the resource does not exist, or a documented policy intentionally conceals its existence.
- 409: a conflict with current state, such as reusing an idempotency key with different parameters.
- 429: a rate limit was exceeded; include Retry-After when a useful retry time is known.
- 5xx: a server-side failure; clients must not assume a mutation had no effect.

## Make retries safe with an explicit idempotency contract

The classic follow-up is: the server committed the payment, but the client timed out before receiving the response. The client cannot infer failure from the timeout. Give it a documented way to retry the same operation without creating a second payment.

Scope an idempotency key to the authenticated account and operation, and store a fingerprint of the meaningful request fields. Atomically claim the key before executing the operation. A matching retry returns the recorded result or a documented in-progress response; the same key with a different payload is rejected.

The key record and the business mutation must be coordinated, not written independently with an unhandled crash window. External payment execution adds another boundary: use the provider's idempotency mechanism when available and reconcile uncertain outcomes. A local deduplication table alone cannot guarantee that an external charge happened only once.

- Document how long keys are retained and what happens after expiration.
- Handle simultaneous requests with the same key, not just sequential retries.
- Define whether a replay returns the original response or a current resource representation.
- Distinguish validation failures, in-progress work, and completed operations in the stored record.
- Use bounded exponential backoff with jitter for retryable failures, and honor Retry-After where applicable.

## Define concurrency and asynchronous operation semantics

For mutable resources, explain how you prevent lost updates. A client can read an ETag and send If-Match with an update; the server rejects a stale precondition with 412 Precondition Failed. The version check must be enforced atomically with the write, not checked separately before an unconditional update.

Client preconditions do not replace business invariants. Two different refund requests can each have valid idempotency keys and still exceed the refundable balance. Enforce that balance constraint transactionally or through another atomic mechanism.

For long-running work, separate acceptance from completion. You can return 201 when creating a durable resource whose state is processing, or 202 Accepted for an accepted asynchronous operation with a status URL. Define states, allowed transitions, terminal outcomes, and recovery when workers fail.

- Explain whether clients poll, receive webhooks, or use a live connection for completion updates.
- If a success response promises durable acceptance, persist enough state to recover the work before responding.
- Use a transactional outbox when a database update and queue publication must not diverge.
- Treat webhook delivery as potentially duplicated and delayed; provide event IDs and verify signatures.
- Define cancellation races explicitly: a cancellation request may arrive after an irreversible action.

## Design pagination around ordering and changing data

Offset pagination is simple and can be sufficient for small administrative lists. For large or frequently changing collections, cursor pagination avoids deep offsets and reduces shifting-page problems. Define the order explicitly, such as created_at descending with id descending as a unique tie-breaker.

Return items and an opaque next_cursor. The cursor can encode the last ordering key and the query context, with integrity protection or server-side storage. Align the database index with the tenant scope, common filters, and ordering.

A cursor does not automatically provide a snapshot. With immutable ordering keys, keyset pagination avoids many duplicates caused by new inserts, but deletions and changing membership can still affect traversal. If callers need a stable export, define snapshot or watermark semantics separately.

- Set a default and maximum page size.
- Specify supported filters and sorts instead of allowing arbitrary expensive queries.
- Bind cursors to the relevant query and authorization scope, and recheck permissions on every request.
- Do not promise an exact total count unless its cost and consistency are acceptable.
- Explain whether clients can resume with an old cursor and when cursors expire.

## Cover authorization, quotas, and resource limits

Authentication establishes identity; authorization decides whether that identity may perform this operation on this resource. Check object-level and tenant-level access on every request. Knowing a payment ID must never be sufficient to read or refund it.

Protect expensive operations as well as raw request volume. Bound body sizes, page sizes, batch sizes, and concurrent asynchronous jobs. A list request returning a few fields and a large export should not necessarily consume the same quota.

Explain where enforcement happens. A gateway can reject oversized requests, validate credentials, and apply coarse limits. Resource ownership checks and business permissions usually require application context. Cached responses and asynchronous workers must preserve the same access boundaries.

- Derive account scope from trusted authentication context, not only a client-supplied account_id.
- Separate read, create, refund, and administrative permissions where appropriate.
- Define rate-limit scope, such as account, credential, user, or IP, and discuss shared-IP trade-offs.
- Keep credentials and sensitive payment data out of URLs and logs.
- Record security-sensitive mutations in an audit trail without exposing secrets.

## Explain protocol choices and compatibility

REST with JSON is a reasonable default for a public resource-oriented API. GraphQL can help clients request different data shapes, but requires query-cost controls and careful field-level authorization. gRPC can suit typed internal calls and streaming, but browser support, debugging, and public-client tooling may require additional infrastructure.

Choose a compatibility policy before listing version numbers. Adding an optional response field is often compatible for tolerant clients, while changing a field's type, meaning, requiredness, or ordering guarantee can break them. New enum values also need a documented unknown-value strategy.

Use a major API version for incompatible contract changes when appropriate, and define deprecation and migration expectations. Keep old clients working during rollout. Test schemas and behavior, including error codes, defaults, retry semantics, and pagination guarantees, not just successful response shapes.

- Use an API description such as OpenAPI to make the HTTP contract reviewable and testable.
- Distinguish the public API version from database schema versions and internal event versions.
- Do not expose storage details that clients would then depend on.
- Explain the concrete client need before choosing GraphQL, streaming, or a custom protocol.

## Practice the API design interview questions that expose gaps

A strong answer connects every promise in the API to an enforcement mechanism. If you promise safe retries, identify the atomic boundary. If you promise stable pagination, define the ordering and snapshot behavior. If you promise immediate read-after-write visibility, explain how routing and storage support it.

When time is limited, go deep on one successful request and one failure scenario rather than sketching dozens of endpoints. Close by stating unresolved trade-offs and how you would observe the API in production, including latency, error rates, throttling, and asynchronous work stuck in progress.

- What happens if the client times out after the write commits?
- What happens when two clients update the same resource simultaneously?
- Can two individually valid requests violate a shared business constraint?
- What does a successful response guarantee, and what is still pending?
- What happens to pagination while records are inserted, deleted, or reordered?
- How do you prevent one tenant from accessing another tenant's resources?
- How would you change this contract without breaking deployed clients?

## Questions to practice

- [Design a payment system](https://sysround.com/questions/design-payment-system): Payment creation and refunds require explicit idempotency, asynchronous status, and honest handling of uncertain outcomes.
- [Design a ticket booking system like Ticketmaster](https://sysround.com/questions/design-ticketmaster): Seat holds and booking endpoints expose expiration, concurrent claims, and the difference between accepting a request and confirming a reservation.
- [Design Instagram](https://sysround.com/questions/design-instagram): Feed and interaction APIs require stable pagination, retry-safe mutations, and visibility checks.
- [Design Dropbox or Google Drive](https://sysround.com/questions/design-dropbox): Resumable uploads, conditional metadata updates, and change feeds require precise client-server contracts.
- [Design a control plane for a distributed database](https://sysround.com/questions/design-database-control-plane): Long-running provisioning and upgrade operations need durable acceptance, status resources, and well-defined retry behavior.

## FAQ

### What is asked in an API design interview?

Expect to turn a product workflow into endpoints, request and response schemas, and documented behavior. Follow-ups commonly cover authentication, authorization, pagination, retries, concurrency, asynchronous work, and backward compatibility. The important part is explaining how the system enforces the contract under failure.

### How is an API design interview different from a system design interview?

An API-focused interview emphasizes the client-visible contract: operations, data shapes, errors, and guarantees. A broader system design interview spends more time on architecture, storage, scaling, and failure recovery. They overlap because API guarantees must be supported by the underlying system.

### Should I use REST or GraphQL in an API design interview?

Choose based on client needs rather than treating either as the expected answer. REST is a practical starting point for resource-oriented workflows; GraphQL can help when clients need varied combinations of related data. State the trade-offs, including caching, authorization, query cost, and tooling, then focus on correctness.

## Related concepts

- [REST API design](https://sysround.com/concepts/rest-api-design)
- [Idempotency and idempotency keys](https://sysround.com/concepts/idempotency)
- [Pagination](https://sysround.com/concepts/pagination)
- [API versioning](https://sysround.com/concepts/api-versioning)
- [Access control and RBAC](https://sysround.com/concepts/access-control)


---
SysRound (https://sysround.com) is an AI practice tool for system design interviews: a voice or typed AI interviewer, a shared whiteboard, then feedback on every skill and a model answer. Its feedback is AI-generated and can be wrong.
