API testing · Scripts · Variables · Tutorial

Chain API requests using values from the previous response

How to chain API requests: extract tokens and ids from a response with swc.* scripts, store them in variables, use them in the next request and run the sequence with the collection runner.

By · · 10 min read

To use a value from one API response in the next request, add a post-response script to the first request that reads the value from the response and saves it in a variable, for example swc.collectionVariables.set('userId', swc.response.json().id). Then reference the variable as {{userId}} in the URL, headers, body or auth of the next request. Save both requests in a collection and run it with the collection runner, and the value is passed along automatically every time.

This guide builds a working chain step by step, shows the common patterns (login tokens, created ids, pagination, polling) and explains the variable rules that make chains work or quietly break. The examples use swc.*, Swastik Codes' scripting API; it is the same object as Postman's pm.*, so you can write pm. instead and Postman scripts run unchanged.

Why chain requests

Real API workflows are rarely a single call:

  • Log in, then call protected endpoints with the token from the login response.
  • Create, then read, update and delete the resource using the id the create call returned.
  • Follow pagination using the cursor or next-page URL from each page.
  • Start a job, then poll until its status says it is finished.

Copying values from one response into the next request by hand works once. It does not work in a test suite you run every day, in CI or on a schedule. Chaining makes the sequence repeatable.

The building blocks

PieceWhat it does
swc.response.json()parses the response body so you can read fields from it
swc.response.headers.get('Location')reads a response header
swc.collectionVariables.set(key, value)stores a value with the collection
swc.environment.set(key, value)stores a value in the active environment
{{key}}inserts a variable into a URL, header, body or auth field
collection runnersends saved requests in order and keeps variables flowing between them

Variables resolve from the narrowest scope that defines them: local, then data (in a data-driven run), environment, collection, then global. The environments and variables page describes the scopes in full.

Step by step: a three-request chain

This example uses JSONPlaceholder, a free fake REST API, so you can follow along in the app with a free account. The chain finds a user by username, lists that user's posts, then loads the comments on the first post.

1. Set up a collection and a base URL

Create a collection called Chaining demo. In its Variables tab add:

VariableInitial value
baseUrlhttps://jsonplaceholder.typicode.com
userId(empty)
postId(empty)

Declaring userId and postId up front is optional but worth doing, for a reason explained in the section on variable rules below.

2. First request: find the user and save the id

Create a GET request to {{baseUrl}}/users?username=Bret, save it in the collection as Find user, and add this Post-response script in the Scripts tab:

JavaScript
const users = swc.response.json();

swc.test('user was found', function () {
  swc.response.to.have.status(200);
  swc.expect(users).to.be.an('array').with.lengthOf(1);
});

swc.collectionVariables.set('userId', users[0].id);

Send it. The API returns an array with one user whose id is 1, and the script stores 1 in the collection variable userId. Open the collection's Variables tab and you will see the new value.

3. Second request: use the id, save the next one

Create List posts: GET {{baseUrl}}/posts?userId={{userId}}. The variable is highlighted in the URL, and shown in red if it does not resolve. Its post-response script:

JavaScript
const posts = swc.response.json();

swc.test('user has posts', function () {
  swc.response.to.have.status(200);
  swc.expect(posts).to.be.an('array').that.is.not.empty;
  swc.expect(posts.every(function (p) { return p.userId === Number(swc.collectionVariables.get('userId')); })).to.be.true;
});

swc.collectionVariables.set('postId', posts[0].id);

4. Third request: use both

Create Get comments: GET {{baseUrl}}/posts/{{postId}}/comments, with a test:

JavaScript
swc.test('comments belong to the post', function () {
  const comments = swc.response.json();
  swc.expect(comments).to.not.be.empty;
  comments.forEach(function (c) {
    swc.expect(c.postId).to.eql(Number(swc.collectionVariables.get('postId')));
  });
});

The tests convert with Number(...) so the comparison works whether the stored value comes back as a number or as text.

5. Run the chain

Open the collection's menu in the sidebar and choose Run collection. The collection runner shows the run order (the order of the requests in the collection, which you can change for the run), sends each request, runs its scripts and passes the updated variables to the next request. The summary shows every test for all three requests. Export it as JSON, JUnit XML or HTML if you need to share the result.

