Backend· 5 min read

Sub-200ms p95: A Practical API Performance Checklist

A practical checklist for fast APIs: measuring p95 latency, indexes and N+1 queries, caching, payloads, external calls, background jobs and monitoring.

By

Full-Stack, Mobile & AI Engineer

Users don't experience your average response time. They experience the slow requests: the checkout that hangs, the feed that takes three seconds on a Monday morning. That's why serious teams track the 95th percentile (p95), the response time that 95% of requests beat, and why "our average is 80ms" can hide a product that feels slow.

This is a practical API performance checklist for getting p95 latency under 200 milliseconds and keeping it there: how to measure, where the time usually goes, and the fixes that matter most, roughly in the order they pay off.

Key takeaways

  • Measure p95 (and p99) per endpoint, not just averages, and watch them over time.
  • The database is usually the bottleneck: missing indexes, N+1 queries and fetching too much.
  • Keep slow work off the request path: send emails, call slow third parties and generate files in background jobs.
  • Cache deliberately, with clear invalidation, for data read far more often than it changes.
  • Monitor with traces so you can see which step of a slow request took the time.

Step 1: measure properly

  • Track latency percentiles per endpoint: p50, p95 and p99. Averages hide tail latency.
  • Use distributed tracing (for example OpenTelemetry) to see time spent in the database, cache and external calls within a request.
  • Log slow queries in the database (PostgreSQL can log statements above a threshold).
  • Measure from the client too: mobile network time is real, and payload size affects it.

Pick the endpoints that matter most (the app's home screen, search, checkout) and set targets for them.

Step 2: the database checklist

CheckSymptomFix
Missing indexSequential scans on large tablesAdd indexes that match filters and sort order (check with EXPLAIN ANALYZE)
N+1 queriesOne query per item in a listJoin, batch-load, or use the ORM's eager loading
Fetching too muchLarge rows, unused columnsSelect only needed columns; paginate
Unbounded queriesRequests that slow down as data growsAlways paginate and limit
Lock contentionSpikes during writesShorter transactions, avoid long-running locks
Connection limitsErrors or queueing under loadConnection pooling sized to the database

In multi-tenant systems, composite indexes usually start with tenant_id, because almost every query filters by it.

Step 3: keep slow work off the request path

A request should do what the user is waiting for, nothing more:

  • Background jobs for emails, push notifications, PDF generation, image processing and analytics events.
  • Webhooks processed asynchronously: acknowledge fast, then process.
  • Timeouts and fallbacks for third-party calls that must stay on the request path, so one slow provider doesn't slow everyone.

Step 4: cache what's read often

  • Cache computed or rarely changing data (catalogues, configuration, aggregated stats) in Redis or in memory, with a time-to-live and explicit invalidation on change.
  • Use HTTP caching headers (and a CDN) for public, cacheable responses.
  • Avoid caching per-user data unless it's expensive to compute and you can invalidate it correctly.

Every cache is a consistency decision. Document what each one stores and when it's invalidated.

Step 5: payloads and serialisation

  • Return only what the screen needs.
  • Compress responses.
  • Avoid serialising huge nested objects, which can cost more CPU than the query itself.
  • Paginate lists for mobile clients (20 to 50 items per page).
From the field: I've architected and shipped 15+ full-stack platforms on React, Next.js, Node.js / NestJS and Python (Django REST, FastAPI) with PostgreSQL, serving 50K+ daily API requests at sub-200ms p95 latency, with OpenTelemetry, Grafana and Sentry for observability. Earlier, at ADAffTech, I cut backend latency by about 35% through indexing and query refactoring. Most of the time, the biggest wins are in the database.

A worked example

Imagine a booking app whose "classes this week" endpoint has a p95 of 1.2 seconds. Tracing shows three things: the query filters by studio and date without a matching index, each class then loads its instructor in a separate query (an N+1 pattern), and the endpoint sends a confirmation email synchronously when called with a booking. Adding a composite index on studio and start time, eager-loading instructors, and moving the email to a background job typically brings an endpoint like this well under 200 milliseconds, without any new infrastructure. That's the pattern most API latency p95 problems follow: a few specific causes, each with an unglamorous fix.

Step 6: infrastructure

  • Place servers near your users and your database near your servers.
  • Scale horizontally for CPU-bound work, and size connection pools accordingly.
  • Watch cold starts on serverless platforms for latency-sensitive endpoints.
  • Use a CDN for static assets and media, so API servers only handle API work.

Step 7: keep it fast

Performance regresses quietly. Protect it:

  • Alert on p95 per key endpoint, not just error rates.
  • Load-test important flows before launches and big campaigns.
  • Review slow-query logs regularly, especially after releases.
  • Add performance budgets to code review for new endpoints on hot paths.

The checklist (save this)

  • p50, p95 and p99 tracked per key endpoint, with alerts.
  • Tracing in place to break down slow requests.
  • Slow-query logging enabled and reviewed.
  • Indexes match real filters and sorts; no sequential scans on big tables in hot paths.
  • No N+1 queries on list endpoints.
  • All list endpoints paginated and limited.
  • Emails, notifications, files and webhooks handled in background jobs.
  • Third-party calls on the request path have timeouts.
  • Caches documented with TTLs and invalidation rules.
  • Responses compressed and trimmed to what clients need.
  • Connection pooling configured; servers and database co-located.

FAQ

What does p95 latency mean?

It's the response time that 95% of requests are faster than. If an endpoint's p95 is 180 milliseconds, one request in twenty takes longer. It's a better measure of user experience than the average, which hides slow outliers.

What's a good API response time for a mobile app?

Aim for p95 under 200 to 300 milliseconds on your key endpoints, measured at the server, then account for mobile network time on top. Screens that need several requests should combine them or load progressively.

How do I find slow database queries?

Enable slow-query logging in your database, use EXPLAIN ANALYZE on suspicious queries, and use tracing to see which queries dominate slow requests. Look for sequential scans on large tables, N+1 patterns and queries returning far more data than needed.

Should I add caching to make my API faster?

Fix database queries and remove slow work from the request path first, because caching adds consistency risks. Then cache data that's read far more often than it changes, with clear expiry and invalidation rules.

Next steps

If your API feels slow, or you're preparing for a launch or a traffic spike, I can profile it, fix the biggest bottlenecks and set up monitoring that keeps it fast. Get in touch, or see how I work as a full-stack developer.

Sources

Bhuwanesh Sisodia

Bhuwanesh Sisodia

Full-Stack, Mobile & AI Engineer

Bhuwanesh Sisodia is a Full-Stack, Mobile & AI Engineer based in Mumbai, with 7+ years of experience and 50+ apps shipped to the App Store and Google Play. He works independently with startups and product teams worldwide.

About meGitHubLinkedInX

Related