JSON Extractor in JMeter: The Complete Advanced Guide
Learn how the JSON Extractor captures values from API responses, how paths and match numbers choose the right data, which variables it creates, how defaults protect your test, and how to chain, loop and secure extracted values like an experienced performance engineer.
Think of a cloakroom at a theatre. You hand over your coat and receive a numbered ticket. Later, you cannot get your coat back unless you present that exact ticket. The number is different for every guest and every evening, so nobody can print the tickets in advance. Modern web services work in the same way. They hand out tickets, such as session tokens, order numbers and cart identifiers, that are different for every user and every call. A test that wants to continue its journey must catch the ticket from one reply and show it in the next request.
The JSON Extractor in JMeter is the element that catches the ticket. It reads a value from a JSON response, stores it in a variable that belongs to that virtual user, and makes it available to every later step. Without it, any API test that involves logging in, creating something or following a link breaks after the first step. This guide explains the JSON Extractor from the first idea to the advanced details: paths, match numbers, generated variables, default values, loops, token handling, performance cost, security and design patterns.
1What Is the JSON Extractor?
The element that catches a value from one reply so the next request can use it.
The simple definition
A post-processor in JMeter is an element that runs right after a request and works with its response. The JSON Extractor is a post-processor made for responses written in JSON, the data format used by most modern web services. You give it a JSON path, which is a short address that points to one place inside the response, and a variable name. After each request, the extractor follows the path, takes the value it finds, and stores it in the variable.
From then on, any element in the test plan can use that variable by writing its name inside the variable placeholder syntax. A header can carry the token, a request body can carry the identifier, an address can include the item number, and an assertion can compare against it. This simple mechanism is the foundation of realistic API tests.
The extractor is like a person at a hotel desk who reads your room number from the key card and writes it on a note for the porter. The room number is different for every guest, but the note makes sure every later step, such as sending luggage up, uses the right number.
The family history
Before JMeter included this element, teams installed a plugin to get the same function, and many older plans still mention it. Current versions include the JSON Extractor in the standard installation. It uses a widely known JSON path library, so the path syntax you learn here works in many other tools as well.
Whenever a later request needs something that the server decided in an earlier reply, you need an extractor. If you catch yourself copying a value by hand from one response into the plan, stop: that value will be different on the next run, and for every user.
2Why We Need Extraction: Dynamic Values and Correlation
Real journeys are full of values that nobody can know in advance.
A recorded or hand-written test often works on the first try with one user and then fails as soon as it runs again. The reason is almost always the same: some values in the recorded requests were created by the server during that session and are no longer valid. Replacing such values with fresh ones taken from live replies is called correlation. The JSON Extractor is the main tool for correlation in API tests.
Session and access tokens
Issued at log-in and sent with every later request. Different for each user, and often valid only for a limited time.
Identifiers of created items
The number or code of a new order, cart, customer or document. Later steps read, change or delete it by that identifier.
Anti-forgery and state values
Hidden values that the server expects to see again in the next request, to prove the request belongs to the same journey.
Links and page markers
Addresses of the next page of results, or of a related resource, supplied inside the reply for the client to follow.
Job and transaction references
Identifiers of long-running work that the client must check later, such as a report being generated.
Items picked from lists
A product chosen from search results, whose identifier is then used to add it to a cart.
What happens without correlation
If you hard-code a token from one run, the second run is rejected as unauthorised. If you hard-code an order number, every virtual user fights over the same order, or the server reports that it does not exist. Under load, the symptoms get worse: hundreds of users updating one item produce locks, conflicts and unrealistic cache hits. The test then measures how the system copes with a strange, unnatural pattern, not with real users.
Example: the test that worked exactly once
A team recorded a shopping journey and replayed it with fifty users. Every user tried to add the same product to the same cart identifier that was captured during recording. The server answered with a conflict for almost all users after the first. Replacing the fixed identifier with a value extracted from the cart creation reply gave each user their own cart, and the errors disappeared.
Treating a test as correct because it passed once with one user. A single run cannot reveal values that should have been different for each user. Always run with several users and different data before trusting a plan.
3The Extractor Family and Where the JSON Extractor Fits
JMeter offers several extractors. The best one depends on the format of the reply.
Extractors all do the same kind of job: read something from a response and store it in a variable. They differ in the language they use to point at the value. Choosing the one that matches the format of the reply gives simpler, faster and more reliable plans.
| Extractor | Best used for | Weakness |
|---|---|---|
| JSON Extractor | JSON replies, with paths that follow the structure | Needs valid JSON, and reads the body or a variable |
| JSON JMESPath Extractor | JSON replies where calculation, filtering or reshaping is needed | A second path language to learn |
| Regular Expression Extractor | Any text, including headers, and replies of unknown format | Brittle on JSON, because it ignores structure |
| Boundary Extractor | Values between two known pieces of text, without pattern syntax | Also ignores structure, and depends on stable surrounding text |
| XPath Extractor | Structured markup replies | Not for JSON |
| CSS Selector Extractor | Values in web page markup | Not for JSON |
| Script-based extraction | Complex logic over the response | Costs CPU, harder to maintain |
Why not just use a pattern?
Many beginners reach for the regular expression extractor out of habit, because it works on any text. On JSON it is risky. A pattern that finds the text after the word “token” breaks if the field order changes, if spaces are added, or if the same word appears elsewhere in the reply. A JSON path follows the structure instead of the text, so layout changes do not matter, and it points at exactly one field. For JSON replies, the JSON Extractor is almost always the right choice.
A text pattern is like searching a printed book for the words “chapter three” and reading what follows. A JSON path is like opening the contents page, going to chapter three, and reading the title there. The second method still works when the book is reprinted with a different layout.
Use a regular expression only when the value is not inside JSON, for example when it sits in a response header or a plain text reply. The JSON Extractor reads the body, so header values need another tool.
4Execution Order and Scope of the Extractor
When the extractor runs decides what it can see, and where it sits decides what it touches.
JMeter runs the elements around a request in a fixed order: configuration elements, pre-processors, timers, the sampler, post-processors, assertions and listeners. The JSON Extractor is a post-processor, so it runs after the response has arrived and before assertions. This is important for two reasons. The extractor can only see the response of the sampler it belongs to. And the values it stores are ready before the next sampler starts, so the next request can use them at once.
flowchart TD
A["Sampler sends the request and receives the reply"] --> B["JSON Extractor follows the path and stores the value"]
B --> C["Assertions check the reply"]
C --> D["Listeners record the result"]
D --> E["Next sampler starts and can use the stored variable"]
Scope: which samplers the extractor serves
An extractor works on the samplers in its scope. As a child of a single sampler, it works only on that sampler. As a child of a controller or a thread group, it works on every sampler below. A broad extractor is convenient for a value that appears in every reply, but it also runs on replies that do not have the value, where it finds nothing and stores the default. Narrow scope is usually the safer choice.
| Placement | Runs after | Typical use |
|---|---|---|
| Child of one sampler | Only that sampler | Almost every extraction, such as the token from the login reply |
| Inside a controller | Every sampler in that controller | A value that appears in all replies of a business step |
| At thread group level | Every sampler of the group | Rare, for a value present in every reply |
Placing the extractor at the thread group level so that it runs after every request. On replies without the field it overwrites the variable with the default value, and a good token from the login step is destroyed by the next request. Keep each extractor next to the sampler that produces the value.
Name each extractor after what it captures and where, such as “Token from login” or “Order id from create”. When something breaks, these names appear in logs and make the cause obvious.
5JSON and Path Basics in Plain Words
A short refresher that makes every later chapter easier.
JSON describes data using only a few ideas. An object is a group of named fields, like a form. An array is an ordered list of items, like the lines on a receipt. A value is text in quotation marks, a number, true or false, the empty value called null, or another object or array. Objects and arrays can be nested inside each other to any depth.
flowchart TD
R["Root object: the whole reply"] --> T["Field: token"]
R --> U["Field: user, an object"]
U --> UI["Field: id"]
U --> UN["Field: name"]
R --> O["Field: orders, an array"]
O --> O1["Item 1: id, total"]
O --> O2["Item 2: id, total"]
The building blocks of a path
A JSON path starts at the root, written as a dollar sign, and walks down by naming fields separated by dots. Square brackets pick items from arrays, counting from zero. A star selects every item, two dots search at any depth, and a filter in brackets keeps only the items that satisfy a condition.
| Building block | Written as | Meaning |
|---|---|---|
| Root | $ | The whole reply, where every path starts |
| Child field | $.token | The field named token directly under the root |
| Nested field | $.user.id | The id inside the user object |
| Array item | $.orders[0].id | The id of the first order, counting from zero |
| All items | $.orders[*].id | The id of every order |
| Search at any depth | $..id | Every field named id, wherever it appears |
| Filter | $.orders[?(@.total > 100)].id | The id of orders whose total is above one hundred |
A path is like a postal address that gets more exact with each part: country, city, street, house number. Each step narrows the search until you reach one door. A wrong or missing step means the postman finds nothing.
Counting array positions from one. The first item is position zero. A path that asks for position one returns the second item, or nothing on a short list, and the extractor quietly stores the default value.
6Definite and Indefinite Paths: One Answer or Many
The shape of your path decides whether you get one value or a list of values.
A path is called definite when it can point to at most one value. It names fields and exact array positions, nothing more. A path is called indefinite when it may match several values. It uses a star, a search at any depth, a filter, or a range of positions. This difference is small to read but large in effect, because it controls what the extractor stores and how the Match No. setting behaves.
| Kind | Example | What you get | When to use it |
|---|---|---|---|
| Definite | $.data.token | One value, or nothing | Tokens, identifiers, single settings |
| Definite with position | $.items[0].id | One value from a fixed place | When the first item is always the one you need |
| Indefinite with star | $.items[*].id | A list of values | Loops, random choice, counting |
| Indefinite with search | $..price | Every price at any depth | Quick exploration, not stable tests |
| Indefinite with filter | $.items[?(@.stock > 0)].id | Only items that meet a rule | Choosing only valid, available items |
Why the difference matters
With a definite path, the Match No. setting has little to decide, because there is only one candidate. With an indefinite path, Match No. becomes the main control. It tells the extractor whether to keep the first match, a random match, a specific numbered match, or all of them. Many beginner problems come from using an indefinite path and forgetting that the result is a list.
Asking for “the passport of the person named in seat 12A” is a definite request. You get one passport or none. Asking for “the passports of everyone on the plane” is indefinite. You must decide whether you want the first one, a random one, or the whole stack.
Choosing the narrowest path that works
Prefer the narrowest path that clearly points to what you need. A path such as $.data.token is easy to read and cheap to run. A path such as $..token searches the whole document and may match unrelated fields that happen to share the name. Narrow paths fail loudly when the structure changes, which is exactly what you want from a test.
Use the search form with two dots only while exploring a new reply. Once you know where the field lives, replace it with the exact path. Exact paths are faster, safer and easier for teammates to understand.
Using a filter on a field that is missing from some items and assuming every item is checked. Items without the field are simply skipped, so the result may be shorter than you expected without any warning.
Example: the wrong price
A tester used the search form to read a price from a product reply. The reply also held a shipping price and a discount price with the same field name. The extractor stored the first match, which was the shipping price. Cart totals in later steps were wrong for weeks. Switching to the exact path fixed it in one minute.
7The Fields of the JSON Extractor Element, One by One
Every box on the screen has a job. Knowing each one removes guesswork.
The element has a small number of fields. Each one is simple on its own, but the fields work together, and a small mistake in one of them changes the result of the others. Here is every field explained in plain words.
Name
A label for the element. Choose a clear name such as “Extract token from login”. Names appear in logs and make large plans readable.
Apply to
Chooses what the extractor reads: the main request and its sub-requests, only the main request, only the sub-requests, or a JMeter variable. Most of the time the main request is correct.
Names of created variables
The names under which values are stored. For several paths, separate the names with semicolons. These names are what you use later.
JSON Path expressions
The paths to read, also separated by semicolons. The first name pairs with the first path, the second with the second, and so on.
Match No.
Decides which match to keep when a path finds several. Zero means random, a positive number means that numbered match, and minus one means all matches.
Compute concatenation var
When ticked, creates an extra variable with all matches joined by commas. It is useful for compact lists.
Default Values
What each variable holds when nothing is found. Also separated by semicolons and paired by position.
The pairing rule
The three list fields, names, paths and defaults, are paired by position. The first item of each belongs together. If you give three names and two paths, the third name has nothing to read. If you give two defaults for three names, the last variable gets no safe fallback. Always keep the three lists the same length and in the same order.
flowchart LR
N["Names: token ; userId ; role"] --> P1["Pair 1: token"]
J["Paths: $.token ; $.user.id ; $.user.role"] --> P1
D["Defaults: NO_TOKEN ; NO_USER ; NO_ROLE"] --> P1
P1 --> V["Variables token, userId, role"]
Think of three columns in a spreadsheet: the label, the cell address and the backup value. Row one in every column belongs to the same entry. If the columns have different lengths, the rows no longer line up and the wrong backup lands next to the wrong label.
Leaving a space after a semicolon or around a name. A hidden space becomes part of the variable name, and later references fail to find it. Type the lists without extra spaces and check the names in a debug view.
8Match No. Explained: First, Random, Specific and All
One small number decides how a list of matches becomes variables.
When a path finds more than one value, the extractor needs a rule. The Match No. field is that rule. It is one of the most important settings in the element, and it is also one of the easiest to misread. Here are the choices.
| Match No. | Meaning | Variables you get | Typical use |
|---|---|---|---|
| 1 | The first match | One variable with the first value | The newest item, the first result |
| 2, 3, and so on | That numbered match | One variable with that value | A fixed position, such as the second item |
| 0 | A random match | One variable with a randomly chosen value | Picking a random product or user |
| -1 | All matches | A count variable plus one numbered variable per match | Loops over every item |
Why random is useful
Real users do not all buy the same product. If every virtual user opens the first item, the test hits the same database rows and the same cache entries, which makes results look better than reality. Choosing a random match spreads the load across the data, which is closer to real behaviour.
Why all is powerful
With minus one, the extractor stores every match as a numbered variable and also stores how many it found. That count lets a loop walk through the list. This is the base of many realistic journeys, such as opening every product on a search page or deleting every item in a basket.
A box of numbered raffle tickets. You can take the first ticket, pull one blindly, ask for ticket number five, or empty the whole box onto the table. Match No. is the instruction you give to the person holding the box.
If a path is definite, keep Match No. at one. Only change it when the path is indefinite and you have a clear reason. A clear setting is a readable setting.
Using random selection in a test that must be repeatable, such as a functional smoke check. Random choices make failures hard to reproduce. Use fixed positions for checks and random positions for load.
Example: the hot product problem
A load test always opened the first search result. Response times looked excellent because that product was cached everywhere. After switching to a random match, the same service showed slow replies for the long tail of products, which is what real shoppers experienced on busy days.
9The Variables the Extractor Creates
One extraction can leave several variables behind. Knowing their names avoids confusion.
For a variable name such as itemId, the extractor can create several related variables. Which ones appear depends on Match No. and on the concatenation option. The table below lists them.
| Variable | Holds | When it exists |
|---|---|---|
itemId | The chosen value, or the default when nothing matched | Always, for a single match choice |
itemId_matchNr | How many matches were found | Always created, and used mainly with all matches |
itemId_1, itemId_2, … | The first match, the second match, and so on | When Match No. is minus one |
itemId_ALL | All matches joined with commas | When concatenation is ticked |
Reading the count
The count variable is the quiet hero. It tells you whether anything was found at all. A count of zero means the path matched nothing, even if the main variable holds a default. A loop uses it as the upper limit, and an assertion or an If Controller can use it to branch the journey.
Leftover variables from earlier runs
Numbered variables from a previous iteration can survive when the new reply is shorter. If the first reply had five items and the second only three, positions four and five may still hold old values unless the extractor clears them. Always trust the count, not the existence of a numbered variable.
flowchart TD
A["Path finds 3 values"] --> B{"Match No."}
B -->|"1"| C["itemId = first value"]
B -->|"0"| D["itemId = random value"]
B -->|"-1"| E["itemId_1, itemId_2, itemId_3 and itemId_matchNr = 3"]
E --> F["Optional itemId_ALL = joined list"]
A shopping receipt shows each item on its own line and a final line with the number of items. The count line tells you how many lines to read. If you ignore it and read past the end, you may read the previous customer’s receipt.
Keep names short, lower camel case and meaningful, such as orderId or productIds. Avoid names that end in an underscore and a number, because they can clash with the numbered variables the extractor creates.
10Default Values: Your Safety Net When Nothing Matches
A good default turns a silent problem into a visible one.
If the path finds nothing, the extractor stores the default value. If you leave the default empty, the variable may not be set at all, or may keep an old value from a previous iteration. Both cases are dangerous, because the next request then sends something wrong, and the failure appears far from its cause.
Choose obviously fake defaults
A good default is easy to spot in logs and impossible to confuse with real data. Values such as NOT_FOUND, NO_TOKEN or MISSING_ORDER_ID are clear. A default such as zero or one looks like a real identifier, and the server may even answer normally for it, which hides the problem completely.
Good defaults
- Stand out in logs and reports
- Cause a clear failure on the next request
- Make assertions easy to write
- Show exactly which extraction failed
Bad defaults
- Look like real values such as 0 or 1
- Leave the variable empty
- Allow stale values to survive
- Hide the failure until much later
Guarding the next step
A default value alone does not stop the journey. Add a check that fails fast when the default appears. A response assertion on the extractor’s own request, or an If Controller that skips dependent steps when the variable equals the default, keeps the test honest and the results clean.
A courier who cannot read an address should write “ADDRESS UNREADABLE” on the parcel, not copy the last address from memory. The first is a clear signal. The second sends the parcel to a stranger and nobody knows why.
Leaving the default field empty because “the path always works”. It works until the day a service changes or fails, and that is the day you most need a clear signal.
Example: the stale basket
A test extracted a basket identifier after each login. When the login service slowed down and returned errors, no new identifier was found, and the old one from the previous iteration stayed in the variable. For an hour the test kept writing to a basket that had already been closed, and the error rate looked like a basket problem instead of a login problem.
11Several Variables in One Element and the Apply To Setting
One extractor can read many fields, and it can read from different sources.
You do not need a separate extractor for every value. A single element can read several fields from the same reply. This keeps the plan compact and makes it easier to see everything that one request produces. The rule is simple: give a list of names, a list of paths and a list of defaults, all separated by semicolons and all in the same order.
When to group and when to split
Group values that come from the same reply and serve the same purpose, such as the token and the user identifier from a login. Split values when they have different match rules, for example one random product and the full list of all products. Because Match No. and the concatenation option apply to the whole element, values that need different settings belong in different elements.
| Situation | Group or split | Reason |
|---|---|---|
| Token, user id and role from login | Group | Same reply, same single-match rule |
| One random product and all product ids | Split | Different Match No. needs |
| Values with different default policies | Split | Easier to read and maintain |
| Twenty unrelated fields | Split by purpose | One huge element is hard to review |
The Apply to setting
Most of the time the extractor reads the response of the request it belongs to. The Apply to field changes this. A request may have sub-requests, for example embedded resources fetched after a page. You can choose to read only the main reply, only the sub-replies, or both. You can also tell the extractor to read a JMeter variable instead of a reply. That last option is helpful when a value was already stored and holds a JSON text that you want to dig into further.
Main sample and sub-samples
The default. Reads the main reply and any sub-replies. Fine when there are no sub-requests.
Main sample only
The safest choice for API tests. It avoids reading unrelated sub-replies.
Sub-samples only
For rare cases where the useful data is in an embedded request.
JMeter variable
Reads a stored variable by name. Useful for second-level extraction.
Imagine a filing clerk with a parcel that contains one big envelope and several small ones. “Apply to” tells the clerk whether to open only the big envelope, only the small ones, both, or to read a note you already copied onto a card.
For API work, choose “main sample only” unless you have a reason not to. It makes the source of every value clear and prevents surprises from sub-replies.
12What Gets Stored: Values Are Always Text
JMeter variables hold text. Understanding this prevents many small surprises.
Whatever the extractor finds, the stored variable is text. A number in the reply becomes the text of that number. A true or false value becomes the words true or false. This is fine for most uses, because later requests are text anyway. It matters when you do arithmetic or compare values, because you must convert the text back to a number in that step.
| Kind of value in the reply | What the variable holds | Care needed |
|---|---|---|
| Text | The text itself | Quotes are not part of the stored value |
| Number | The digits as text | Large or decimal numbers may show a different format than the raw reply |
| True or false | The word true or false | Compare as text |
| Null | Depends on the version and the path | Treat as a special case and test it |
| Object | The object written back as JSON text | Can be large, and spacing may differ from the original |
| Array | The array written back as JSON text | Use a deeper path if you need single items |
Objects and arrays as text
If the path points to an object or an array, the stored variable contains a JSON text of that part. This is useful when you want to pass a whole block to the next request, such as an address object copied from one reply into a new order. It is also a way to cut a big reply into smaller pieces for later extractors. Keep in mind that the text may be formatted differently from the original, so do not compare it character by character with the raw reply.
Numbers and formats
Very large identifiers and decimals can look different after being parsed and written back. If an identifier is a long number, check it in a debug view and, if the format changes, ask the service owners whether the field should be a text field. Silent changes in digits are rare, but they are painful to find.
A photocopier copies everything as a picture of the page, never as the original object. A copied banknote is a picture of a banknote. In the same way, every extracted value is a text copy of the original value, which is fine for passing along and not fine for spending.
Assuming a stored null looks the same in every version, or that an empty text means the field was missing. A field that exists with an empty value and a field that is missing are different situations. Test both with a real reply and decide how your plan should treat each.
When you need to compare a number, wrap the variable in a small calculation in a later step, and keep the original text variable untouched. This keeps your plan readable and avoids hidden conversions.
13Using Extracted Values in Later Requests
Extraction is only half the job. The other half is using the value in the right place.
Once a variable exists, you use it by writing its name inside a dollar sign and curly braces in any later field. That includes the path of a request, a query parameter, a header, a request body, an assertion or even another extractor. JMeter replaces the placeholder with the stored text just before sending.
Request path
Resource identifiers often sit in the path, such as the order to open or delete.
Query parameters
Search terms, page tokens and filters travel here.
Headers
Access tokens go into an authorization header. A header manager at plan level can apply it to every request.
Request body
Identifiers and copied blocks are placed inside the JSON body of create and update calls.
Assertions
Echo checks compare a reply field with the variable you sent.
Controllers and timers
Conditions and loop counts may use the count variable and the values.
Scope: who can see the variable
A variable belongs to the virtual user that created it. Another virtual user cannot see it. This is a good thing, because each user gets their own token and their own order identifier. It also means a value extracted in one thread group cannot be read in another thread group directly. If you need to share a value across groups, a property is the usual route, and it needs care, which a later chapter explains.
Order of use
The extractor runs after its own request, so its variable is ready for the next request in the sequence. It is not ready for the same request, because that request has already been sent. If a value is needed in the very first request, it must come from somewhere earlier, such as a data file or a setup step.
sequenceDiagram
participant U as Virtual user
participant S as Server
U->>S: Request 1, login
S-->>U: Reply with token
Note over U: Extractor stores token
U->>S: Request 2 sends stored token
S-->>U: Reply for that user
A hotel desk gives you a key card at check-in. The card is yours alone, and every door you visit later reads it. The card cannot open the door that issued it before you received it, because you did not have it yet.
Using a variable in a field that is evaluated before the extractor runs, for example in a setup step that executes only once, expecting a per-iteration value. Check where each value is born and where it is used.
14A Chaining Walkthrough: Log In, Create, Read and Delete
A realistic story shows how each extractor feeds the next request.
Let us follow a simple but typical journey for a task management service. The user logs in, creates a task, reads the task back, and deletes it. Three values travel between steps: the access token, the new task identifier and, for checking, the title of the task. Each step has an extractor that prepares the next one.
Log in
The reply holds an access token. An extractor stores it under the name token, with a default of NO_TOKEN. An assertion confirms the token path exists.
Create a task
The request sends the token in a header and a generated title in the body. The reply holds the new task identifier. An extractor stores it as taskId, with a default of NO_TASK.
Read the task
The request uses taskId in its path. An echo assertion checks that the reply carries the same identifier and the same title that were sent.
Delete the task
The request uses taskId again. The reply should confirm the deletion, and an assertion checks the confirmation field.
flowchart LR
A["Login"] -->|"token"| B["Create task"]
B -->|"taskId"| C["Read task"]
C -->|"taskId"| D["Delete task"]
A -.->|"token in header"| C
A -.->|"token in header"| D
What this teaches
Every arrow in the picture is an extraction on the sending side and a use on the receiving side. When something fails, you can follow the arrows backward. If the delete step fails with a missing identifier, you look at the create step’s extractor. If every step fails, you look at the login. This makes problems small and local, which is the whole point of designing journeys as chains.
Name steps with numbers and verbs, such as “01 Login”, “02 Create task”. Reports sort neatly, and a failing step tells its story at a glance.
Example: cleaning up after yourself
A team created ten thousand tasks per hour during a load test and never deleted them. After a night the database was full and the next morning’s tests began failing. Adding the delete step, fed by the same identifier extractor, kept the data size stable and the results comparable between runs.
15Looping Over Extracted Lists with the ForEach Controller
When you extract all matches, a loop lets you act on each of them.
Suppose a search reply contains twenty product identifiers and your user should open each one. The extractor, with Match No. set to minus one, creates numbered variables and a count. The ForEach Controller then walks through those numbered variables one by one, running its child requests each time with the current value ready.
The fields that matter
| Field | What to enter | Detail |
|---|---|---|
| Input variable prefix | The extractor variable name, such as productId | The controller looks for productId_1, productId_2 and so on |
| Start index for loop | Zero to start at the first item | The index is exclusive, so zero means the loop begins at item one |
| End index for loop | The count, or a number you choose | The end index is inclusive, so the count includes the last item |
| Output variable name | The name each child request uses, such as currentProduct | The current value is placed here on each turn |
| Add an underscore before number | Tick it | Matches the underscore in the numbered variable names |
Limiting the loop
Real shoppers do not open every item. If a search returns a hundred products, you may want only the first five. Set the end index to a small number and keep the count as a safety limit. Be careful that the end index does not exceed the real count, because the loop would then read variables that do not exist and send placeholder text to the server.
flowchart TD
A["Search request"] --> B["Extractor, Match No. -1: productId_1 ... productId_N and count"]
B --> C["ForEach Controller: prefix productId"]
C --> D["Open product request uses current value"]
D --> C
C --> E["Loop ends, continue to checkout"]
A teacher with a numbered class list calls name one, name two, name three, until the list ends. The count tells the teacher where the list stops, and the underscore-number pattern is the numbering on the list.
Forgetting that the start index is exclusive. Setting it to one skips the first item. Set it to zero to begin at the first match.
Add a small think time inside the loop, so the virtual user behaves like a person reading each page, not like a machine firing requests as fast as it can.
16Pagination and Polling: Extracting Inside a While Loop
Some journeys do not know in advance how many steps they need. The reply decides.
Two common situations need a loop that depends on a reply. The first is pagination. A service returns a list in pages, and each reply carries a marker that points to the next page. The second is polling. A service starts a job in the background, and the user must ask again and again until the job is done. In both cases an extractor reads a field from the reply, and a loop condition uses that field to decide whether to continue.
Pagination step by step
The first request asks for the first page. The extractor stores the next-page marker, with a default that means “no more pages”, for example an empty marker that the loop condition understands. The loop condition checks the variable. While a marker exists, the loop runs the next page request using the marker. When the reply no longer contains a marker, the extractor stores the default, the condition becomes false, and the loop ends.
flowchart TD
A["First page request"] --> B["Extractor reads next marker, default is empty"]
B --> C{"Marker has a value?"}
C -->|"Yes"| D["Request next page with marker"]
D --> B
C -->|"No"| E["Loop ends"]
Polling step by step
Polling is similar. The start request returns a job identifier. A loop then calls the status request. The extractor reads the status field, and the loop continues while the status says the job is still running. A short pause between calls prevents hammering the service. When the status says done or failed, the loop ends, and an assertion checks that the final status is the expected one.
| Loop type | Value extracted | Continue while | Safety limit |
|---|---|---|---|
| Pagination | Next page marker | The marker has a real value | A maximum number of pages |
| Polling | Job status | The status is still running | A maximum wait time or attempt count |
| Retry on pending | A ready flag | The flag is not yet true | A maximum number of retries |
Always add a safety limit
A loop that depends only on a reply can run forever if the reply never changes. A bug in the service, a wrong path in your extractor, or a default that equals a real value would trap your virtual user inside the loop. Add a second condition, such as a counter that stops the loop after a fixed number of rounds. Treat reaching that limit as a test failure, because the journey did not complete in a reasonable time.
Reading a book with a bookmark in each chapter that says “continue on page so-and-so” is pagination. Waiting at a bakery counter and asking “is my bread ready yet?” every minute is polling. In both cases a sensible person gives up after a long time instead of waiting for ever.
Choosing a default value that matches a real status, such as the word running. If the extractor fails, the default says “still running”, and the loop spins until the safety limit, or for ever without one. Use a clearly different default and handle it as an error.
Example: the export that never ended
A report export took between ten and sixty seconds. The test polled once per second with no limit. When a bug made the job stall, a hundred virtual users sat in the loop for the full test hour, sending millions of status calls. Adding a limit of ninety attempts and treating the limit as a failure turned a hidden disaster into a clear red line on the chart.
17Random Selection for Realistic Load
Real users choose different things. Your extractor can imitate that.
Load tests are only as honest as the data they use. If every virtual user opens the same product, edits the same record, or searches the same word, caches and databases behave in a way that real traffic never would. The extractor’s random mode helps by choosing one match at random from a list that the server just provided. Because the list comes from the server, every choice is valid at that moment.
Where random choice helps
Random product
Search, pick a random result and open it. This spreads reads across the catalogue.
Random available slot
For booking systems, choose any free slot from a filtered list, so the test does not try the same slot every time.
Random category
Read the list of categories, choose one, and browse it. Each user follows a different path.
Random existing user
For admin tests, choose any user from a returned page to view or edit.
Combining random choice with filters
A random pick from an unfiltered list may select an item that cannot be used, such as a product that is out of stock. A filter inside the path removes those items before the random choice happens. In this way the extractor only draws from valid candidates, and your later requests rarely fail for business reasons that have nothing to do with performance.
flowchart LR
A["Server returns 40 products"] --> B["Path filter keeps 25 in-stock products"]
B --> C["Match No. 0 picks one at random"]
C --> D["Variable productId holds the chosen value"]
D --> E["Next request opens that product"]
Weighing randomness against repeatability
Random choices make a run differ from the previous one. For comparing two builds, differences that come from random picks can look like differences in the product. Two habits reduce this problem. Use large enough runs, so that the randomness averages out. And log the chosen values in a debug view during a small trial, so you can reproduce a surprising failure by feeding the same value manually.
A restaurant inspector who always tastes the first dish on the menu learns little. One who chooses dishes at random, from the dishes that are actually being served today, gets a fair picture of the kitchen.
Record the chosen identifier in the request name or a log field for failed requests. When a single random item causes a slow or failing reply, you will know which item to investigate.
Using random choice and then asserting a fixed expected value. The expected value must come from the same chosen item, usually through the variable, not from a hard-coded example.
18Token Lifecycle: Logins, Expiry and Refresh
Tokens are the most common extracted value, and the most common source of long-run failures.
Most modern services give a short-lived token after login. Every later request carries it. The extractor captures it once, and a header manager sends it on each call. This pattern is simple for a two-minute test, and it becomes interesting in a two-hour test, because tokens expire. If the test ignores expiry, the second half of the run fails with authorization errors that say nothing about the performance of the service.
Three ways to handle expiry
| Approach | How it works | Good when | Care needed |
|---|---|---|---|
| Log in each iteration | Every loop begins with a login and a fresh extraction | Journeys are short and login is part of real behaviour | Adds load to the login service |
| Refresh on a schedule | A refresh call runs before the token’s lifetime ends | Long journeys with one login | Needs the expiry time and a second extraction of the new token |
| React to an authorization failure | On an unauthorized reply, the plan logs in again and retries once | Expiry times are unpredictable | Risk of retry storms if the login itself fails |
Capturing the expiry too
Many services return the token and a lifetime in seconds, or an expiry time. Extract both with one element. A small calculation can then store the time at which a refresh is due, and an If Controller can compare it with the current time before each request. This keeps the test running smoothly for hours, and it mirrors how real clients behave.
flowchart TD
A["Login, extract token and lifetime"] --> B["Work requests with token"]
B --> C{"Token close to expiry?"}
C -->|"No"| B
C -->|"Yes"| D["Refresh call, extract new token"]
D --> B
A shared token for everyone
Sometimes a test uses one token for all virtual users, for example a service account. This is simpler, and it is also less realistic, because the server sees one identity with a very high request rate and may throttle it. If the real system has many users, the test should use many tokens, one per virtual user, each captured by its own login.
A day pass at an amusement park stops working at closing time. A visitor who stays until evening needs a new pass, or a renewal at the gate. A clever visitor renews before the gates close rather than being stopped halfway on a ride queue.
Counting authorization errors from an expired token as service failures in the report. They are test design failures. Separate them with an assertion that names the cause, so that the performance numbers stay trustworthy.
Set a test duration that is longer than the token lifetime during your pilot run. If nothing breaks, your expiry handling works. If it breaks, you found the problem on a quiet day.
19Pairing the Extractor with the JSON Assertion
The extractor takes a value. The assertion proves the value was worth taking.
An extractor and an assertion often read the same path, and they have different jobs. The extractor stores a value for later. The assertion says whether the reply was correct. If you use only the extractor, a failed extraction quietly stores the default and the problem appears later. If you add an assertion on the same path, the failure appears on the request that caused it, with a clear name.
| Question | Extractor | JSON Assertion |
|---|---|---|
| Main job | Save a value for later use | Decide pass or fail for this reply |
| If the path finds nothing | Stores the default and the request still passes | Marks the request as failed with a message |
| Changes the report | No, unless a later step fails | Yes, failures appear in error rates |
| Best placement | Under the request that produces the value | Under the same request, after the extractor |
The standard pairing
For every value you extract and later depend on, add an existence check on the same path. For identifiers, this is usually enough. For values that must have a meaning, such as a status, add an equals check as well. With this pair in place, a missing token, an empty list, or a changed field name stops the journey early at the exact request that produced it.
Token
Extract the token. Assert that the token path exists.
Created identifier
Extract the new identifier. Assert it exists and, if the format is known, that it has the expected shape.
List of items
Extract all identifiers. Assert that the first item exists, so an empty list is a visible failure.
Status flag
Extract the status for loops. Assert the final value after the loop ends.
Why not just rely on the default?
A default can only protect the next request. It cannot tell the report that this request, the one that should have returned the value, was the real culprit. The assertion gives the failure a home. The result is shorter investigations, cleaner error counts and fewer arguments about whether a failure belongs to the test or the system.
A cashier takes your note and puts it in the till, which is the extractor. A second person checks whether the note is genuine, which is the assertion. A till full of notes is no help if nobody checks them, and a checker with no till has nowhere to put the good notes.
Order matters inside one request. Place the extractor first and the assertion after it, and give both names that mention the field. Failure messages then read like a sentence.
Writing the path twice by hand and letting the two drift apart after a service change. Keep a short note of where each path is used, and update both together.
20Testing and Debugging Your Extractor
A few simple tools show exactly what the extractor saw and what it stored.
An extractor that has not been tested on real data is only a guess. Fortunately, JMeter gives several ways to see what is happening. Use them every time you add or change an extractor, and again after any service change.
The tools you can use
View Results Tree with the JSON path tester
Run one request. In the results tree, choose the response and switch the display to the JSON path tester. Type your path and see the matches at once, without rerunning anything.
Debug Sampler
Add it after the request. It lists all variables and their current values in its own reply, so you can see what the extractor stored.
Debug PostProcessor
Attach it to a request to record variables and properties for that request in its sample details, which keeps the plan tidier.
The log
Extraction problems and invalid paths show as messages in the log viewer. Open it when a variable remains at its default.
A short routine for each new extractor
First, run the request once with a single thread and look at the real reply. Second, test the path in the path tester and confirm that it finds exactly what you expect, nothing more and nothing less. Third, add a debug view and confirm that the variable holds the right value. Fourth, break the path on purpose and confirm that the variable shows your obviously fake default. Finally, remove or disable the debug elements before a large run, because they cost memory and time.
flowchart LR
A["Run one request"] --> B["Test path in the path tester"]
B --> C["Check variable in a debug view"]
C --> D["Break the path on purpose"]
D --> E["Confirm the default appears"]
E --> F["Disable debug elements for the real run"]
A new smoke alarm should be tested with a button press and then with a little smoke. Pressing the button proves the alarm sounds. A real test proves it senses a problem. An extractor needs both: the happy path and the deliberate break.
Leaving a View Results Tree enabled during a large run. It stores every reply in memory and can slow down or crash the generator. Use it only for small trials and for failed requests.
Copy a healthy reply and a broken reply into a notes file next to your plan. You can then test paths without a live system, which is perfect for quick reviews before a release.
21Alternatives and When to Choose Them
The JSON Extractor is a great default, but it is not the only tool in the box.
JMeter offers several ways to pull a value from a reply. The right one depends on the format of the reply and on how complex the rule is. For structured JSON replies, the JSON Extractor is almost always the clearest and most readable choice. For other formats, or for unusual rules, another tool may fit better. Knowing the options helps you choose without hesitation.
| Tool | Best for | Strength | Weakness |
|---|---|---|---|
| JSON Extractor | JSON replies with clear structure | Readable paths, filters, lists | Needs valid JSON |
| JSON JMESPath Extractor | JSON replies, in versions that include it | Powerful query language with projections | A second path language for the team to learn |
| Regular Expression Extractor | Text, headers, odd or broken formats | Works on anything that is text | Fragile when the text layout changes |
| Boundary Extractor | Values between two known text markers | Simple and fast | Markers must be unique and stable |
| XPath Extractor | XML and HTML replies | Structure-aware for markup | Wrong tool for JSON |
| CSS Selector Extractor | HTML pages | Familiar to web developers | Only for HTML |
| Script-based extraction | Complex rules and calculations | Unlimited flexibility | Harder to read, test and keep fast |
A simple decision path
If the reply is valid JSON, start with the JSON Extractor. If your rule needs a calculation, or the path language cannot express it, consider a script step, but keep it small. If the reply is not JSON, choose the tool made for that format. If a reply mixes formats, such as JSON inside a text line, extract the outer part first with a boundary or a pattern, and then use the JSON Extractor on the variable with the Apply to setting.
flowchart TD
A["What is the reply format?"] --> B{"Valid JSON?"}
B -->|"Yes"| C["JSON Extractor"]
B -->|"No, XML or HTML"| D["XPath or CSS Selector Extractor"]
B -->|"No, plain text or headers"| E["Boundary or Regular Expression Extractor"]
B -->|"Mixed"| F["Cut out the JSON part first, then JSON Extractor on the variable"]
A kitchen has a paring knife, a bread knife and a cleaver. You can cut a tomato with any of them, but the paring knife is quick, safe and clean. The JSON Extractor is the paring knife for JSON: right for most jobs, and not the tool for sawing bread.
Avoid using a regular expression on JSON just because you know it well. A change in field order or spacing can silently break it, while a path stays correct as long as the structure stays the same.
Writing a long script to do what one path can do. Scripts cost more CPU, hide logic from reviewers and are harder to maintain. Reach for them only when a built-in element cannot do the job.
22Performance Cost of Extraction
A small element repeated thousands of times per second is no longer small.
Every extraction reads the reply, parses it as a document, and evaluates a path. With one user this takes a tiny fraction of a millisecond. With hundreds of users and thousands of requests per second, the sum becomes visible in the CPU and memory of the load generator. If the generator is busy, it measures its own delay as response time, and your results become less trustworthy.
What drives the cost
Size of the reply
Bigger replies take longer to parse. A reply of several megabytes costs far more than one of a few kilobytes.
Number of paths
Each path is evaluated on the parsed document. Many paths on one large reply add up.
Path complexity
Searches at any depth and filters visit many nodes. Exact paths touch only a few.
Stored variable size
Extracting a big object into a variable keeps that text in memory for every virtual user.
Request rate
The same element running ten times more often costs ten times more.
Practical ways to keep the cost low
Extract only what a later step truly needs. Use exact paths instead of searches. Avoid storing whole objects when one field is enough. Remove extractors that nobody uses any more, which is common in plans that have grown over time. For big replies that you only need to pass along, think about whether the later request can ask the server for a smaller reply instead.
Measuring the effect
The honest way to know is to measure. Run the same test with the extractors enabled and disabled at a moderate load, and compare the CPU and memory of the generator and the response times. If the difference is small, you are safe. If it is large, trim the work or add more generator machines. Keep the CPU of a generator below a comfortable level, so that the timings stay a measure of the service, not of the tool.
| Choice | Cheaper option | Costlier option |
|---|---|---|
| Path style | Exact path | Search at any depth |
| What to store | One field | A whole object or array |
| How many extractors | Only those needed later | Everything just in case |
| Debug elements | Disabled in real runs | Left enabled for every sample |
A mail room clerk who opens every letter completely, and copies out the whole text, works much slower than one who only reads the reference number on the top corner. Both can do the job, and only one can keep up when the mountain of letters arrives.
Blaming the service for slow responses when the generator itself was overloaded. Always watch the generator’s CPU and memory during a run, and treat a high reading as a warning about the accuracy of the results.
23Distributed Tests, Threads and Properties
Where a variable lives decides who can see it. This matters more when many machines are involved.
In a distributed test, one controller machine sends the same plan to several worker machines. Each worker runs its own virtual users, and each virtual user has its own variables. A value extracted on one worker is not visible on another, and a value extracted by one user is not visible to another user. For most journeys this is exactly what you want, because each user logs in and works with their own data.
Variables and properties
| Item | Visible to | Typical use | Care needed |
|---|---|---|---|
| Variable | One virtual user | Tokens, identifiers, per-user data | Cannot be shared with other users |
| Property | All users on the same JMeter process | Shared settings such as a service account token | Not shared across worker machines automatically |
Sharing a value inside one machine
Sometimes a setup step logs in once, and every user should use that result. A property can carry it. A small script step copies the extracted variable into a property in the setup group, and the main group reads the property into a variable at the start. This pattern is useful, and it must be used with care. Properties are global to the process, so only one source should write them, and users should only read them.
Sharing across worker machines
Each worker runs the setup step itself, so each worker gets its own value. If the service requires a single shared token for the whole test, the setup values must be consistent, for example by giving the token through a command-line option to every worker. Otherwise, let each worker log in separately, which is simpler and often more realistic.
flowchart TD
C["Controller sends the plan"] --> W1["Worker 1: own users, own variables"]
C --> W2["Worker 2: own users, own variables"]
W1 --> S1["Setup step: login, property on worker 1"]
W2 --> S2["Setup step: login, property on worker 2"]
S1 --> U1["Users on worker 1 read property"]
S2 --> U2["Users on worker 2 read property"]
Each office branch of a company has its own notice board, which every employee in that branch can read, while each employee also has a private notebook. A note pinned in the London branch is invisible in the Paris branch unless someone sends it there on purpose.
Expecting one worker’s extracted value to appear on another worker. The workers are separate processes, and nothing travels between them except what you send deliberately.
Prefer per-user variables for per-user data. Reach for properties only for truly shared items, and name them clearly so nobody mistakes them for user data.
24Security and Privacy of Extracted Values
Extracted values are often secrets. Treat them as carefully as the real system would.
The values that testers extract are often the most sensitive ones in the whole exchange: access tokens, session identifiers, customer identifiers and sometimes personal data. Because JMeter stores variables in memory and can write them to files and reports, a careless setup spreads secrets to places where they do not belong.
Where secrets can leak
Saved result files
Result files that store request headers and response bodies may contain tokens and personal data. Store only what you need.
Extra variables in results
A setting can add chosen variables to every row of the result file. Never list a token variable there.
Debug views and logs
Debug elements print values. Screenshots and shared logs may then reveal them.
Assertion messages
Failure messages can include the value found. Keep sensitive values out of custom messages.
Shared plans and reports
Plan files saved with real tokens in default fields end up in repositories and tickets.
Good habits
Use dedicated test accounts and synthetic data, never real customer data. Use short-lived tokens, and rotate them. Keep real credentials out of plan files by reading them from protected sources at run time. Mask or remove values before sharing a report or a screenshot. Delete result files that contain secrets when the investigation ends, and apply the same retention rules your company uses for other sensitive files.
flowchart LR
A["Token extracted into a variable"] --> B["Used in headers of later requests"]
A --> C{"Where else might it go?"}
C --> D["Result file"]
C --> E["Debug view and logs"]
C --> F["Failure messages"]
C --> G["Saved plan defaults"]
D --> H["Limit what is saved and mask before sharing"]
E --> H
F --> H
G --> H
A hotel key card is harmless until someone photographs it, prints a copy and pins it on a public board. Extracting a token is like handing the key to a guest. The guest, and the hotel, are responsible for not leaving copies around.
Pasting a screenshot of a debug view into a ticket without checking what it shows. A single visible token can be enough for someone to act as that user until it expires.
Example: the token in the ticket
A tester attached a result file to a public bug tracker. The file held the authorization header of every request. A security review spotted long-lived tokens in it, and the team had to rotate credentials for the whole test environment. Since then, the team saves only response times and error messages by default and masks the rest.
25Design Patterns for Reliable Extraction
A few reusable habits make extraction plans clean, fast to debug and easy to hand over.
Patterns are proven answers to common problems. They save time, because you do not invent a new approach each time, and they help a team read each other’s plans. Here are the patterns that serve JSON extraction best.
| Pattern | What it does | Why it helps |
|---|---|---|
| Extract, assert, use | Pair each extractor with an existence check, then use the value | Failures appear at the request that caused them |
| Fake defaults | Use clearly fake default values such as NOT_FOUND | Makes failed extractions obvious in logs |
| Narrow paths | Prefer exact paths over searches | Faster and more stable |
| Filter then choose | Filter candidates in the path and choose randomly from the result | Realistic load with valid data |
| Count-guarded loops | Loop using the count variable and a safety limit | Loops end, even when the service misbehaves |
| Setup then share | Do shared work once in a setup group | Avoids repeated work and extra load |
| Reuse through modules | Keep common login and extraction steps in one reusable block | One place to fix when the service changes |
| Clean up after use | Delete test data created by the journey | Keeps runs repeatable and the environment healthy |
Naming conventions
Agree on a naming style and keep it. Short, meaningful, lower camel case names such as authToken, orderId and productIds are easy to read. Name elements by what they extract: “Extract authToken from login”. When someone reviews the plan a year later, the names tell the story without opening a single element.
A map of the plan
For larger plans, keep a one-page map listing every variable, where it is extracted, where it is used and what happens when it is missing. It takes an hour to create and saves days of confusion, especially when new people join the team or when the service changes its replies.
A well-run kitchen uses labels, standard containers and a written recipe. Anyone can step in, find the sauce and know what it contains. A plan with good patterns is a labelled kitchen: new cooks are productive on day one.
Create a template journey with the extract, assert, use pattern already built in. Copy it for every new journey, and your plans start out with the right habits.
Copying a login block into every thread group instead of reusing one shared block. When the login changes, you must hunt down every copy, and you will miss one.
26Anti-Patterns: Common Extraction Mistakes and How to Fix Them
These habits look harmless and cause long, confusing investigations.
An anti-pattern is a solution that seems reasonable and keeps causing trouble. Learning to recognise them is the fastest way to improve a plan. Each box below names the problem, explains the harm and shows the better way.
Problem
The default value field is left empty, or filled with a realistic value such as zero or one.
Why It’s Harmful
When extraction fails, the variable is empty, stale or looks real. Later requests send wrong data, sometimes receive normal answers, and the cause stays hidden for hours.
Correct Approach
Use clearly fake defaults such as NOT_FOUND, and add an assertion or a condition that fails fast when the default appears.
Problem
Paths that search the whole document, such as a double dot followed by a common field name, are used in stable tests.
Why It’s Harmful
The search can match unrelated fields with the same name, the first match may change when the service adds a field, and the cost grows with the size of the reply.
Correct Approach
Use exact paths in tests. Keep searches for the exploration phase only.
Problem
The extractor is the only guard. There is no assertion on the same path.
Why It’s Harmful
A failed extraction does not mark the request as failed. The error shows up two or three steps later, and the report blames the wrong request.
Correct Approach
Add an existence check beside every extractor whose value is needed later.
Problem
Loops that depend only on an extracted value, with no safety limit.
Why It’s Harmful
If the value never changes, virtual users stay inside the loop for ever, creating huge traffic and hiding the real problem.
Correct Approach
Add a counter-based limit and treat reaching it as a failure.
Problem
Extracting large objects and arrays into variables “just in case”.
Why It’s Harmful
Each virtual user keeps its own copy in memory, which multiplies cost, and the extra parsing slows the generator.
Correct Approach
Extract only the fields that later steps use, with exact paths.
Problem
Tokens are saved in result files, shown in debug views that are shared, or kept as real values in saved plans.
Why It’s Harmful
Secrets spread to tickets, repositories and chat messages. Anyone with access can act as that user while the token is valid.
Correct Approach
Use test accounts, short-lived tokens, limited saved data and masking before sharing anything.
Review a plan against this list once a quarter. Most plans contain at least two of these mistakes, and fixing them takes less time than a single long investigation.
27Best Practices Checklist for Extractors
A short list you can follow every time you add an extractor to a plan.
Best practices are the habits of experienced testers, collected in one place. None of them is hard. Together they make plans that are faster to build, easier to read and much less likely to fail in mysterious ways.
Look at a real reply first
Run one request and read the reply before writing a path. Never guess the structure from documentation alone.
Write the narrowest exact path
Test it in the path tester and confirm it returns exactly what you expect.
Name variables and elements clearly
Use short, meaningful names, and keep the three lists of names, paths and defaults in the same order.
Set a fake default
Make failed extractions visible and impossible to confuse with real data.
Add an assertion on the same path
Make the failure appear on the request that caused it.
Choose Match No. on purpose
First for single values, random for realistic choice, all for loops, and a fixed position only when the position has meaning.
Break it on purpose
Confirm that the default appears and the assertion fails with a clear message.
Remove debug elements before a big run
They cost memory and time, and they can leak secrets.
Do
- Test paths on real replies
- Keep defaults obviously fake
- Limit loops with counters
- Use per-user variables for per-user data
- Keep secrets out of reports
Do not
- Guess paths from memory
- Leave defaults empty
- Loop on a reply with no limit
- Extract whole objects without a reason
- Share tokens in screenshots
A pilot reads a checklist before every flight, not because the pilot forgets how to fly, but because the checklist protects against the one step that is easy to skip on a busy day. The extractor checklist works the same way.
28Real-World Scenarios: Extraction in Typical Systems
Four common kinds of systems, and how the extractor supports each of them.
Ideas become clearer when we see them in familiar settings. Here are four systems that testers meet again and again, and the extraction needs of each.
Online shop
A shopper logs in and the extractor stores the token. A search reply returns a list of products, and a random match with an in-stock filter chooses one. The cart reply returns a cart identifier, which the checkout request needs. The order reply returns an order number, which the history request must contain. The chain has four extractions, each paired with an assertion, and the shop’s database sees a realistic spread of products.
Banking application
After login, the account list reply holds several account identifiers. A random match picks one account for balance and statement requests. A transfer request creates a reference, which the extractor stores so a later status request can confirm the transfer. Strict security rules apply, so the team masks tokens, uses synthetic accounts and limits what is saved in result files.
Ticket booking platform
A search returns events, and a filter keeps those with free seats. A random event is chosen, and its seat map returns available seats. The extractor picks a seat and a hold identifier. A short timer limits the hold, so the extractor and the following payment step must run quickly, and a polling loop watches the booking status until confirmation.
Data export service
A request starts a report and returns a job identifier. A polling loop reads the status with a capped number of attempts and a pause between them. When the status says done, the extractor reads a download link, and a final request fetches the file. The total time from start to download is measured as one transaction, which is what the business customer cares about.
| System | Main extracted values | Special care |
|---|---|---|
| Online shop | Token, product id, cart id, order id | Filter to in-stock items before random choice |
| Banking | Token, account id, transfer reference | Masking and synthetic data |
| Ticket booking | Event id, seat id, hold id | Time-limited holds and polling |
| Data export | Job id, status, download link | Safety limit on polling |
Draw your own system’s value chain on one page before building. Mark every value that moves from one reply to a later request. Each mark is an extractor, and each extractor is paired with an assertion.
29Troubleshooting: When the Variable Is Empty or Wrong
A calm, step-by-step approach finds the cause of most extraction problems in minutes.
When an extractor does not work, the symptom is usually the same: the variable holds the default value, holds an old value, or holds something unexpected. The cause is almost always one of a short list. Work through the list from top to bottom.
| Symptom | Likely cause | What to check |
|---|---|---|
| Variable holds the default | Path finds nothing | Test the path in the path tester on the real reply |
| Variable holds the default | Reply is not valid JSON, such as an error page | Read the real reply and the status code |
| Variable holds the default | Wrong source in Apply to | Check whether the extractor should read the main reply or a variable |
| Variable is empty | Spaces in the variable name or path | Look for hidden spaces around semicolons and names |
| Placeholder text appears in the next request | Variable name mismatch | Compare spelling and case in the extractor and in the request |
| Old value appears | Extraction failed and the old value survived | Set a default and add an assertion |
| Loop reads strange values | Wrong start or end index, or leftover numbered variables | Use the count variable and remember that the start index is exclusive |
| Wrong value chosen | Path matches several fields | Make the path exact or add a filter |
| Works with one user, fails with many | Variable shared by mistake through a property, or rate limits | Use per-user variables and check server throttling |
The five-question routine
flowchart TD
A["Variable wrong or empty"] --> B{"Is the reply valid JSON?"}
B -->|"No"| C["Fix the request or the environment first"]
B -->|"Yes"| D{"Does the path find the value in the path tester?"}
D -->|"No"| E["Correct the path"]
D -->|"Yes"| F{"Do names, paths and defaults line up?"}
F -->|"No"| G["Fix the lists, remove hidden spaces"]
F -->|"Yes"| H{"Is the variable used after it is created?"}
H -->|"No"| I["Reorder steps or move the extractor"]
H -->|"Yes"| J["Check scope, properties and server limits"]
Most problems end at the first or second question. A reply that is not valid JSON is the single most common cause, because overloaded services often return an HTML error page with the same status code that the extractor expected to see on a good day. The path itself was fine, and the reply simply was not what you thought it was.
If a radio is silent, you first check whether it is plugged in and tuned, before opening the casing. Check the reply, then the path, then the settings, in that order, and avoid rewriting the whole plan on a guess.
Why the first question matters most
Many testers spend an hour adjusting a path that was never wrong. The server returned an error page, a login screen or an empty body, and the path simply had nothing to read. Looking at the real reply first costs ten seconds and removes the most common false trail. Keep the habit: before touching any setting, open the reply that the extractor actually received and read it with your own eyes.
The second most common cause is timing. The extractor reads the reply of its own request, so it must sit under the request that produces the value, not under a neighbour. When an element is moved or copied in a large plan, its place in the tree changes silently, and it may start reading a reply that does not hold the field. A quick look at the tree structure, and at the name of the request above the extractor, confirms that the right pair is together.
Changing several things at once while debugging. Change one thing, run once, look at the result. If you change three things and the problem disappears, you will not know which change fixed it, and it will return.
30Pre-Flight Checklist and Long-Term Maintenance of Extractors
Extractors depend on the shape of replies, and replies change. A small routine keeps plans healthy.
An extractor is a quiet promise: “the value you need lives at this address.” When the service changes its reply, the promise can break without any visible error in the plan itself. A short check before each important run, and a regular review after each release, keeps the promise honest and protects your results.
| Area | Question to ask | Why it matters |
|---|---|---|
| Paths | Were all paths tested on a fresh, real reply? | Catches changed field names and positions |
| Defaults | Does every extractor have an obviously fake default? | Makes failures visible |
| Assertions | Is there an existence check beside each extractor? | Failures appear on the right request |
| Loops | Does every loop have a safety limit? | Prevents runaway traffic |
| Tokens | Is expiry handled for the test duration? | Avoids late-run authorization errors |
| Cost | Are only needed values extracted, with exact paths? | Protects generator accuracy |
| Security | Are secrets kept out of saved files and screenshots? | Prevents leaks |
| Debug | Are debug elements disabled for the real run? | Saves memory and avoids exposure |
Healthy run
One user, one loop. Every extracted variable holds a real value, and every assertion passes.
Broken run
Break one path on purpose. The default appears and the matching assertion fails with a clear message.
Small concurrent run
A handful of users with different data. Each one should see its own token and its own identifiers.
Real run
Start the full test, watch the failure counts by request name, and keep an eye on the generator.
Habits that keep plans healthy
Review after every release
Compare replies with the previous release. Update paths on purpose after confirming the change was intended.
Keep sample replies
Store healthy and broken replies next to the plan, so paths can be tested without a live system.
Remove unused extractors
Delete extractors whose variables nobody uses. They cost time and confuse readers.
Keep a variable map
One page that shows where each variable is born, used and what happens when it is missing.
Fixing a path after a release without asking whether the change was announced. A renamed field may break real clients too, and your failing test may be the first warning anyone gets.
Keep a small library of known good samples
Teams that test often benefit from a folder of sample replies, one healthy and one broken for each important request. With these files you can try a new path in seconds, show a colleague exactly what the service returned on a bad day, and review changes without touching a live environment. Samples also work as living documentation. A new tester can read them and understand the journey faster than from any written description, because they show real field names, real nesting and real values.
31Version Differences and Compatibility Notes
Small details can differ between versions. Verify on your own installation.
JMeter and its path library evolve. Details such as how a null is stored, how a numeric value is written back, which functions are available inside a path, or which related extractors ship with the tool can differ between versions. This guide describes the general behaviour that holds across common recent versions, and it avoids promising exact details that may change.
What to verify on your version
Null handling
Run a reply with a null value and look at what the variable holds. Decide how your plan treats it.
Number formatting
Extract a large identifier and a decimal, and compare them with the original reply.
Match variables
Confirm which numbered and count variables appear for single, random and all-match settings.
Filter and function support
Try the filter or function you plan to use in the path tester before building the plan around it.
Related elements
See which extractors your version offers, so you know whether a query-language alternative is available.
A one-page compatibility test
Keep a tiny test plan with a few sample replies and the extractors you rely on. Run it whenever you upgrade the tool or move to a new machine. If any variable changes its value or format, you know before your real tests do. This small habit has saved many teams from chasing a “service problem” that was really a tool upgrade.
A cook who changes ovens always bakes a test loaf first. The recipe has not changed, but each oven has its own habits. A one-page compatibility test is the test loaf of your plan.
Write the tool version in your test report. When results differ between two runs, the version is the first thing to compare.
32Frequently Asked Questions
Short, plain answers to the questions testers ask most often.
It reads a reply, follows one or more paths to find values, and stores them in variables that later requests, assertions and controllers can use.
Whenever a later request depends on something the server created in an earlier reply, such as tokens, identifiers, page markers and status values.
Give lists of names, paths and defaults, separated by semicolons and in the same order. The first name pairs with the first path and the first default.
It decides which match to keep when a path finds several: a positive number for that numbered match, zero for a random one, and minus one for all matches with a count variable.
The path found nothing. Most often the reply was not what you expected, such as an error page, or the path has a spelling, case or position mistake. Test the path in the path tester on the real reply.
A clearly fake value such as NOT_FOUND, never something that looks real like zero or one, and never an empty value.
Set Match No. to minus one, then use a ForEach Controller with the variable name as the prefix, a start index of zero, an end index equal to the count or a smaller limit, and the underscore option ticked.
Text. Convert them in a later step when you need arithmetic or numeric comparison.
Not directly. Variables belong to one user. Shared values can travel through properties, which are visible to all users on the same JMeter process but not across worker machines.
Yes, whenever a later step depends on the value. An existence check on the same path makes failures appear on the request that caused them.
A little. Each extraction parses the reply. Use exact paths, extract only what you need, and compare generator load with and without extractors.
Log in again each iteration, refresh before the lifetime ends using an extracted expiry, or react to an authorization failure with one new login. Pilot the test for longer than the token lifetime.
33Summary and Key Takeaways
The short version, for the day you only have two minutes.
The JSON Extractor is the bridge between one reply and the next request. It reads a path, stores the result in a variable and lets your journey follow real data instead of fixed guesses. Used with care, it makes tests realistic, repeatable and easy to debug. Used carelessly, it hides failures behind stale or fake values.
Key Takeaways
- Extract what the server creates — tokens, identifiers, page markers and status values change with every run, so the plan must read them from replies instead of hard-coding them.
- Learn the fields — names, paths and defaults are paired by position, and Match No. decides between first, random, specific and all matches.
- Make failure loud — use obviously fake defaults and add an existence assertion beside every extractor, so problems appear on the request that caused them.
- Prefer exact paths — they are faster, clearer and fail visibly when the structure changes, while searches can quietly pick the wrong field.
- Loop with limits — ForEach, pagination and polling loops need the count variable or a counter, so a stuck reply cannot trap users for ever.
- Mind scope and cost — variables belong to one user, properties are shared inside one process, and every extraction costs parsing time on the generator.
- Protect secrets — keep tokens out of saved results, shared screenshots and messages, use synthetic data, and review plans after every release.