Available — booking into Q4 2026 UK · Remote BST · GMT+1
All fixes
F/01 11 min read reviewed Sep 2026· Strapi 5

Why is your Strapi API slow?

TL;DR

In most cases it isn't Strapi. It's unbounded population. A request using populate=* or the populate=deep plugin fetches the entire content tree for every entry, so response times grow with your schema rather than with what the page actually needs. Before changing anything, confirm the slowness is in the API at all: request the endpoint directly and compare it against what your frontend is doing.

00· Before anything

First: prove it's the API

"Strapi is slow" is reported far more often than it's true. A page that takes four seconds to render might be one slow Strapi request, or twelve fast ones fired sequentially, or an unoptimised image, or a cold serverless function.

Request the endpoint on its own, away from the frontend:

bash
curl -w "\ntotal: %{time_total}s\n" -o /dev/null -s \
  "https://your-api/api/articles?populate=*"

Then run the same request with explicit population of only the fields that page needs. If the second is dramatically faster, you have a population problem and the rest of this page applies. If both are fast, the problem is in front of Strapi and you're looking in the wrong place.

Do this before you add Redis. Caching a query you should have fixed hides the problem until the cache misses.

Likely causes · Most → least common

bars = how often this is the answer

Cause 01 of 05

Unbounded population

Symptom

Response time scales with your schema rather than with page size. Adding an unrelated content type makes existing endpoints slower. Payloads are large and contain fields the frontend never reads.

How to confirm

Compare populate=* against explicit population on the same endpoint. Check the response size, not just the time: wc -c on the body is often more revealing.

Why it happens

populate=* fetches one level of every relation, component, media field and dynamic zone. The populate=deep community plugin goes further and removes the depth ceiling entirely, which means the cost of every request changes each time someone adds a relation, and nobody notices until it's in production.

The fix

Explicit population, scoped to what the page renders:

text
/api/articles?fields[0]=title&fields[1]=slug
  &populate[cover][fields][0]=url
  &populate[cover][fields][1]=alternativeText
  &populate[author][fields][0]=name

The Strapi 5 documentation recommends limiting population depth to two or three levels in production. Beyond that, question the content model rather than the query.

Two things worth knowing: fields has no effect on relational, media, component or dynamic zone fields. You scope those inside their own populate block. And large populate lists are bounded by the query parser's arrayLimit, which defaults to 100.

Where to put it

Don't leave population choices to the frontend. Route-level middleware centralises them, so query cost is a decision made once in the API rather than re-litigated in every component. I've written up the pattern in more detail in Building high-performance Strapi applications on strapi.io.

06

If it's none of these

Check the unglamorous things. Node 20 or later is a hard requirement for Strapi 5: schema.tables.toSorted is not a function is the canonical symptom of a Node version that's too old. Confirm your database isn't on a starved instance or exhausting its connection pool under load. And check whether slowness correlates with draft-and-publish status queries, which add cost that's easy to miss.

There are open issues worth reading if your case doesn't match anything here: #13288 on populate=* response times and #23168 on general API slowness both have useful discussion.

Common questions

Diagnosed everything and still stuck?

S/01 — Performance & Architecture Rescue.

A fixed-scope week that finds the cause and fixes what's causing it.