Pattern: log in and reuse the token

The most common chain is authentication. Suppose your API has POST /auth/login that returns {"access_token": "...", "expires_in": 3600}.

The Login request (with No auth in its Authorization tab, since it is how you get credentials) has a JSON body that uses environment variables:

JSON
{
  "email": "{{email}}",
  "password": "{{password}}"
}

and a post-response script:

JavaScript
swc.test('login succeeded', function () {
  swc.response.to.have.status(200);
});

const body = swc.response.json();
swc.environment.set('token', body.access_token);
swc.environment.set('tokenExpiresAt', Date.now() + body.expires_in * 1000);

Then open the collection, set its Authorization to Bearer token with the value {{token}}, and leave the other requests on Inherit auth from parent. Every request after Login is sent with the fresh token. Put Login first in the collection so the runner sends it first. The JWT authentication testing guide covers what to assert about the token itself.

swc.environment.set writes to the active environment, so select one before you run. If no environment is active, use swc.collectionVariables.set instead.

Fetching the token only when needed

Instead of a separate login request, a collection-level Pre-request script can fetch a token with swc.sendRequest whenever there is none or it has expired. Collection scripts run before every request in the collection:

JavaScript
const expiresAt = Number(swc.environment.get('tokenExpiresAt') || 0);

if (!swc.environment.get('token') || Date.now() > expiresAt - 30000) {
  swc.sendRequest({
    url: swc.environment.get('baseUrl') + '/auth/login',
    method: 'POST',
    header: [{ key: 'Content-Type', value: 'application/json' }],
    body: {
      mode: 'raw',
      raw: JSON.stringify({ email: swc.environment.get('email'), password: swc.environment.get('password') })
    }
  }, function (err, res) {
    if (err) { console.log(err); return; }
    const body = res.json();
    swc.environment.set('token', body.access_token);
    swc.environment.set('tokenExpiresAt', Date.now() + body.expires_in * 1000);
  });
}

Requests made with swc.sendRequest go through the same network rules as normal sends and show up in the console, so you can see what the script sent. For standard OAuth 2.0 flows the built-in OAuth 2.0 auth type is simpler, since it gets and refreshes tokens without a script; see OAuth 2.0 in an API client.

Pattern: create, then read, update and delete

A create call gives you the id the rest of the CRUD chain needs. Many APIs return it in the body; some only in the Location header.

JavaScript
// Post-response script of "Create order"
swc.test('order created', function () {
  swc.response.to.have.status(201);
});

const order = swc.response.json();
swc.collectionVariables.set('orderId', order.id);

// If the API only sends a Location header such as /orders/ord_123:
const location = swc.response.headers.get('Location');
if (location) {
  swc.collectionVariables.set('orderUrl', location);
}

The following requests use {{baseUrl}}/orders/{{orderId}}: GET to confirm it exists, PATCH to change it, DELETE to remove it, and a final GET that expects 404. That last request is easy to forget and catches APIs that only pretend to delete. If you are unsure which codes to expect at each step, see HTTP status codes explained.

Pattern: values inside JSON bodies

Variables work inside request bodies too. When the next request needs a value in its body:

JSON
{
  "orderId": "{{orderId}}",
  "quantity": 2,
  "note": "created by run at {{$isoTimestamp}}"
}

{{$isoTimestamp}} is a dynamic variable, generated fresh on each send, alongside others such as {{$guid}} and {{$randomInt}}. If you need a value that is not a plain string, such as an array or an object from the previous response, store it as JSON and insert it without quotes:

JavaScript
swc.collectionVariables.set('itemIds', JSON.stringify(swc.response.json().items.map(function (i) { return i.id; })));
JSON
{ "ids": {{itemIds}} }

Pattern: loops and polling with setNextRequest

The runner normally goes top to bottom. A script can change that with swc.execution.setNextRequest('Request name'), which jumps to the named request next, or swc.execution.setNextRequest(null), which ends the iteration. This is how you follow pagination:

JavaScript
// Post-response script of "List orders" (URL: {{baseUrl}}/orders?cursor={{cursor}})
const page = swc.response.json();

if (page.next_cursor) {
  swc.collectionVariables.set('cursor', page.next_cursor);
  swc.execution.setNextRequest('List orders');
} else {
  swc.collectionVariables.set('cursor', '');
}

