Improving API Performance

Pagination, caching, compression, async processing and connection pooling.

Intermediate8 min read3 interview questions

A slow API is usually slow for a small number of reasons: too much data, too many round trips, waiting on something slow, or doing work the caller didn’t need to wait for.

Measure with tracing first, then apply the techniques that match the bottleneck you actually found.

Diagram

small pages202 AcceptedClientAPIgzip · pagedCachehot readsConnection poolDBQueueslow work
Serve hot data from cache, reuse connections, page and compress responses, and queue slow work.

Key concepts

Pagination
Return results in pages (limit plus a cursor) instead of thousands of rows.
Caching
Cache frequent reads in memory, Redis or a CDN, with ETag for conditional requests.
Payload compression
gzip or Brotli shrink JSON by 70–90%, reducing transfer time.
Connection pooling
Reuse open database and HTTP connections to skip setup cost on every call.
Async processing
Return 202 Accepted and process long jobs in the background, with a status endpoint or webhook.
Async logging
Write logs to an in-memory buffer and flush in the background.

Trade-offs

Benefits

  • Faster responses and better user experience
  • Lower infrastructure cost per request
  • More capacity before scaling out

Costs

  • Caching and async flows add consistency questions
  • Compression costs CPU for tiny payloads

Use it when

  • List endpoints, dashboards, search, reports
  • Any endpoint with p95 above its target

Common mistakes

  • Returning entire tables because “the UI filters it”
  • Creating a new HttpClient per request and exhausting sockets
  • Doing email sending or PDF generation inside the request

Interview questions

Q1Cursor vs offset pagination?
Offset (page=50) is simple but slow for deep pages and unstable when rows are inserted. A cursor (last seen ID or timestamp) seeks directly and stays stable.
Q2How does ETag improve performance?
The server returns a hash of the resource. The client sends it back in If-None-Match; if unchanged, the server replies 304 Not Modified with no body.
Q3A report endpoint takes 40 seconds. How would you redesign it?
Accept the request with 202, run the report in a background worker from a queue, store the result, and let the client poll a status URL or receive a webhook.