How to load-test a Django API before launch

· 6 min read · Epochs engineering team

Most performance problems are found by customers, on the busiest day of the year. Load testing moves that discovery to a quiet afternoon before launch. Here is the approach we use for Django and Django REST Framework APIs.

1. Decide what “fast enough” means

Before writing a single test, agree on targets: the response time most requests must stay under, the error rate you can accept, and the traffic you expect at peak. Without targets, a load test produces numbers but no decisions.

Use real data where you have it. Analytics, server logs or the previous peak season tell you which endpoints matter and how many requests per second they really receive.

2. Test against something production-like

Results from a laptop or an empty database are misleading. Run tests against a staging environment with the same instance sizes, the same database engine and a realistic volume of data. A query that takes 5 ms on 1,000 rows can take seconds on 10 million.

3. Write scenarios, not single requests

Real users log in, browse, search, filter and submit forms. Model those journeys and weight them by how often they happen. Tools such as k6, Locust or JMeter all support scripted scenarios with think time between steps.

  • Authenticate the way real clients do, including token refresh.
  • Mix read-heavy and write-heavy journeys in realistic proportions.
  • Vary the data each virtual user requests, so caches are not flattering every result.

4. Ramp up and find the first bottleneck

Increase load in steps and watch response times, error rates and server metrics together. The goal of a stress test is to find the first component that gives up — and why.

In Django projects the usual suspects are N+1 queries from missing select_related or prefetch_related, missing database indexes, expensive serializers, synchronous calls to slow third-party APIs and an exhausted database connection pool.

5. Fix in order of impact, then re-test

Change one thing at a time and re-run the same scenario, so every improvement is measured rather than assumed. Database indexes and query fixes are often the cheapest wins; caching and moving slow work to background queues come next; bigger servers come last.

6. Keep it fixed

A one-off test only protects one release. Add a short performance check to your CI pipeline for the most important endpoints, and fail the build when they slow down beyond an agreed threshold. That way the bottleneck you fixed cannot quietly return.