Next Practical Step
Build your first HTTP flow with Building a Web Test Plan, then parameterize hosts and threads with \${__P(...)} for CLI runs.
Complete JMeter API load testing guide: HTTP Request setup, headers and auth, CSV parameterization, correlation, assertions, CLI runs, and dashboards.
Apache JMeter is widely used for API load testing because the same HTTP Request sampler that drives browser-style web tests also drives REST, SOAP, and many JSON/XML backends. This guide walks through building an API-focused plan: install and first run, sampler configuration, headers and auth, parameterization, correlation, assertions, load sizing, CLI execution, reporting, and common failure modes.
It is grounded in JMeter’s documented behaviour for HTTP sampling, variables, best practices, and dashboard reporting. For full field lists, use the Component Reference and Functions and Variables pages.
In JMeter, an API test is still a test plan: one or more Thread Groups, samplers that issue protocol requests, config elements (defaults, headers, cookies), post-processors that extract data, assertions that mark samples as pass/fail, and listeners or CLI result files for analysis.
For HTTP APIs you typically:
Content-Type, Accept, and auth headers.JMeter does not execute client-side JavaScript in a real browser. It measures the server-facing protocol exchange (requests and responses). That is usually what you want for API performance, but it is different from browser UX timing.
If you are new to JMeter:
A maintainable API plan often looks like this tree:
Test Plan├── User Defined Variables (optional)├── HTTP Request Defaults├── HTTP Header Manager├── HTTP Cookie Manager (if cookie-based sessions)└── Thread Group ├── Login (HTTP Request) + extractors + assertions ├── Business APIs (HTTP Requests) + extractors + assertions └── (optional) CSV Data Set Config for user/data rowsPrefer one sampler reused in a loop with variables over dozens of nearly identical samplers. Official best practices call this out as a way to cut resource use and keep plans maintainable.
The HTTP Request sampler is the core element for REST and most HTTP APIs.
| Area | Typical API use |
|---|---|
| Protocol / Server / Port | Prefer HTTP Request Defaults so every sampler inherits host and TLS settings |
| Method | GET, POST, PUT, PATCH, DELETE, HEAD, OPTIONS as needed |
| Path | Endpoint path, often with variables: /v1/orders/\${orderId} |
| Parameters | Query or form parameters (depending on method and encoding) |
| Body Data | Raw JSON, XML, or other payload for POST/PUT/PATCH |
| Content encoding | Character encoding for the request body when relevant |
For JSON APIs, put the payload in Body Data and set Content-Type: application/json on the Header Manager (or a child Header Manager scoped to that sampler). Using form parameters when the API expects a JSON body is a common source of 400 responses.
Example body with variables:
{ "email": "\${email}", "quantity": \${quantity}, "requestId": "\${__UUID}"}Add HTTP Request Defaults under the Test Plan or Thread Group and set:
https for most public APIs)Samplers then only override path, method, and body. That matches the pattern used in the web test plan tutorial.
Use an HTTP Header Manager for:
Content-Type: application/jsonAccept: application/jsonAuthorization: Bearer \${accessToken} after loginX-Request-Id, API keys, tenant IDs)Scope matters:
Content-Type).For advanced web scenarios that customize User-Agent or similar headers, see Building an Advanced Web Test Plan.
If the API uses session cookies (or a BFF that sets cookies), add an HTTP Cookie Manager. Place it where the web tutorial recommends so each thread keeps its own cookie jar. Without it, multi-step flows that rely on Set-Cookie will fail after the first request.
Bearer / JWT after login
/oauth/token or /login.access_token (JSON Extractor or Regular Expression Extractor).Authorization to Bearer \${accessToken}.API key header
\${__P(apiKey,)}) and inject it as a header. Prefer properties for secrets passed on the CLI so they are not hard-coded in the .jmx.Basic auth
Authorization header deliberately. See also the advanced web plan notes on where to put the Authorization Manager.SOAP
SOAPAction, Content-Type). The same pattern applies to modern REST JSON APIs.If you already have working curl commands from developers or Postman exports, use JMeter’s cURL import to create HTTP samplers quickly, then clean up defaults, variables, and assertions.
Hard-coded credentials and IDs do not scale. Grounded options from the manual:
From best practices:
USER, PASS).\${USER} and \${PASS} on samplers.Use CSV for product IDs, search terms, and tenant names the same way. Pre-generate large data files instead of inventing random data in expensive scripts during the run when possible.
See the dedicated Functions and Variables topic and the full functions reference. Frequently used in API tests:
| Need | Example |
|---|---|
| Unique id | \${__UUID} |
| Random int | \${__Random(1,1000)} |
| Timestamp | \${__time(yyyy-MM-dd'T'HH:mm:ss)} (escape commas in formats when needed) |
| Thread number | \${__threadNum} |
| Property override | \${__P(threads,10)} |
Define Thread Group or host settings from properties so CLI and CI can override without editing XML:
threads = \${__P(threads,10)}rampup = \${__P(rampup,30)}host = \${__P(host,api.example.com)}Run:
jmeter -n -t api-plan.jmx -Jthreads=50 -Jrampup=60 -Jhost=staging.example.com -l results.jtl -e -o report/Official guidance: use __P (or __property) for values that must be global or overridden from the command line; use variables for thread-local state. For many properties together, pass a property file with -q.
Most real APIs require values from response A in request B (login token, cart id, ETag, CSRF).
For JSON bodies, prefer JSON Extractor or JMESPath-style extraction when available on your JMeter version/plugins stack over brittle full-body regular expressions. Use Regular Expression Extractor for free-text tokens, HTML fragments, or headers when needed.
Typical flow:
{"access_token":"...","expires_in":3600}.accessToken.Bearer \${accessToken}.orderId; next GET uses /orders/\${orderId}.Always set a Default Value on extractors (for example NOT_FOUND) so missing tokens are obvious in View Results Tree while debugging. Pair extractors with assertions so a failed login does not silently produce thousands of 401s.
For crafting regex correlation fields quickly, use the site’s Regex Extractor Builder (browser-only paste analysis), then paste Name, Regular Expression, Template ($1$), Match No., and Default into the extractor under the sampler.
Without assertions, JMeter may report “green” throughput while every response is an error page or empty body. Common choices:
200, or substring/contains checks on body.Official best practices advise using as few assertions as possible under load because each assertion costs CPU. Debug with richer checks; keep a minimal set for the load run (status code + one critical field is a common compromise).
API load is rarely a single GET in a loop. Useful logic controllers (see test plan elements and component reference):
\${accessToken} / response variables.Timers (constant, uniform random, precise throughput) model think time and pacing. Without think time, threads hammer the API as fast as responses return - which is valid for stress tests but not for “N concurrent users browsing.”
From best practices: thread count depends on injector hardware, plan design, and how fast the server responds. Undersizing threads relative to target rate contributes to coordinated omission (inflated performance picture when the client cannot keep issuing requests on schedule).
Practical workflow:
Also use the Heap Estimator and Coordinated Omission tools when results look “too good” or injectors struggle.
Documented resource advice:
jmeter -n -t api-plan.jmx -l results.jtl -e -o report/| Flag | Meaning |
|---|---|
-n | Non-GUI mode |
-t | Test plan path |
-l | Sample results file (prefer CSV saveservice defaults) |
-e | Generate HTML dashboard after the run |
-o | Output folder for the dashboard (must be empty/new) |
-Jname=value | Set JMeter property for \${__P(name)} |
Further best practices for lean runs:
-l you can disable them all.vars.get("x") instead of \${x} inside cached scripts.The dashboard generator reads CSV results and produces:
user.properties)Generate at end of test with -e -o, or offline from an existing .jtl.
Listeners such as View Results Tree and Summary Report help while building the plan. They are not a substitute for the dashboard under load.
For live graphs during long API tests, use the Backend Listener to stream metrics to InfluxDB/Grafana (or other backends supported by your setup). That complements, not replaces, the post-run dashboard.
API plans belong in the pipeline:
.jmx and CSV data next to application code.results.jtl and the report/ folder.Details and examples: CI/CD Load Testing. Parameterize hosts and thread counts with -J / \${__P(...)} so the same plan hits staging nightly and a smaller gate on every PR.
| Style | JMeter approach |
|---|---|
| REST JSON | HTTP Request + Header Manager + JSON extractors/assertions |
| SOAP/XML | HTTP Request + XML body + SOAP-related headers (WS plan) |
| GraphQL | HTTP POST to /graphql with JSON body (query / variables); treat like REST |
| Multipart upload | HTTP Request file upload fields (see component reference) |
| WebSocket / gRPC | Not core HTTP; requires plugins or other tools - plan protocol support explicitly |
JMeter’s strength is protocol breadth (HTTP plus JDBC, JMS, LDAP, FTP, and more in-box). Stay on HTTP Request when the system under test is an HTTP API unless you have a clear need for another sampler.
| Mistake | What goes wrong | Fix |
|---|---|---|
| Load test in GUI with View Results Tree | Injector CPU/memory distort results | CLI + -l; listeners off |
| No assertions | Errors counted as successful samples | Assert status + critical fields |
| Hard-coded tokens | Fail after expiry; not multi-user | Extract per thread; CSV users |
| All threads same user | Unrealistic cache/locking behaviour | CSV Data Set unique logins |
| Zero ramp-up to high threads | Spike, not soak; noisy errors | Ramp over minutes |
| Functional mode / saving full bodies | Huge files, slow I/O | CSV, minimal saveservice fields |
| BeanShell/JavaScript for hot paths | High CPU, poor scalability | JSR223 Groovy, compiled cache |
| Undersized threads for target RPS | Coordinated omission | Thread calculator + pilot |
Secrets committed in .jmx | Credential leaks | \${__P(secret,)} / CI secrets |
\${__P(...)}.jmeter -n -t … -l … -e -o ….-Xmx guidanceYes. Use the HTTP Request sampler with the appropriate method, path, headers, and body. REST is not a separate sampler type; it is HTTP with JSON/XML payloads and status conventions.
No. JMeter does not render pages or run browser JavaScript. It records protocol-level timings for the requests it sends. Client-side rendering cost is out of scope unless you use a real-browser tool alongside JMeter.
You can record browser or proxy traffic, then delete static assets and parameterize. Many API teams prefer building HTTP samplers directly or importing cURL. If you record, follow the recorder guide and best practices on include/exclude patterns.
Extract the token from the login response into a variable, then set header Authorization to Bearer \${token} via Header Manager. Keep the token thread-local unless you intentionally share via properties.
It depends on target throughput, response time, think time, and injector capacity. Start from RPS × service time, validate with a pilot, and use the Thread Calculator. Official docs stress correct sizing to avoid coordinated omission.
Common causes: server saturation (response time rose), injector limits, listeners left on, think time/timers, assertion cost, connection limits, or too few threads. Compare achieved hits/s on the dashboard with active threads and error rate.
Non-GUI. Official best practices recommend CLI mode for load and GUI for building/debugging.
On this page