Skip to main content
Every call to client.get() or client.post() already returns a Promise, so there’s no separate “async” method to learn. The SDK queues every request from a client instance internally and only runs up to concurrency of them at the same time.

Basic usage

1

Set a concurrency limit

Pass concurrency when creating the client to cap how many requests run at the same time:
const { ZenRows } = require("zenrows");

const client = new ZenRows("YOUR_ZENROWS_API_KEY", { concurrency: 5, retries: 1 });
Set concurrency to match your ZenRows plan’s concurrent request limit. Each client instance enforces its own limit. Two scripts (or two client instances) don’t share it, so running both at once can still trigger 429 Too Many Requests errors. See Concurrency for how plan limits work.
2

Scrape a list of URLs in parallel

Map your URLs to client.get() calls and use Promise.allSettled() to wait for all of them, even if some fail:
const { ZenRows } = require("zenrows");

const client = new ZenRows("YOUR_ZENROWS_API_KEY", { concurrency: 5, retries: 1 });

const urls = [
  "https://www.scrapingcourse.com/ecommerce/",
  // ... more URLs
];

(async () => {
  const promises = urls.map((url) => client.get(url));

  const results = await Promise.allSettled(promises);
  console.log(results);
})();
Promise.allSettled() waits for every call to finish, even if some of them fail. Unlike Promise.all(), it doesn’t reject as soon as one promise fails, so the rest of the batch still completes.
3

Split fulfilled and rejected results

const rejected = results.filter(({ status }) => status === "rejected");
const fulfilled = results.filter(({ status }) => status === "fulfilled");
Each fulfilled result’s .value is a standard Response, read the same way as client.get()’s return value.

When to think about concurrency

You don’t need to do anything special to run requests concurrently, calling .get()/.post() in a .map() already benefits from the internal queue. Set concurrency explicitly when you want to match a specific plan limit, or lower it if you’re seeing 429 errors.

POST in parallel

The same pattern works with client.post():
const promises = urls.map((url) =>
  client.post(url, {}, { data: new URLSearchParams({ key1: "value1" }).toString() }),
);

const results = await Promise.allSettled(promises);

How it works under the hood

The SDK uses fastq internally to queue every get() and post() call from the same client instance and run at most concurrency of them at once (default 5). This applies uniformly, there’s no separate queue for GET versus POST. Retry behavior configured via retries applies the same way regardless of how many requests run in parallel. See Error Handling and Retries for details.

Troubleshooting

  • 429 Too Many Requests even though concurrency is set correctly: Another script or process is using the same API key at the same time. Concurrency limits are per plan, not per client instance, so simultaneous scripts share the same real ceiling even though each ZenRows client only tracks its own local limit.
  • Some responses in the list are much slower than others: This is expected; total throughput depends on the slowest requests in each concurrent batch. See the Concurrency guide for the underlying math.
  • A Promise.all() call fails as soon as one request errors: Use Promise.allSettled() instead, as shown above, so a single failure doesn’t abort the whole batch.
  • Cancelling a request doesn’t free a concurrency slot right away: ZenRows keeps the slot occupied for up to a few minutes after a client-side cancellation while it finishes processing server-side.

Pricing

Requests are billed identically whether they run one at a time or concurrently. Cost depends on the config you send per request, not on how many run in parallel:
ConfigurationCost multiplier vs. basic
Basic request1x
js_render: true5x
premium_proxy: true10x
js_render: true + premium_proxy: true25x
Running requests concurrently doesn’t add cost by itself. It only changes how fast the same requests complete. See ZenRows Pricing.

FAQ (Frequently Asked Questions)

  1. You can set it to any number that matches your ZenRows plan’s concurrent request limit.
Yes. Every call from the same ZenRows instance, GET or POST, goes through one shared internal queue.
No. Every method already returns a Promise, so there’s no separate async variant to call, unlike SDKs for languages without built-in async I/O.