You wrote an HTTP endpoint. It works when you hit it once from Postman. But what happens when 200 clients hit it at the same time? You need numbers: requests per second, latency percentiles, and how many responses came back with a non-2xx status. autocannon gives you those numbers from your terminal in about ten seconds.
autocannon is an HTTP/1.1 benchmarking tool written in Node.js. It fires a controlled flood of requests at a URL and reports throughput and latency. This guide walks you through install, a basic run, every flag you’ll actually use, reading the output, and driving autocannon from a Node script. It also draws a clear line between what a load test tells you and what it doesn’t, so you know when to reach for a functional and contract test instead.
What autocannon is
autocannon opens a fixed number of concurrent connections to a URL and keeps sending requests for a set duration (or a set request count). While it runs, it samples latency and counts responses. When it finishes, it prints a table of latency percentiles and a summary of total requests and bytes read.

It measures one thing: how much load your server handles and how fast it responds under that load. It does not check whether the response body is correct, whether your API matches its OpenAPI spec, or whether a multi-step workflow returns the right data at each step. Keep that distinction in mind. It shapes where autocannon fits in your testing stack, covered near the end.
If you’ve used wrk or Apache Bench, autocannon fills the same slot with a Node-native install and a programmatic API you can call from JavaScript.
Install
autocannon ships as an npm package. Install it globally to get the autocannon command anywhere:
npm i autocannon -g
You need Node.js installed first. If you’d rather not install globally, run it on demand with npx:
npx autocannon http://localhost:3000
Or add it to a project as a dev dependency when you plan to script it:
npm i autocannon --save-dev
Verify the install:
autocannon --version
Basic run
The simplest form is the command plus a URL. This runs the default benchmark: 10 connections for 10 seconds.
autocannon http://localhost:3000
Turn the dials with three flags you’ll use constantly. -c sets the number of concurrent connections, -d sets the duration in seconds, and -p sets pipelining (how many requests each connection sends before waiting for a response).
autocannon -c 100 -d 30 -p 10 http://localhost:3000
That command opens 100 connections, runs for 30 seconds, and pipelines 10 requests per connection. Higher connection and pipelining counts push more load, which is how you find the point where latency starts climbing.
To send a fixed number of requests instead of running for a duration, use -a (amount):
autocannon -c 10 -a 10000 http://localhost:3000
That stops after 10,000 requests regardless of time.
POST requests, headers, and a body
Change the method with -m, add headers with -H, and pass a request body with -b. Here’s a POST to a JSON endpoint:
autocannon -c 50 -d 20 \
-m POST \
-H 'Content-Type=application/json' \
-H 'Authorization=Bearer YOUR_TOKEN' \
-b '{"name":"load-test","active":true}' \
http://localhost:3000/api/users
Note the header format: -H 'Key=Value', and you repeat -H for each header. If your body is large or lives in a file, use -i to read it from disk instead of inlining it:
autocannon -m POST -H 'Content-Type=application/json' -i payload.json http://localhost:3000/api/users
Rate limiting the test
By default autocannon sends as fast as it can. Sometimes you want a steady, realistic rate rather than a maximum-pressure flood. -R caps total requests per second across all connections:
autocannon -c 50 -R 500 -d 60 http://localhost:3000
That holds the test at 500 requests per second for 60 seconds. This is useful when you want to measure latency at an expected production load rather than at the breaking point.
Warmup and worker threads
Two more flags help on heavier runs. -W (warmup) sends traffic for a short interval before autocannon starts sampling, so your first numbers aren’t skewed by cold caches or a JIT that hasn’t warmed up. -w (workers) spreads the load across multiple Node worker threads, which matters when a single thread can’t generate enough requests to saturate a fast server:
autocannon -c 200 -d 30 -w 4 http://localhost:3000
Reach for -w only when you’ve confirmed the load generator itself is the bottleneck. If latency looks suspiciously flat as you raise -c, your generator may be maxed out, and adding workers gives you a truer picture of the server’s ceiling.
Reading the results
When a run finishes, autocannon prints a latency table and a summary line. A trimmed example:
Running 10s test @ http://localhost:3000
10 connections
┌─────────┬──────┬──────┬───────┬──────┬─────────┬─────────┬──────────┐
│ Stat │ 2.5% │ 50% │ 97.5% │ 99% │ Avg │ Stdev │ Max │
├─────────┼──────┼──────┼───────┼──────┼─────────┼─────────┼──────────┤
│ Latency │ 0 ms │ 1 ms │ 4 ms │ 6 ms │ 1.2 ms │ 0.9 ms │ 24.1 ms │
└─────────┴──────┴──────┴───────┴──────┴─────────┴─────────┴──────────┘
251k requests in 10.05s, 27.9 MB read
Here’s how to read it:
- The percentile columns (2.5%, 50%, 97.5%, 99%) matter more than the average. The 50% column is the median. The 99% column tells you the latency your slowest 1 percent of users see. Tail latency is where real problems hide, so watch the 97.5% and 99% numbers.
- Avg and Stdev give you the mean and how spread out latencies are. A high standard deviation means inconsistent response times.
- The summary line (“251k requests in 10.05s”) is your throughput. Divide requests by seconds for requests per second.
Two flags make the output more useful. Add -l to print the full set of latency percentiles (including p99.9 and beyond), and add --renderStatusCodes to see a per-status-code breakdown so you can catch a wave of 500s hiding behind a good throughput number:
autocannon -c 100 -d 20 -l --renderStatusCodes http://localhost:3000
Watch the errors, timeouts, and non-2xx counts. A server can post a high request rate while quietly returning errors. If non-2xx is not zero, your throughput number is measuring failures, not success.
Programmatic use in a script
autocannon exposes a Node.js API, so you can run benchmarks from a script and act on the results. This is where it earns its keep in automation: run a test, read the numbers, and fail a build if latency crosses a threshold.
The core call takes an options object and returns a promise:
const autocannon = require('autocannon')
async function run() {
const result = await autocannon({
url: 'http://localhost:3000',
connections: 100,
duration: 20,
pipelining: 1
})
console.log(`Avg latency: ${result.latency.average} ms`)
console.log(`Req/sec: ${result.requests.average}`)
console.log(`Non-2xx: ${result.non2xx}`)
}
run()
The result object carries histograms for latency, requests, and throughput, each with average, min, max, and percentile fields like p99. It also carries errors, timeouts, and non2xx counts.
To turn that into a gate, add a check that exits non-zero when a budget is blown:
const autocannon = require('autocannon')
const P99_BUDGET_MS = 250
async function run() {
const result = await autocannon({
url: 'http://localhost:3000/api/health',
connections: 100,
duration: 30
})
const p99 = result.latency.p99
console.log(`p99 latency: ${p99} ms (budget ${P99_BUDGET_MS} ms)`)
if (p99 > P99_BUDGET_MS || result.non2xx > 0) {
console.error('Performance budget exceeded')
process.exit(1)
}
}
run()
If you want the live progress bar and results table that the CLI shows, pass the instance to autocannon.track:
const autocannon = require('autocannon')
const instance = autocannon({
url: 'http://localhost:3000',
connections: 10,
duration: 10
}, console.log)
autocannon.track(instance, { renderProgressBar: true })
process.once('SIGINT', () => instance.stop())
For a multi-request scenario, pass a requests array so each connection cycles through several calls:
autocannon({
url: 'http://localhost:3000',
connections: 20,
duration: 15,
requests: [
{ method: 'GET', path: '/api/users' },
{ method: 'POST', path: '/api/data', body: '{"x":1}',
headers: { 'Content-Type': 'application/json' } }
]
}, console.log)
autocannon vs wrk and ab
All three answer the same question (how fast, under how much load), and the right pick depends on your stack.
- Apache Bench (ab) is the classic single-binary tool. It’s everywhere and simple, but single-threaded and dated.
- wrk is fast and can drive heavy load with Lua scripting for custom requests. It’s a compiled C tool, so you install it outside npm.
- autocannon matches wrk on throughput for most workloads and wins on ergonomics if you already live in Node:
npm ito install, a JavaScript API to script, and support for pipelining, HAR files, and per-request scenarios out of the box.
If Node is your runtime, autocannon is the low-friction choice. Prefer Python tooling? See how to load-test without Python. Want a scriptable option with a large feature set? Compare k6 load testing.
Where functional testing and Apidog fit
autocannon tells you your endpoint serves 12,000 requests per second at a p99 of 40 ms. It does not tell you the endpoint returns the right data. A load test can pass with flying colors while the API returns malformed JSON, ignores an auth header, or drifts from its OpenAPI contract. Throughput is not correctness.
That’s the gap functional and contract testing fills, and it’s where Apidog complements a load tool rather than replacing one. Apidog is not a load generator. It runs saved test scenarios that assert on status codes, response schemas, and values across multi-step flows, so you catch the bugs a benchmark can’t see.
You run both in CI, and they answer different questions. Use autocannon (or wrk) to answer “is it fast enough under load?” Use the Apidog CLI to answer “is it correct?” The Apidog CLI is headless and runs saved test scenarios or suites from any CI step that has Node:
npm install -g apidog-cli
apidog run \
--access-token "$APIDOG_ACCESS_TOKEN" \
-t <scenarioOrSuiteId> \
-e <environmentId> \
-r cli,html,junit
The -t flag targets a saved scenario, folder, or suite by id, -e selects the environment, and -r picks one or more reporters (cli, html, json, junit) so the run produces artifacts your pipeline can archive. For a full walkthrough, see how to run API tests from the Apidog CLI, the copy-paste CI/CD pipeline, and the GitHub Actions workflow.
A healthy pipeline runs functional and contract checks on every push (does it work?), then runs a load test before release (does it hold up?). autocannon owns the second question. Apidog owns the first.
FAQ
Is autocannon accurate for production load testing?
autocannon produces reliable throughput and latency numbers for HTTP/1.1 endpoints, and when you set a target rate with -R it corrects for coordinated omission, a step many simpler tools skip. For accurate results, run it from a machine close to your server (network latency dominates otherwise) and use enough connections to saturate the endpoint. Run it against a staging environment that mirrors production, not against your laptop’s dev server.
Does autocannon support HTTP/2 or WebSockets?
No. autocannon benchmarks HTTP/1.1. For HTTP/2 or WebSocket load testing you need a different tool. This is the main constraint to check before you pick it.
How many connections should I use?
Start at the default of 10, then increase -c until requests per second stops rising and latency starts climbing. That inflection point is roughly your server’s capacity. Pushing far past it measures your load generator’s limits more than your server’s.
Can I run autocannon in CI?
Yes. The programmatic API is built for it: run a benchmark, read result.latency.p99 and result.non2xx, and call process.exit(1) when a budget is exceeded. That turns a benchmark into a pass/fail gate you can wire into any Node-capable CI step.
What’s the difference between -a and -d?
-d runs for a number of seconds. -a runs until a number of requests completes, then stops. Use -d for a steady-state load test and -a when you want to send an exact request count.



