skip to content

What argument forms does k6's http.batch() accept, and what does each form return?

level: middleimportance: should knowfreq 50%

answer

  1. one call, several requests, one VU
  2. exactly one argument, array or object
  3. array in, array out; object in, object out
  4. positional order is method, url, body, params
  5. a bare string argument is rejected

basics

~20 s

http.batch() takes exactly one argument: an array or an object of requests. Each entry is a URL string, a positional array of method, url, body, params, or an object with those keys. Array in, array out; object in, object out.

solid answer

~40 s

`http.batch()` issues several requests concurrently inside one iteration of one VU and returns once all of them have finished. It accepts exactly one argument, and it must be an array or an object -- a bare URL string throws `invalid http.batch() argument type string`. Each entry can be a URL string (shorthand for a GET), a positional array `[method, url, body, params]`, or an object `{ method, url, body, params }`. The return shape mirrors the argument: pass an array and you get an array of `Response` objects in the same order; pass an object of named requests and you get an object keyed by those same names, which is far easier to read than indexing by position. Concurrency is capped by the `batch` and `batchPerHost` options, which default to 20 and 6.

code

javascript · 15 lines
javascript
import http from 'k6/http';

export default function () {
  const responses = http.batch({
    landing: 'https://quickpizza.grafana.com/',
    order: [
      'POST',
      'https://quickpizza.grafana.com/api/post',
      JSON.stringify({ sku: 'A-19', qty: 3 }),
      { headers: { 'Content-Type': 'application/json' } },
    ],
  });

  console.log(responses.landing.status, responses.order.status);
}

go deeper

for a junior

Know that http.batch() runs several requests at once from one VU and that you hand it an array or an object, never a lone URL string. Read the results back in the same shape you passed in.

for a middle

Explain the four entry forms and the positional order method, url, body, params, and why the object-of-named-requests form is easier to maintain than indexing an array by position.

for a senior

Reason about the concurrency caps: batch at 20 and batchPerHost at 6, both per VU, and what that means when a batch targets a single hostname. Handle entries that fail to parse without losing the rest of the results.

for a principal

Judge when batching is the right shape at all. It compresses independent requests into one iteration, but it also hides per-request pacing, so decide deliberately where a batch models the client and where sequential calls do.

## What `http.batch()` is for `http.batch()` fires several HTTP requests concurrently from a single VU inside a single iteration, then returns once every one of them has completed. Without it, `http.get()` and friends block the VU until each response arrives, so a page's worth of assets would be fetched strictly one after another. `batch` is the k6 call surface for "these requests do not depend on each other". It takes **exactly one argument**. Calling it with none throws `no argument was provided to http.batch()`; calling it with two throws `http.batch() accepts only an array or an object of requests`; and passing a bare URL string throws `invalid http.batch() argument type string` -- a surprisingly common mistake, because every other `k6/http` function happily takes a string URL. ## The four ways to write one request Inside the array or object, each entry may take any of these forms, and you can mix them freely in the same call. | Entry form | Example | Notes | |---|---|---| | URL string | `'https://a.test/'` | shorthand for a GET | | positional array | `['POST', url, body, params]` | order is fixed; pass `null` for body to reach the params slot | | object | `{ method: 'POST', url, body, params }` | `url` is mandatory; `method` defaults to GET | | tagged URL | `http.url` + backtick template | groups dynamic paths under one `name` tag | Two details are easy to miss. In the object form, the request key is `params`, matching the third argument of `http.post()` -- in the positional array form, the same object simply sits in position four. And in the object form, a `method` of `GET` or `HEAD` causes k6 to **drop any body you supplied**, so a body silently disappears rather than erroring. ## The return shape mirrors the argument shape - Pass an **array** and you get an array of `Response` objects, in the same order as the requests you listed. You read them as `res[0]`, `res[1]`. - Pass an **object** of named requests and you get an ordinary object with the same keys, each holding one `Response`. You read them as `res.home`, `res.api`. The object form is worth reaching for as soon as a batch grows past two or three entries: `res.checkout.status` says what it means, whereas `res[3].status` breaks the moment somebody inserts a request above it. An entry that fails to parse does not take the rest of the batch down with it: - An object entry with no `url` key produces `batch request <key> doesn't have a url key`. - A malformed URL produces a parse error naming the offending string. - With the `throw` option off, k6 logs `A batch request failed` as a warning and still returns the results container. - The offending slot holds a `Response` whose `error` and `error_code` are populated, exactly as a standalone failed request would be. ## What limits the concurrency Two root options bound how many of the batched requests are actually in flight at once, and both apply per VU: 1. `batch` caps the total simultaneous requests one `http.batch()` call may have open. Its default is **20**, so a batch of 50 URLs starts 20 and queues the rest, promoting a queued one each time a slot frees. 2. `batchPerHost` caps the simultaneous requests to any single hostname within that batch. Its default is **6**, and it can never raise the total above `batch`. The practical consequence: batching 40 URLs that all point at one host does not produce 40 concurrent requests, it produces 6. If a batch seems slower than the arithmetic suggests, `batchPerHost` is usually the reason. ## Params work per entry Every entry carries its own params object, so nothing about a batch is shared except the concurrency caps: - `headers` and `cookies` are per entry, so one request can carry an auth token and the others cannot. - `timeout` is per entry, so a slow report endpoint can have a longer budget than the assets beside it. - `redirects`, `responseType` and `responseCallback` are per entry too. - `tags` are per entry, which is how you keep a batch's samples separable in the results. That is how you give one entry a longer timeout or a narrower expected-status rule while its neighbours keep the defaults. ```javascript const res = http.batch({ landing: 'https://quickpizza.grafana.com/', create: ['POST', url, payload, { headers: { 'Content-Type': 'application/json' } }], }); ``` Each request in the batch emits its own metric samples exactly as a standalone call would, so a batched request is indistinguishable from a sequential one in the results. This is k6 v2.x behaviour.

  • In k6, why might batching 40 URLs to one host not give 40 concurrent requests?
    `batchPerHost` caps simultaneous requests to a single hostname and defaults to 6, so only six run at a time no matter how large the batch is. The wider `batch` option, default 20, caps the batch as a whole, and `batchPerHost` can never lift the total above it.
  • How does http.batch() differ from awaiting several http.asyncRequest() calls in k6?
    `http.batch()` is synchronous from the script's point of view: it blocks until every entry finishes and returns them together. `http.asyncRequest()` returns a Promise per call, so you control when to await and can interleave other work, but you also manage the concurrency yourself instead of getting the `batch` and `batchPerHost` caps.

saying these in an interview costs you the question

  • Passing a single URL string straight to http.batch()
  • Expecting an array back when an object of named requests was passed
  • Thinking batch spreads its requests across several VUs
  • Putting the params object in position three of the array form
  • Assuming batch requests skip metric collection