Slow API: identify what is slowing down your response times and optimise it
A slow API is felt everywhere: a mobile app showing endless loading indicators, a web interface that freezes, partner integrations that time out, infrastructure costs that climb because each request ties up resources for too long. The problem is often gradual and only becomes obvious as data or traffic grows.
The causes are rarely in the framework itself. They lie in database access (N+1 queries, missing indexes, unoptimised queries), in response size, in the absence of caching, in synchronous calls to external services, in serverless cold starts or in badly sized connection pools.
This page describes how to measure precisely where the time goes, what the most frequent causes are depending on the architecture (monolith, microservices, serverless) and the optimisations to apply, from the SQL index to HTTP caching.
Typical symptoms
- Response times at the 95th or 99th percentile are much higher than the median: most requests are fast, but some are very slow.
- Response times grow with data size (lists, histories, dashboards) or with the number of concurrent users.
- Clients receive timeout errors (timeout, 504) at peak hours.
- Some endpoints are slow while similar ones respond instantly.
- The first request after a period of inactivity is very slow (cold start), then the following ones are fast.
- The CPU or the database is saturated even though traffic is moderate.
Possible causes
N+1 queries
The ORM loads a list then runs an additional query for each item (author, category, price). A page of one hundred items generates one hundred and one queries instead of two. It is the most frequent cause on Laravel, Django, Rails, Spring Data, Prisma or TypeORM.
Missing or unsuitable indexes
A WHERE, JOIN or ORDER BY clause on an unindexed column forces the database to scan the whole table. The problem goes unnoticed with little data and explodes with growth.
Oversized payloads
Responses returning every field and every relation, without pagination or field selection, base64-encoded images, multi-megabyte JSON serialised on every call.
No caching
The same reference data (catalogue, configuration, permissions) is recomputed on every request even though it rarely changes. No application cache (Redis), no HTTP caching (ETag, Cache-Control, CDN).
Synchronous external calls
Each request waits for a third-party service (payment, geocoding, email, another microservice) without a short timeout or parallelisation. The API latency becomes the sum of everyone else's latency.
Cold starts and connection pools
Serverless functions (AWS Lambda, Cloud Functions) initialising everything on each call, a connection pool too small that makes requests wait, or too large that saturates the database.
Heavy tasks processed synchronously
PDF generation, email sending, image resizing or exports executed inside the HTTP request instead of being handed to a queue.
Checks to perform
- 1
Measure per endpoint
An APM (Datadog, New Relic, Elastic APM, OpenTelemetry with Grafana Tempo or Jaeger) shows how time is split between code, database and external calls for each route, with percentiles. Without an APM, access logs with response time (Nginx $request_time) give a first view.
- 2
Count SQL queries per call
Enable the ORM query log (Laravel Debugbar, Django Debug Toolbar, Hibernate show_sql, Prisma query logging) on a test environment: a query count that grows with the list size signals an N+1.
- 3
Enable the slow query log
MySQL: slow_query_log with a low long_query_time; PostgreSQL: log_min_duration_statement or the pg_stat_statements extension, which ranks queries by total time. You get the list of queries to optimise first.
- 4
Analyse execution plans
EXPLAIN (MySQL) or EXPLAIN ANALYZE (PostgreSQL) on slow queries: a "Seq Scan" or "type: ALL" on a large table indicates a missing index; very high "rows" values, a badly filtered join.
- 5
Measure response size
curl -s -o /dev/null -w '%{size_download} %{time_starttransfer} %{time_total}' https://api.example.com/route shows the weight and timings. Compare with what the client actually needs.
- 6
Trace external calls
In the APM or with timestamped logs around each outgoing HTTP call, measure the latency of each dependency and verify that a timeout is configured.
- 7
Check pools and cold starts
Watch the database connection metrics (pg_stat_activity, Threads_connected) and the initialisation time of serverless functions (init duration in CloudWatch). Compare the latency of the first request and the following ones.
Solutions
Eliminate N+1 queries
Eager loading of relations (with() on Laravel, select_related and prefetch_related on Django, JOIN FETCH or EntityGraph on JPA, include on Prisma), or hand-written aggregated queries for complex cases.
Add the right indexes
Indexes on filtered, joined and sorted columns, composite indexes in the right order, partial or covering indexes on PostgreSQL, then execution plan verification after creation.
Slim down responses
Systematic pagination (cursor-based for large lists), field selection, dedicated endpoints per use case, Gzip or Brotli compression, and binary files served from object storage.
Cache intelligently
Redis cache for reference data and expensive computations with targeted invalidation, HTTP headers (Cache-Control, ETag) for public responses, a CDN in front of read endpoints.
Decouple external calls and heavy tasks
Short timeouts, parallel calls when they are independent, circuit breakers, and queues (SQS, RabbitMQ, Redis, Sidekiq, BullMQ) for anything that does not need an immediate response.
Size the infrastructure
Connection pool matched to the number of instances, PgBouncer or RDS Proxy in front of PostgreSQL, provisioned concurrency or warm instances for serverless, autoscaling on the relevant metrics.
When should you call a professional?
- You have no APM or per-endpoint metrics and do not know where to start.
- The slow queries involve complex joins, aggregations or a data model you cannot change on your own.
- The API feeds a mobile app or partners and the slowness generates complaints or service-level breaches.
- You are preparing for a traffic increase (launch, campaign, major new customer) and want guarantees.
- The standard optimisations have been done and times remain high: the architecture (decomposition, distributed cache, database) needs examining.
How Agencei can help
- 1
Observability set-up
Instrumentation with OpenTelemetry or an APM, per-endpoint dashboards with percentiles, slow query log and tracing of external calls.
- 2
Performance audit
Analysis of the slowest and most called routes, of SQL queries and their execution plans, of serialisation, caching and infrastructure.
- 3
Optimisation
N+1 fixes, index creation, rewrite of critical queries, pagination, application and HTTP caching, heavy tasks made asynchronous, pool tuning.
- 4
Load testing
Realistic scenarios with k6, Gatling or Locust before and after optimisation to validate the gains and know the real capacity.
- 5
Handover and follow-up
Report with measurements, changes made and recommendations, response time alerts, and support for your team to maintain good practices.
Frequently asked questions
How do I know whether my API has an N+1 problem?
Count the SQL queries executed for a call that returns a list. If that number grows with the number of items returned, it is an N+1. Framework debug bars and APMs show it immediately.
Will adding servers solve the slowness?
Only if the bottleneck is application CPU. If the database is saturated by unoptimised queries, more instances make the problem worse. Measurement must come before scaling.
Should I move to microservices or GraphQL to be faster?
Neither makes an API faster by nature. Microservices add network latency; GraphQL can generate N+1 queries if not paired with DataLoader. Most gains come from queries, indexes and caching.
What response time should I aim for?
It depends on the use: an API called on every interaction of an interface should respond within a few tens of milliseconds, an export or a report can take longer and be processed asynchronously. What matters is measuring at high percentiles, not just on average.
How long does an API optimisation take?
Instrumentation and the audit often take a few days. Fixes range from a few hours for indexes and N+1 queries to several weeks if the architecture or the data model must evolve.
Related services
API and backend
REST and GraphQL APIs, Node.js, Spring Boot and Python backend services, documented and secured.
See this servicePerformance Optimization
Profiling-based diagnosis and targeted fixes: Core Web Vitals, SQL queries, caching, CDN.
See this serviceTechnical Audit
Independent review of code, architecture and infrastructure, with a prioritised report.
See this serviceDatabase services
Design, optimization, administration and backups for your PostgreSQL, MySQL, MongoDB and Redis databases.
See this servicePostgreSQL expertise
Schema design, optimization, replication, version upgrades and migration to PostgreSQL.
See this serviceTell us about your project
Describe your need in a few lines: we come back to you with a first analysis and the next steps.