Key takeaways
- Fetch the token once, in a setUp Thread Group, and share it through a JMeter property, so the timed requests measure the API and not the authorization server.
- For a load test, use the client credentials grant with a dedicated test client. The password grant that older tutorials (including the screenshots below) use is now forbidden by the OAuth security best practice, RFC 9700.
- Extract the token with a JSON Extractor, set it with a JSR223 (Groovy) element rather than BeanShell, and send it in an HTTP Header Manager as
Authorization: Bearer <token>. - Tokens expire. Pick a token lifetime longer than the test, or refresh on purpose and keep the refresh out of the timed samples.
To load test an OAuth 2.0 secured API in JMeter, request an access token from the token endpoint with one HTTP Request, pull access_token out of the JSON response, store it where every thread can read it, and add it to each API call as a Bearer header. The steps below use Apache JMeter 5.6.3 (the current release, which needs Java 8 or newer).
How does OAuth 2.0 change a load test?
The API you want to measure no longer receives plain requests. Every call must carry an access token that the authorization server issued, and issuing tokens is work the server does too. If every virtual user requests its own token before every call, you are partly load testing the authorization server, and its response times get mixed into your API numbers. Decide which of the two you want to measure, and build the test plan for that one.

The authorization code flow: the application gets a grant, trades it for an access token, then calls the resource server with it.
Which grant to script depends on who calls the API in production (RFC 6749 defines them):
| Grant | Typical caller | Fit for a load test |
|---|---|---|
| Client credentials | A backend service calling an API | Best fit: one POST to the token endpoint, no user and no browser |
| Authorization code (with PKCE, RFC 7636) | Web, mobile and single-page apps acting for a user | Needs a user login and a redirect, so generate tokens ahead of the test (see below) |
| Resource owner password | Legacy apps that collect the password themselves | Do not build new tests on it. RFC 9700 (January 2025) says it “MUST NOT be used” |
| Implicit | Old browser apps | Do not use. RFC 9700 says clients “SHOULD NOT” use it |
OAuth 2.1 would fold these rules into one document (PKCE for every authorization code flow, no implicit and no password grant), but it is still an Internet-Draft at the IETF (draft-ietf-oauth-v2-1, revision 16, September 2026), not an RFC. RFC 9700 is the published reference today.
How do I get an OAuth 2.0 token in JMeter?
Build the test plan in this order. JMeter’s own docs say setUp Thread Group threads “execute before the test proceeds to the executing of regular Thread Groups”, which is what makes it the right place for the token request. Regular thread groups run independently of each other, so a second group does not wait for the first.
Test Plan
setUp Thread Group (1 thread, 1 loop)
HTTP Request "Get token"
HTTP Header Manager
JSON Extractor
JSR223 PostProcessor
Thread Group "Load" (your users)
HTTP Header Manager (Authorization: Bearer ${__P(token)})
HTTP Request "GET /your-secured-endpoint"
Step 1: Request the token with an HTTP Request
Add a setUp Thread Group (Add > Threads (Users) > setUp Thread Group) with one thread and one loop, and an HTTP Request inside it. For the client credentials grant, the token endpoint expects a form-encoded POST (RFC 6749, section 4.4):
POST /oauth/token HTTP/1.1
Host: your-auth-server.example
Content-Type: application/x-www-form-urlencoded
Authorization: Basic <base64 of client_id:client_secret>
grant_type=client_credentials&scope=your-scope
In JMeter that is method POST, your token path, and grant_type and scope as parameters on the Parameters tab (an HTTP Header Manager under the sampler sets Content-Type: application/x-www-form-urlencoded). RFC 6749 (section 2.3.1) makes HTTP Basic the way a client authenticates to the token endpoint and allows the id and secret in the body only as a fallback it does not recommend, so use Basic unless your server only accepts the body form. Use a dedicated test client and keep the secret in a user-defined variable or a property passed with -J, not typed into the plan.

The token request in an older plan, screenshot from JMeter 5.3. It sends a JSON body with grant_type: password, which many servers still accept but which RFC 9700 now prohibits. Use client credentials, as above, for new tests.
Step 2: Extract the access token with a JSON Extractor
Under the token request, add Add > Post Processors > JSON Extractor. Set Names of created variables to access_token;expires_in and JSON Path expressions to $.access_token;$.expires_in. JMeter’s reference says both lists are semicolon separated and must have the same number of entries. Fill Default Values with TOKEN_NOT_FOUND;0 so a failed token request shows up as an obvious bad value instead of an empty header.
Step 3: Share the token with a JSR223 PostProcessor
Variables belong to one thread; JMeter properties are global and, in the manual’s words, “can be used to pass information between threads”. Add Add > Post Processors > JSR223 PostProcessor, set Language to groovy, and use:
props.put("token", vars.get("access_token"))
Older tutorials, including the first version of this one, use a BeanShell PostProcessor for this. JMeter’s documentation recommends moving from BeanShell to JSR223 with Groovy, for performance and because the BeanShell library has limited maintenance. The Groovy line does the same job.
Step 4: Send the token in an HTTP Header Manager
In the regular Thread Group that carries the load, add Add > Config Element > HTTP Header Manager with one header: name Authorization, value Bearer ${__P(token)}. __P reads a JMeter property. The Header Manager sits at the Thread Group level, so every request below it carries the header.

