skip to content

In a Postman script, what do `pm.info.iteration` and `pm.info.iterationCount` report during a data-driven run?

level: middleimportance: should knowfreq 40%

answer

  1. Two numbers, one is an offset
  2. Counting starts where arrays start
  3. The total is the plan, not the file
  4. Last pass is total minus one

basics

~20 s

pm.info.iteration is the zero-based index of the iteration currently executing; pm.info.iterationCount is how many iterations the run was asked to perform — the requested count when one was given, otherwise the data file's row count.

solid answer

~40 s

Both are **sandbox** members and both describe position, not data. `pm.info.iteration` is an **index**, zero-based, so the first pass reports zero and the last reports one less than the total. `pm.info.iterationCount` is the **planned total** for the run: it reflects the count the run was asked to perform, which is the row count when only a data file was supplied. Together they let a script know where it is — `pm.info.iteration === 0` is the first pass, `pm.info.iteration === pm.info.iterationCount - 1` is the last — which is how one-off setup or a final summary is guarded without adding a request for it. Note the asymmetry: `iterationCount` describes the plan, so it can exceed the number of distinct rows the file actually holds.

code

javascript · 6 lines
javascript
if (pm.info.iteration === 0) {
  console.log('first pass of', pm.info.iterationCount);
}
if (pm.info.iteration === pm.info.iterationCount - 1) {
  console.log('last pass');
}

go deeper

for a junior

Remember which of the two is the position and which is the total, and that the position starts at zero. That alone answers the screening form of this question.

for a middle

Explain the last-pass expression and why the total is the run's plan rather than the file's length, since an explicit count can exceed the number of distinct rows.

for a senior

Show how you use the pair operationally — one-off setup, a final tally, and logging that names both the pass and the record so a failure is diagnosable without re-running.

for a principal

Own the design question of whether per-run setup belongs inside an iteration guard at all, versus outside the suite where it is not re-evaluated on every pass.

## Two numbers, two different meanings During a run, the sandbox exposes the position of the current iteration through two members of `pm.info`: - **`pm.info.iteration`** — the **index** of the iteration currently executing, counted from zero. - **`pm.info.iterationCount`** — the **total number of iterations** the run was asked to perform. They are easy to confuse because both are small integers about the same run, but one is an offset and the other is a size. Reading `pm.info.iteration` as "how many iterations so far" is the single most common error, and it produces off-by-one guards that fire on the wrong pass. ## `pm.info.iteration` is an index, not a count - The first pass reports **zero**, not one. - The last pass reports `pm.info.iterationCount - 1`. - It advances once per iteration, not once per request, so every request inside one pass sees the same value. - It is available in both script hooks, so a pre-request script and a test script in the same pass agree about where they are. - It says **where** you are, never **what** you are working on; the row itself lives in `pm.iterationData`. ## `pm.info.iterationCount` is the plan, not the data `iterationCount` reports what the run intends to do. When only a data file is supplied, that is the number of rows, and the two coincide. When an explicit count is supplied as well, the count is what `iterationCount` reports — which means it can be **larger than the number of distinct rows in the file**, because a count past the data's last row simply repeats that last row rather than wrapping or failing. | what the run was given | `pm.info.iterationCount` reports | distinct rows actually seen | |---|---|---| | a data file alone | the file's row count | all of them | | a data file and a smaller count | the smaller count | only the leading rows | | a data file and a larger count | the larger count | fewer than the count, last row repeated | | a count alone, no data file | the count | none; the scope is empty | The consequence is precise and worth saying out loud in an interview: **`pm.info.iterationCount` is not a way to learn how many rows the data file has.** It tells you how many passes the run will make, which is a different fact whenever both flags are present. ## Using the pair 1. **First-pass setup.** Guard work that must happen exactly once with `if (pm.info.iteration === 0) { ... }` — creating a fixture, fetching something shared, seeding a counter. 2. **Last-pass wrap-up.** Guard a final tally with `if (pm.info.iteration === pm.info.iterationCount - 1) { ... }`, which is the only expression that identifies the last pass without hardcoding a number. 3. **Diagnosable logging.** Print the index alongside the row so a failure names the pass and the record together, instead of leaving you to count entries in the output. 4. **Progress arithmetic.** `pm.info.iteration + 1` over `pm.info.iterationCount` is the human-readable form, and writing the `+ 1` explicitly is a reminder that the raw value is an offset. ## Where the pair misleads you - **Off-by-one on the last pass.** `pm.info.iteration === pm.info.iterationCount` is never true. Any guard written that way is dead code that never runs. - **Treating the count as the data length.** With a larger explicit count, some of those passes are repeats of the final row; the count cannot tell you that happened. - **Assuming a repeated pass looks different.** It does not. The index keeps advancing normally and the row scope answers as usual with the last row's values. - **Expecting a global counter.** Neither member accumulates anything across runs; both describe the run currently executing and nothing outside it. ## Why this is asked It separates candidates who have written per-row scripts from candidates who have only read about them. The zero-based index and the last-pass expression come out instantly if you have ever guarded setup work in a data-driven run, and the follow-up — whether the count equals the number of rows — quietly checks whether you know that the count and the file are independent inputs that can disagree.

  • Can a script use `pm.info.iterationCount` to check that every row of the data file ran?
    No. It reports the number of passes the run was asked to make, not how many rows the file holds. With an explicit count larger than the file, some passes repeat the last row and the count still reads the requested total. To verify row coverage you need something the rows themselves carry, or you drop the count and let the file govern.
  • Does `pm.info.iteration` change between the pre-request and test hooks of the same request?
    No. It advances once per iteration, so every request in a pass — and both hooks of every request — sees the same value. It also does not change from one request to the next inside a pass, which is why it identifies the pass rather than the position within it.

saying these in an interview costs you the question

  • Says the first iteration reports one, not zero
  • Compares the index to the count for the last pass
  • Treats the iteration count as the data file's row count
  • Thinks the index advances once per request
  • Expects a repeated last row to look different positionally