You built a checkout API scenario that works locally: three chained requests, assertions on each response, and a green run every time. Then CI needs the same scenario to run against 40 input combinations, load data from a QA-maintained file, and target staging and production with different credentials. Don’t clone the scenario 40 times. Use Apidog CLI iteration data.
With Apidog, you build the scenario once in the visual builder, then run it from a terminal with apidog-cli. The key flag is -d, short for --iteration-data. It accepts a CSV file, a JSON file, or a test data set stored in your Apidog project, then runs the scenario once per row/object.
How -d reads iteration data
From apidog run --help:
-d, --iteration-data <path|testDataId> Define the data which use for iterations (either JSON or CSV)
The argument can be either:
- a local CSV or JSON file path
- a test data ID from your Apidog project
For a local CSV:
apidog run --access-token $APIDOG_ACCESS_TOKEN \
-t 605067 -e 1629989 \
-d ./test-data/checkout-cases.csv \
-r cli
This runs test scenario 605067 once per CSV row. On each iteration, Apidog binds the row values to variables used in the scenario.
For JSON, use the same flag:
apidog run --access-token $APIDOG_ACCESS_TOKEN \
-t 605067 -e 1629989 \
-d ./test-data/checkout-cases.json \
-r cli
You do not need a separate format flag. Keep CSV headers or JSON keys aligned with the variable names used in your requests and assertions.
CSV data: best for flat test cases
Use CSV when each case fits naturally into columns.
Example checkout-cases.csv:
sku,quantity,coupon,expected_status,expected_total
DESK-01,1,SAVE10,200,89.10
DESK-01,0,SAVE10,422,0
CHAIR-09,3,,200,447.00
DESK-01,1,EXPIRED,410,0
GHOST-99,1,SAVE10,404,0
In your Apidog scenario, reference those columns as variables:
{
"sku": "{{sku}}",
"quantity": {{quantity}},
"coupon": "{{coupon}}"
}
Then assert against row-specific expectations:
status == {{expected_status}}
total == {{expected_total}}
Each CSV row becomes one iteration:
| Row | Case |
|---|---|
| 1 | Valid coupon |
| 2 | Invalid quantity |
| 3 | No coupon |
| 4 | Expired coupon |
| 5 | Unknown SKU |
The empty coupon value in row three is passed as an empty string, which makes it useful for no-coupon coverage.
JSON data: best for nested payloads
Use JSON when your test input contains nested objects or arrays.
Example checkout-cases.json:
[
{
"label": "valid order, two items",
"order": {
"items": [
{ "sku": "DESK-01", "qty": 1 },
{ "sku": "CHAIR-09", "qty": 2 }
],
"shipping": { "country": "US", "method": "ground" }
},
"expected_status": 200
},
{
"label": "unshippable country",
"order": {
"items": [{ "sku": "DESK-01", "qty": 1 }],
"shipping": { "country": "ZZ", "method": "ground" }
},
"expected_status": 422
}
]
In the scenario, reference the object directly:
{
"order": {{order}}
}
Then assert against:
status == {{expected_status}}
The label field is useful for reports. Instead of debugging “iteration 2,” you see a named case like unshippable country.
For JSONPath-based assertions and variable extraction, see setting assertions and extracting variables from a JSON response.
Practical rules for iteration files
Follow these rules to avoid false-positive runs in CI:
- Match names exactly. A CSV header named
qtywill not bind to{{quantity}}. - Quote CSV fields that contain commas.
- Use JSON instead of CSV for nested payloads.
- Store expected results in the data file, not hardcoded in the scenario.
- Commit local data files next to the tests when they should version with the code.
- Keep credentials out of CSV and JSON files.
A good data file contains test inputs and expected outputs, not environment-specific secrets.
Run from a stored Apidog data set
Instead of passing a file path, you can pass a stored test data ID:
apidog run --access-token $APIDOG_ACCESS_TOKEN \
-t 605067 -e 1629989 \
-d 38291 \
-r cli
This tells the CLI to fetch data set 38291 from the Apidog project.
Use this when:
- QA owns and maintains the case table in Apidog
- the data should not live in your repository
- multiple team members need a shared editable data source
Use a committed CSV or JSON file when:
- the data must be reviewed in pull requests
- the test data should be pinned to a commit
- changes to test cases should move with code changes
Run offline from an exported scenario file
You can also export a test case from Apidog and run the exported file directly:
apidog run ./checkout.apidog-cli.json -r cli,html
In this mode, the first argument is the exported test file. You do not need -t or -e.
You can still add iteration data:
apidog run ./checkout.apidog-cli.json \
-d ./checkout-cases.csv \
-r cli,junit
This is useful for:
- locked-down CI runners that cannot fetch scenarios from Apidog cloud
- reproducible test runs from a frozen scenario snapshot
- keeping the exact scenario definition versioned with the repo
For setup details, see the Apidog CLI installation guide and the complete Apidog CLI reference.
Combine -d with -n
-n controls iteration count:
apidog run --access-token $APIDOG_ACCESS_TOKEN \
-t 605067 -e 1629989 \
-n 50 \
-r cli
When you provide -d, the data file usually controls the number of iterations. For example, 40 CSV rows means 40 scenario runs.
Use -n when:
- you want to repeat a scenario without a data file
- you are running a simple soak test
- you intentionally want to repeat the whole data-driven run
For most data-driven API tests, leave -n out and let the data file define the run count.
Pass environment-specific values at runtime
Use --env-var and --global-var for values that change between environments:
apidog run --access-token $APIDOG_ACCESS_TOKEN \
-t 605067 -e 1629989 \
-d ./checkout-cases.csv \
--env-var "base_url=https://staging.internal" \
--global-var "api_key=$RUNTIME_API_KEY" \
-r cli,junit
This keeps responsibilities separate:
| Data source | Contains |
|---|---|
| CSV/JSON | test inputs and expected outputs |
| Apidog environment | reusable environment config |
| CI secrets | tokens, API keys, credentials |
| CLI overrides | per-run values |
Do not put secrets in iteration data. Pass them from your CI secret store instead.
Generate per-iteration reports
Use reporters to make failures visible in CI:
apidog run --access-token $APIDOG_ACCESS_TOKEN \
-t 605067 -e 1629989 \
-d ./checkout-cases.csv \
-r cli,junit \
--out-dir ./test-reports
Common reporter options:
| Reporter | Use case |
|---|---|
cli |
readable terminal output |
junit |
CI test results and dashboards |
html |
browsable report artifact |
json |
structured output for post-processing |
--out-dir controls where report files are written.
For CI, cli,junit is a practical default.
Continue after failed iterations
By default, a run can stop on the first failed assertion. For data-driven tests, you often want all failures in one report.
Use:
apidog run --access-token $APIDOG_ACCESS_TOKEN \
-t 605067 -e 1629989 \
-d ./checkout-cases.csv \
--on-error continue \
-r cli,junit \
--out-dir ./test-reports
--on-error continue runs all rows even if some fail. The command still exits non-zero if any iteration fails, so it remains a valid CI gate.
Useful modes:
| Mode | Behavior |
|---|---|
end |
stop on first error |
continue |
run all iterations and report all failures |
ignore |
continue and ignore errors |
Use continue for broad data tables. Use end for quick smoke tests.
Debug variable binding issues
The most dangerous failure mode is a green run that did not actually bind data.
Example symptoms:
- request body still contains
{{sku}} - values are blank even though the CSV has data
- assertions pass against default or empty payloads
Run with --verbose:
apidog run --access-token $APIDOG_ACCESS_TOKEN \
-t 605067 -e 1629989 \
-d ./checkout-cases.csv \
--verbose \
-r cli
Then inspect the actual request body sent for each iteration.
Common causes:
- Header and variable mismatch
CSV:
product_sku,quantity
DESK-01,1
Request:
{
"sku": "{{sku}}"
}
Fix by renaming product_sku to sku, or update the scenario to use {{product_sku}}.
- Unquoted CSV comma
Bad:
sku,note,expected_status
DESK-01,priority, customer,200
Better:
sku,note,expected_status
DESK-01,"priority, customer",200
- Wrong path in CI
The file may exist locally but not in the CI working directory. Add a check before the test run:
ls -la ./test-data
cat ./test-data/checkout-cases.csv
If a data-driven run passes suspiciously, inspect one verbose request before trusting the result.
Add the run to GitHub Actions
Here is a full GitHub Actions workflow:
name: Data-driven API tests
on: [pull_request]
jobs:
api-tests:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: '20'
- name: Install Apidog CLI
run: npm install -g apidog-cli
- name: Run data-driven scenario
env:
APIDOG_ACCESS_TOKEN: ${{ secrets.APIDOG_ACCESS_TOKEN }}
run: |
apidog run --access-token $APIDOG_ACCESS_TOKEN \
-t 605067 -e 1629989 \
-d ./test-data/checkout-cases.csv \
--on-error continue \
-r cli,junit \
--out-dir ./test-reports
- name: Upload report
if: always()
uses: actions/upload-artifact@v4
with:
name: api-test-report
path: ./test-reports
What this does:
- installs the CLI with npm
- reads
APIDOG_ACCESS_TOKENfrom GitHub Secrets - runs the scenario once per CSV row
- continues after failed rows
- writes CLI and JUnit reports
- uploads reports even when the test fails
To switch to JSON:
-d ./test-data/checkout-cases.json
To switch to a stored Apidog data set:
-d 38291
Everything else can stay the same.
The same pattern works in GitLab CI, Jenkins, CircleCI, and other systems:
- Install Node.js.
- Install
apidog-cli. - Expose
APIDOG_ACCESS_TOKEN. - Run
apidog runwith-d. - Publish the report directory as an artifact.
For more CI detail, see automating API tests in GitHub Actions. For the full CLI flag surface, see the complete Apidog CLI guide.
Recommended workflow
Start small:
- Build one scenario in Apidog.
- Replace hardcoded request values with variables.
- Put expected results in assertions using variables.
- Create a CSV with:
- one happy path
- one known failure
- one boundary case
- Run locally with
-d. - Add
--verboseif bindings look wrong. - Add more rows as bugs and edge cases appear.
- Move the command into CI with
--on-error continueandjunit.
The scenario stays small. Coverage grows by adding rows.
Example local run:
apidog run --access-token $APIDOG_ACCESS_TOKEN \
-t 605067 -e 1629989 \
-d ./test-data/checkout-cases.csv \
--on-error continue \
-r cli,junit \
--out-dir ./test-reports
Example staging run:
apidog run --access-token $APIDOG_ACCESS_TOKEN \
-t 605067 -e 1629989 \
-d ./test-data/checkout-cases.csv \
--env-var "base_url=https://staging.internal" \
--global-var "api_key=$STAGING_API_KEY" \
--on-error continue \
-r cli,junit
Example production smoke run:
apidog run --access-token $APIDOG_ACCESS_TOKEN \
-t 605067 -e 1630001 \
-d ./test-data/checkout-smoke.csv \
--global-var "api_key=$PROD_API_KEY" \
-r cli,junit
For a comparison of tool choices, see which tool to choose for data-driven API testing with CSV or JSON. For a CLI comparison from the Postman ecosystem, see Apidog CLI vs Newman.
Frequently asked questions
Can -d take both a file path and a stored data set?
No. Pass one value per run: either a local CSV/JSON path or a stored test data ID.
Use a file path when the data should version with the repo. Use a stored data ID when the team maintains the table inside Apidog.
Do I have to specify CSV or JSON?
No. Use the same -d flag for both.
-d ./cases.csv
or:
-d ./cases.json
Keep CSV headers or JSON keys matched to your scenario variables.
What happens if I use -d and -n together?
The data file usually defines the iteration count. Use -n only when you intentionally want extra repeats or when you are running without a data file.
Why did my data-driven run pass without testing the right input?
Most likely the data did not bind. Check for:
- mismatched variable names
- malformed CSV rows
- wrong file paths in CI
Run once with:
--verbose
Then inspect the actual request payload.
How do I keep credentials out of the data file?
Pass credentials at runtime:
--global-var "api_key=$RUNTIME_API_KEY"
or:
--env-var "token=$TOKEN"
Store the actual values in your CI secret manager.
Can I run the same data against staging and production?
Yes. Keep the scenario and data fixed, then switch the environment with -e or override environment variables at runtime.
Wrapping up
-d is the core flag for data-driven runs in Apidog CLI. It can read a local CSV file, a local JSON file, or a stored Apidog test data set. Pair it with --on-error continue, cli,junit reporters, and runtime variable overrides to make it CI-friendly.
Build the scenario once. Add cases as rows. Let CI run the whole table on every pull request.
Download Apidog, point apidog run at your first data file, and turn one scenario into repeatable API coverage.
Top comments (0)