Key takeaways
- Throughput is the number of requests or transactions a system completes per unit of time, usually reported as requests per second.
- As load grows, throughput rises until something saturates. After that it flattens and response time climbs instead: that bend is your capacity.
- Throughput, concurrency and response time are linked. Report throughput together with response time percentiles, because either number alone can be made to look good.
Throughput in performance testing is the amount of work a system completes per unit of time, most often counted as requests per second (RPS) or transactions per second (TPS). Response time tells you how long one request takes; throughput tells you how much work the whole system gets through. Read together, they show whether a system is both fast and able to keep up with demand.
This post covers what throughput means in performance testing, how it differs from adjacent metrics (response time, latency, RPS, TPS, virtual users), how to measure it in JMeter and k6, what limits it, and how to read it in a test report.
The quick answer
Throughput = work completed ÷ time. Usually expressed as requests per second (RPS), transactions per second (TPS), or bytes per second for data-heavy workloads.
| Metric | What it measures | Typical unit |
|---|---|---|
| Throughput | How much work the system completes | requests/second, transactions/second, MB/second |
| Response time | How long an individual request takes, from sending it to receiving the full response | milliseconds |
| Latency | Time until the first part of the response arrives (in JMeter, effectively time to first byte) | milliseconds |
| Concurrent users (VUs) | How many simulated users are active at the same time | count |
| Error rate | Percentage of failed requests | % |
A system can have high throughput and high response time (lots of requests completing, each one slow), or low throughput and low response time (fast requests, but little traffic). The two are not independent, though: for a fixed number of concurrent users, slower responses mean fewer completed requests per second, as the formula below shows.
What is throughput in performance testing?
Throughput is a count divided by a time window. The most common form in web performance testing is requests per second (RPS): the number of HTTP requests the system completes in a second, averaged over the measurement window.
You can read throughput over different windows:
- Total throughput. Total requests ÷ total test duration.
- Sustained throughput. Average RPS during the steady-state part of the test (after ramp-up, before ramp-down).
- Peak throughput. The highest short window of the test, for example the busiest second.
- Throughput per endpoint. The same metric, broken down by which API endpoint or page handled the request.
The summary tables in JMeter and the end-of-test summary in k6 give you total throughput per request label. For a single URL, ApacheBench prints it as requests per second at the end of every run. Sustained and peak throughput come from a throughput-over-time chart. For capacity decisions sustained throughput is the one to use, because a peak can mislead: a 5-second burst at 5,000 RPS does not prove the system can hold 5,000 RPS for an hour.
Throughput vs response time vs latency
These three are easy to conflate. They measure different things:
- Throughput = how much the system completes in aggregate (per second).
- Response time = how long each individual request takes from send to receive (per request). See what response time means in performance testing for the full breakdown.
- Latency = how long until the response starts to arrive. In everyday networking it means network delay; in JMeter’s reports it runs from just before sending the request to just after the first part of the response arrives, so it includes server processing too.
How they relate:
- Throughput = concurrency ÷ response time. This is Little’s Law (concurrency = throughput × time in the system) rearranged. With 10 virtual users, no think time and 100 ms responses, the most those users can push is 10 ÷ 0.1 s = 100 RPS. If response time doubles to 200 ms under load, the same 10 users complete only 50 RPS. With think time between requests, use throughput = users ÷ (response time + think time). The relationship between throughput and response time has more worked cases.
- As you add load (more VUs), throughput grows roughly in proportion until a bottleneck appears. After that, extra VUs raise response time without raising throughput.
- Pure network delay usually stays the same as load grows, unless the network itself saturates. What climbs is time spent on the server: queueing, processing and GC pauses.
The shape of throughput against concurrency is the most useful graph in performance testing. A proportional region followed by a sharp elbow shows where the system hit its breakpoint.
RPS vs TPS vs VUs
These get used interchangeably and shouldn’t be:
- RPS (Requests Per Second). Number of HTTP requests the system completes per second. The lowest-level count: every GET, POST and asset load.
- TPS (Transactions Per Second). Number of business-level operations the system completes per second. A “checkout” transaction might involve five HTTP requests (auth, cart, payment, confirm, email trigger), so 10 TPS of checkouts is 50 RPS.
- VUs (Virtual Users / Concurrent Users). Number of simulated users active at the same time. This is not a throughput measurement at all: VUs are the load, RPS and TPS are the work that load produces. More on the difference in virtual users vs requests per second.
When someone says “we need to handle 1,000 users”, clarify: 1,000 concurrent users, or 1,000 transactions per second? They’re different problems with different capacity implications.
How much throughput one virtual user produces depends on its loop: requests per second per VU = 1 ÷ (response time + think time). A VU that waits 3 seconds between requests to a page answering in 200 ms makes about 1 ÷ 3.2 ≈ 0.3 RPS, so 1,000 such users produce roughly 310 RPS. A VU with no think time hitting a 100 ms endpoint makes about 10 RPS, and more if responses are faster, until the load generator itself runs out of CPU or connections.
What limits throughput
Throughput hits a ceiling when one of these resources saturates first:
- CPU on the application server. The most common bottleneck for compute-heavy workloads. Fix: scale horizontally (more nodes) or vertically (bigger instances).
- Database connection pool. Once the pool is exhausted, requests wait for a free connection even if the database still has capacity. Fix: size the pool to what the database can handle, and put a pooler such as PgBouncer in front of PostgreSQL when many app instances connect.
- Database CPU or I/O. When the database itself is the bottleneck. Fix: query optimisation, indexes, read replicas, sharding.
- Network bandwidth. Less common, but real for very high request rates or large payloads. Fix: larger network capacity, CDN offloading, response compression.
- Thread pool / worker pool. Common in synchronous frameworks. Fix: increase the pool size or switch to async I/O.
- External dependencies. Third-party APIs you call per request. Fix: caching, circuit breakers, async patterns.
- Memory pressure leading to GC pauses. Fix: heap tuning, leak fixes, more memory.
The point of a load test is to make this curve visible. The point of a capacity test is to find the maximum throughput the system can sustain while still meeting its response time targets.
How to measure throughput
In JMeter, the Aggregate Report and Summary Report show a Throughput column per sampler label, calculated as the number of samples divided by the time from the start of the first sample to the end of the last. The listener picks the unit (per second, per minute or per hour) so that the displayed number is at least 1.0, so check the unit before comparing runs. When results are saved to CSV, throughput is always in requests per second. Because that time window includes ramp-up and ramp-down, the figure comes out lower than the steady-state rate.
For throughput over time, generate JMeter’s HTML dashboard after a non-GUI run (jmeter -n -t test.jmx -l results.jtl -e -o report). It has Hits per second, Transactions per second and Response Time vs Request per second graphs. The Backend Listener can stream live metrics to InfluxDB or Graphite during the run.
In k6, every request is counted by the built-in http_reqs metric, a Counter. The end-of-test summary prints its total and its rate, for example http_reqs: 45 6.56/s, and that rate is your average throughput. To fail a run when throughput falls too low, set a threshold on the counter’s rate:
export const options = {
thresholds: {
http_reqs: ['rate>100'], // fail unless the average is above 100 requests per second
},
};
Server-side, your load balancer or APM tool also reports throughput. It is a useful cross-check of the load generator against what the system actually saw.
Reading throughput in a test report
Three patterns to look for:
1. Linear scaling region
VUs vs throughput plotted as a line that grows proportionally. Adding 10% more VUs gives about 10% more RPS. This is healthy. The system has headroom.
2. The elbow
A point where the line flattens. Adding more VUs no longer increases throughput. This is the breakpoint: the system has hit its capacity ceiling, and the next thing to climb is response time, because requests now queue. The response time vs virtual users guide shows how to spot this knee in a LoadFocus report.
3. The cliff
A point where throughput drops as you add load. The system is in a degraded state, usually because failures are consuming resources (timeouts holding connections, retry storms, GC pauses cascading). It rarely recovers until the load is removed.
What “good” throughput looks like
There’s no universal number. A cached static endpoint and an authenticated API that writes to a database can differ by orders of magnitude on the same hardware, so a throughput figure means little without the workload behind it.
The relevant question isn’t “is 200 RPS good?” but “does my system meet its targets at the load I forecast, with headroom?” If you forecast 100 RPS and p95 stays under 1 second at 200 RPS, you have margin. If you forecast 500 RPS and p95 is already 3 seconds at 400 RPS, you have a problem.
Throughput across multiple regions
A test run from one region measures your system through one network path. When your users are spread out, run the test from several regions at once: per-region results show differences that a single-region test hides, such as a slow CDN edge, per-region rate limits or a distant origin.
LoadFocus runs tests from 25 AWS regions and breaks response times, throughput and errors down by test location, so one slow region stands out.
To check whether adding servers actually raises the throughput ceiling, run the same stepped test before and after scaling out and compare where the elbow lands. That is scalability testing.
Frequently Asked Questions
What does “throughput” mean in software testing?
Throughput is the rate at which a system completes work, usually measured in requests per second (RPS) or transactions per second (TPS). It complements response time, which measures how long each individual unit of work takes.
Is throughput the same as RPS?
In web performance testing, throughput is most commonly expressed as RPS (requests per second). But throughput is the broader concept. It can be measured as transactions per second, bytes per second, or any work-per-time unit that fits the workload.
What’s the difference between throughput and latency?
Throughput is how much work the system completes per unit of time. Latency is how long a single request waits before its response starts to arrive. A system can serve many requests per second while each user still waits a long time for theirs.
What’s the difference between throughput and response time?
Throughput is the aggregate number of requests completed per second. Response time is the duration of each request. They are linked: for a fixed number of concurrent users, throughput equals users divided by response time (plus think time), so when response time rises, throughput from those users falls.
How do I increase throughput?
Find what saturates first, then work on that: scale out for CPU bottlenecks, tune connection pools for database connection exhaustion, add caching for read-heavy workloads, switch to async I/O for thread pool saturation, add memory or fix leaks for GC pressure. A stepped load test that pushes past the elbow shows which one it is.
What’s a good throughput number?
There’s no universal answer. The right question is whether throughput meets your targets at the load you forecast, with headroom. A cached static endpoint and a database-heavy API can differ by orders of magnitude and both be fine for their workloads.
How is throughput measured in JMeter?
The Aggregate Report and Summary Report show throughput per sampler label as samples divided by elapsed time, in requests per second, minute or hour (the unit is chosen so the number is at least 1.0). Saved to CSV, it is always requests per second. The HTML dashboard adds Hits per second and Transactions per second graphs over time.
How is throughput measured in k6?
k6 counts requests in the built-in http_reqs Counter. The end-of-test summary shows the total and the per-second rate, and a threshold such as http_reqs: ['rate>100'] fails the run if average throughput drops below 100 RPS. k6 can also stream metrics to outputs such as InfluxDB, Prometheus remote write, Datadog or OpenTelemetry.
Can throughput decrease as I add load?
Yes, and it’s a bad sign. Throughput falling as load rises means the system is in cascading failure: timeouts holding connections, retry storms, GC pauses growing. This is the cliff pattern, distinct from the more common elbow (throughput plateaus) and the linear region (throughput scales).
Bottom line
Throughput is the productivity metric in performance testing: how much work the system actually does per second. Pair it with response time percentiles (p50, p95, p99) to know both how busy the system is and how fast individual users are served.
For load tests that measure throughput at expected peak, LoadFocus runs JMeter and k6 scripts from 25 cloud regions, with results per location and per request. Pushing past expected peak until throughput stops growing is a stress test; the difference is covered in load testing vs stress testing.
If you’d rather hand off the throughput analysis, especially when capacity decisions depend on it, LoadFocus load testing services has engineers write the scripts, run the tests and write up the analysis.