API testing · Collection runner · CSV · Tutorial
Data-driven API testing with CSV and JSON files
Drive a collection run from a CSV or JSON data file, read each row in requests and scripts, control iterations and failure handling, export JUnit or HTML reports and reuse the data in monitors.
Data-driven API testing means writing a request and its tests once, then running them many times with different inputs taken from a file. Each row of a CSV or JSON file becomes one iteration. Inside the requests, {{columnName}} is replaced with that row's value; inside scripts, swc.iterationData.get('columnName') returns it. The collection runner repeats the selected requests once per row and reports every result.
In Swastik Codes you open a collection's menu, choose Run collection, attach the file under Data file, and press Run. This guide builds a small working example, then covers iterations, delays, failure handling, variable persistence, reports and scheduled runs.
When data-driven tests are worth it
Data files shine when the logic is the same and only the inputs and expected outputs change:
- boundary values for a validation endpoint (empty strings, maximum lengths, invalid formats),
- one test per customer tier, region, currency or feature flag,
- regression checks for a list of known ids after a migration,
- the same smoke test against many tenants.
If each case needs different logic, separate requests are clearer. If only the data changes, a data file keeps the collection small and the cases easy to review.
Before you start
Runs from the web app are executed by the swastik.codes server: the browser streams the results as they happen. That needs a free account, and the server refuses private and loopback addresses, so the API under test must be publicly reachable. The example below uses the public https://jsonplaceholder.typicode.com API.
Step 1: write the request with data variables
Create a collection called Users with one GET request named Get user:
GET https://jsonplaceholder.typicode.com/users/{{userId}}{{userId}} is not defined in any environment; it will come from the data file. If you send the request by hand now, the variable is unresolved and the editor highlights it as missing. That is expected.
Step 2: write tests that read the row
In the request's Scripts tab, add a Post-response script that compares the response with the expected values in the same row:
const row = {
userId: swc.iterationData.get('userId'),
expectedStatus: Number(swc.iterationData.get('expectedStatus')),
expectedName: swc.iterationData.get('expectedName'),
expectedUsername: swc.iterationData.get('expectedUsername')
};
swc.test(`user ${row.userId}: status ${row.expectedStatus}`, function () {
swc.response.to.have.status(row.expectedStatus);
});
if (row.expectedStatus === 200) {
const user = swc.response.json();
swc.test(`user ${row.userId}: name is ${row.expectedName}`, function () {
swc.expect(user.name).to.eql(row.expectedName);
});
swc.test(`user ${row.userId}: username is ${row.expectedUsername}`, function () {
swc.expect(user.username).to.eql(row.expectedUsername);
});
}Two details matter:
- CSV values are strings. As in Postman, every value read from a CSV file is a string, so
expectedStatusis converted withNumber()before comparing with the numeric status code. JSON data files keep their types. - Put the row value in the test name. The run results list tests by name, so
user 3: name is Clementine Bauchtells you immediately which row failed.
Step 3: create the data file
Save this as users.csv:
userId,expectedStatus,expectedName,expectedUsername
1,200,Leanne Graham,Bret
2,200,Ervin Howell,Antonette
3,200,Clementine Bauch,Samantha
11,404,,The first line is the header: each column name becomes a variable. The last row checks that a non-existent user returns 404. JSONPlaceholder has ten users, so id 11 is not found.
The parser handles quoted fields ("Smith, John"), escaped quotes (""), line breaks inside quotes, Windows line endings and a byte-order mark, and detects semicolon- or tab-separated files from the header line.
The same data as JSON is an array of objects:
[
{ "userId": 1, "expectedStatus": 200, "expectedName": "Leanne Graham", "expectedUsername": "Bret" },
{ "userId": 2, "expectedStatus": 200, "expectedName": "Ervin Howell", "expectedUsername": "Antonette" },
{ "userId": 3, "expectedStatus": 200, "expectedName": "Clementine Bauch", "expectedUsername": "Samantha" },
{ "userId": 11, "expectedStatus": 404 }
]JSON is the better choice when values are numbers, booleans or nested objects you want to post as a body, for example {{payload}} filled from a nested object.
Step 4: run it
- In the sidebar, open the Users collection's menu and choose Run collection (or Run folder on a folder).
- The runner tab shows the requests in run order. Untick any you want to leave out and use Move up / Move down to change the order.
- Under Data file, choose Select file and pick
users.csv. A preview table shows the parsed rows, and Iterations is set to the number of rows (4). - Pick an Environment if your requests use one.
- Press Run.
Results stream in as each request finishes. The summary shows requests, failed requests, tests passed and failed, the average response time and the total duration. The results table lists each iteration and request with its status, time, size and tests; filter it to failed requests, or to a single iteration, and open any row to see its tests, console output and (if saved) the response.
Iterations, delays and stopping on failure
Iterations defaults to the number of data rows, or 1 without a file. If you set more iterations than there are rows, the last row is reused for the extra iterations, which matches Postman's runner and Newman.
Delay (ms) waits between requests. Use it for rate-limited APIs: with a limit of 60 requests per minute, a 1000 ms delay keeps a long run under it.
Stop the run controls failure handling:
- Never (run every request) – the default; you get the complete picture.
- On the first failure (test or error) – stop at the first failed test or error.
- On the first failed test – stop on assertion failures only.
- On the first error (send or script) – stop when a request cannot be sent or a script throws, but keep going through failed assertions.
The hosted service caps a run at 1,000 iterations and 10,000 data rows, and the delay at 60 seconds.
Variables across requests and iterations
Data-driven runs often chain requests: create something with the row's data, then read it back. The run options control how script-set variables behave:
- Persist variables between requests – within one iteration, variable changes made by scripts always carry to the next request; with this on they also carry into later iterations. Turn it off if every row should start from the same values.
- Keep variable values after the run – write the final values back to the environment, globals and collection when the run ends. Leave it off if you do not want a test run to change your working environment.
A chained example with two requests, Create post and Get post:
// Create post – Body (raw JSON):
// { "title": "{{title}}", "body": "{{body}}", "userId": {{userId}} }
// Create post – Post-response script
swc.test(`created "${swc.iterationData.get('title')}"`, function () {
swc.response.to.have.status(201);
});
swc.variables.set('createdId', swc.response.json().id);swc.variables.set writes a local variable that lasts for the run, so the next request in the same iteration can use {{createdId}}.
Scripts can also change the flow per row. swc.execution.skipRequest() in a pre-request script skips a request for rows that do not apply:
// Pre-request: only rows marked as admin cases call the admin endpoint
if (swc.iterationData.get('role') !== 'admin') {
swc.execution.skipRequest();
}Skipped requests are counted separately in the results, so they do not look like passes.
Cookies, responses and logs
Three more options are useful on long runs:
- Save responses stores response headers and bodies in the results so you can inspect failures afterwards. Turn it off for very large runs.
- Run without stored cookies starts with an empty cookie jar; Save cookies after run writes cookies the run received back to the cookie manager. Every request in one run shares a single cookie jar.
- Turn off logs during run drops
console.logoutput, which speeds up long runs.
Reports for CI and for people
When the run finishes, export it as:
- JSON – the full result for your own tooling,
- JUnit – XML that CI systems and test dashboards understand,
- HTML – a self-contained report to attach to a ticket or send to a colleague.
Past runs are kept in the runner's run history on your device, so you can compare a failing run with the last good one.
Run it on a schedule
A data-driven collection makes a good monitor. Monitors run a collection on a cron schedule from the server and alert by email or a signed webhook, on failures, on every run, or when a failing monitor recovers. In a monitor's advanced settings you can attach iteration data (up to 1,000 rows), set the number of iterations, choose which requests run and in which order, and set retries and timeouts. See API monitoring.
Troubleshooting
Every iteration uses the same value. The column name in the file and the variable name in the request differ (check case and stray spaces in the header row), or an environment or collection variable with the same name is masking the data. Data values take precedence over environment, collection and global variables, but a local variable set by a script (swc.variables.set) takes precedence over data.
A numeric comparison fails with matching values. CSV values are strings. Convert with Number() or compare with string literals, or use a JSON data file.
The run stops early. Check Stop the run, and whether a script calls swc.execution.setNextRequest(null), which ends the current iteration.
"too_many_iterations" or "too_much_data". The run exceeds 1,000 iterations or 10,000 rows. Split the data file.
Requests fail to send. If the target is localhost or a private address, the server refuses it; use a publicly reachable environment.
Frequently asked questions
What file formats can the collection runner read?
CSV (comma, semicolon or tab separated, with a header row) and JSON arrays of objects.
How do I read a data value in a script?
swc.iterationData.get('column'). swc.iterationData.toObject() returns the whole row. In requests, use {{column}}.
Is this compatible with Postman data files?
Yes. The same CSV and JSON files work, values from CSV are strings as in Postman, and extra iterations reuse the last row as Newman does.
Can I use a data file for load tests?
Yes. Performance tests can also take a data file, so each virtual user works through realistic inputs. See API load testing in the browser.
Try it
Open the app, create the Users collection above, attach users.csv and run it. Runs need a free account. The collection runner feature page lists every option, writing API tests with swc.* scripts covers the assertions in more depth, and API monitoring explains how to run the same collection every few minutes.