Polling a background job looks the same: repeat Check job status until the job reports done, then jump on to Download result. Add a delay between requests in the runner settings so you poll politely, and make sure the loop has an exit, because a job that never finishes would otherwise keep the run going until you stop it. setNextRequest is meant for collection runs and monitors; it does not change anything when you click Send on a single request.

Variable rules that decide whether a chain works

Which scope to use. Use collection variables for values that belong to one API's test flow (ids, cursors). Use environment variables for values that differ between staging and production, such as base URLs and credentials, and for tokens tied to those credentials. swc.variables.set sets a local variable: when you click Send it lasts only for that request, so it is the wrong choice for passing a value to a request you send later by hand.

Shared versus personal values. Every variable has an initial value, shared with your team, and a current value, which stays on your device and is never stored on the server. When a script updates a variable that already exists, it changes the current value. When a script creates a variable that does not exist yet, the value becomes its initial value, which is shared when the collection or environment syncs. So declare token (with an empty initial value) before your scripts set it, and a real token never ends up in the shared copy.

Single sends versus runs. When you click Send, changes to environment, collection and global variables are written back as soon as the scripts finish, so you can run a chain by hand, one request after another. Collection variables are only written back when the request is saved in that collection. In the collection runner, changes always flow from one request to the next within an iteration. The Persist variables between requests option (on by default) also keeps them between iterations and writes them back to your environment after the run; turn it off when every iteration should start from the same initial values.

Debugging. If a request is sent with an empty value, open the console panel. It shows the request exactly as sent, after variable substitution, plus anything your scripts logged. Logging the value you extract is often the fastest check:

JavaScript
console.log('saving userId', users[0] && users[0].id);

Chaining without scripts: Flows

If you prefer not to write scripts, Flows chain saved requests on a visual canvas. Each node's output becomes the next node's input: after a login request, a Select node on body.token picks out the token, and the next request uses it as {{input}}, for example in an Authorization: Bearer {{input}} header. Condition, Loop and Delay nodes handle branching and polling. Flows suit workflows that branch or loop over data, while the collection runner with scripts suits test suites with assertions and reports. Both use your saved requests.

Scripts + collection runnerFlows
How values are passedvariables set in scriptsnode output feeds the next node
Branching and loopssetNextRequestCondition and Loop nodes
InputCSV or JSON data files, one iteration per rowone input per run (JSON or text)
ReportsJSON, JUnit XML, HTMLper-node run log
Triggered byRun button, monitorsRun button, webhook

Common mistakes

  • Running requests in the wrong order. The runner follows the collection order unless you reorder it or use setNextRequest. Put the request that produces a value before the one that uses it.
  • No active environment. swc.environment.set has nowhere to write. Select an environment or use collection variables.
  • Reading the body of an error. If login fails, swc.response.json().access_token is undefined, and every later request fails with 401. Test the status before extracting, so the first failure explains the rest.
  • A stale value from an earlier run. A variable keeps its last value. Clear it at the start of a run (swc.collectionVariables.unset('orderId') in the first request's pre-request script) if a leftover value could hide a failure.

Frequently asked questions

How do I use a response value in the next request?

In the first request's post-response script, read the value and save it: swc.collectionVariables.set('id', swc.response.json().id). In the next request, use {{id}} wherever the value is needed.

Do Postman pm.environment.set scripts work for chaining?

Yes. pm and swc are the same object in Swastik Codes, so pm.environment.set, pm.collectionVariables.set and pm.response.json() behave the same way. Imported Postman collections keep their chains.

How do I save a value from a response header?

Use swc.response.headers.get('Header-Name'), for example swc.collectionVariables.set('orderUrl', swc.response.headers.get('Location')).

Can I chain requests without running the whole collection?

Yes. Variables set by scripts are saved when you click Send, so you can send the requests one at a time in order. Use the collection runner when you want the chain to run unattended, or a folder run to run only part of it.

Can I run a chain of requests on a schedule?

Yes. Attach the collection to a monitor and it runs on a cron schedule with the same scripts, alerting you by email or webhook when a test fails.

Try it on your own API

Swastik Codes runs in the browser and is free to use. Create an account to send requests, sync your workspaces and invite your team.