The Authorization header reads the shared property. In this screenshot the plan is organised as two thread groups, token first, then data.
Step 5: Add the secured request and check it
Add an HTTP Request for the endpoint (GET /your-secured-endpoint) and an assertion such as a Response Assertion on status code 200. A 401 usually means the token is missing, malformed or expired, and a 403 means it is valid but lacks the scope. Run one thread with a View Results Tree first and read the real request: the header should show a long token, not TOKEN_NOT_FOUND. Remove the listener before the real run, and see the order JMeter executes elements in if a header or extractor does not seem to fire.
What happens when the token expires during the test?
Requests start returning 401 once the token’s expires_in seconds have passed, and the error rate jumps from that moment. You have three honest options:
- Use a longer-lived token for the test. Ask for a test client whose tokens outlive the run. This is the simplest option and keeps the numbers clean.
- Fetch a token per virtual user. Put the token request in the load Thread Group inside a Once Only Controller and keep the token in a variable (
${access_token}) instead of a property. Be aware that each user then hits the token endpoint, so you are testing the authorization server as well. Report those timings separately. - Refresh on a schedule. Store the expiry in a property (
System.currentTimeMillis() + expires_in * 1000) and wrap a refresh request in an If Controller that fires shortly before it. Have one thread do it, not every thread at once, or the refresh itself becomes a spike.
Whatever you choose, name the token requests clearly, so you can filter them out of the response time report. The OAuth2 load testing guide covers the token side of this in more depth.
How do I load test an API behind the authorization code flow?
You cannot script the browser login with a few HTTP Requests, because the flow needs a user, a redirect and, with PKCE, a code verifier whose SHA-256 hash (the S256 method, RFC 7636) was sent in the first request. The practical approach is to separate the two problems. Issue a batch of test tokens ahead of the run, with a script or a test client that your authorization server permits, write them to a CSV file, and read one per thread with a CSV Data Set Config, as in how to use a CSV data set in JMeter. Each virtual user then sends its own Bearer ${token} header. That also gives you realistic per-user identities, where one shared token makes every request look like the same caller to rate limits and caches.
How do I keep credentials safe in a JMeter test plan?
Treat the plan, the .jtl result file and the JMeter log as places a secret can leak. Use a test client and test accounts, never production credentials. Pass the secret on the command line (-Jclient_secret=... and ${__P(client_secret)} in the plan) so it is not saved in the .jmx. Remember that a View Results Tree or a saved result with response data enabled records the token itself, and that tokens in a CI log stay there. Revoke or let the test client’s tokens expire when the run is done.
How do I run this test at scale?
Run it in non-GUI mode (jmeter -n -t plan.jmx -l results.jtl) with the secret passed in -J properties, and size the load by ramping threads up gradually, as described in JMeter’s ramp-up period. To generate load from several regions without managing load generators, LoadFocus runs JMeter scripts as they are from 25 cloud regions, with up to 20,000 concurrent virtual users per test on paid plans (loadfocus.com/load-testing, October 2026). See the LoadFocus JMeter load testing page for the details.
Frequently Asked Questions
How do I handle OAuth in a load test?
Do the token exchange before the measured requests. Otherwise you are load testing the auth server, and its timings get mixed into the numbers for the API you meant to test. In JMeter that means a setUp Thread Group that fetches the token and shares it through a property.
Which OAuth 2.0 grant should I use for a JMeter test?
Use client credentials for service-to-service APIs. For APIs behind a user login, pre-generate tokens through the authorization code flow with PKCE and feed them in from a CSV file. Avoid the password and implicit grants: RFC 9700 says the password grant must not be used and the implicit grant should not be.
What happens when the token expires mid-run?
Requests start failing with 401 unless you refresh it. Use a token that outlives the test, fetch a token per virtual user, or refresh from a single thread shortly before expiry.
Should I use BeanShell or JSR223 in JMeter?
JSR223 with Groovy. JMeter’s documentation recommends migrating from BeanShell for performance and because BeanShell has limited maintenance.
Are the credentials safe in the test plan?
Treat them as exposed. Tokens and credentials end up in logs and result files, so use test accounts rather than anything that touches real data, and pass secrets with -J instead of saving them in the plan.
Sources
- RFC 6749, The OAuth 2.0 Authorization Framework (client credentials grant, client authentication)
- RFC 7636, Proof Key for Code Exchange
- RFC 9700, Best Current Practice for OAuth 2.0 Security (January 2025)
- draft-ietf-oauth-v2-1, The OAuth 2.1 Authorization Framework (Internet-Draft, October 2026)
- Apache JMeter component reference and user manual: test plan (October 2026)
Related reading
- Performance Testing APIs With OAuth2
- Load Test a UI With JMeter’s WebDriver Sampler
- JMeter Distributed Testing: Controller and Worker Setup
Run this JMX in the cloud, from 25+ regions
Upload your JMeter script to LoadFocus and run it with thousands of virtual users from 25+ cloud locations, with no load generators to manage. Results show p95/p99 response times, throughput and errors per request, and a free tier is included.