> ## Documentation Index
> Fetch the complete documentation index at: https://docs.zenrows.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Error Handling and Retries with the Node.js SDK

> Configure automatic retries in the ZenRows Node.js SDK and handle failed requests reliably.

Web scraping requests occasionally fail for transient reasons, like a temporary server hiccup or a dropped connection. The SDK can retry these automatically so you don't have to write that logic yourself.

## Basic usage

Retries are **off by default** (`retries: 0`). Pass a `retries` count to the client constructor to turn them on:

```javascript theme={null} theme={null}
const { ZenRows } = require("zenrows");

const client = new ZenRows("YOUR_ZENROWS_API_KEY", { retries: 2 });

const response = await client.get("https://www.scrapingcourse.com/ecommerce/");
```

With `retries: 2`, a failed request is attempted up to 2 additional times (3 attempts total) before the SDK resolves with the last response it received.

## When to enable retries

Enable `retries` (2-3 is a reasonable starting point) whenever you're running requests at scale or in production, anywhere a transient failure shouldn't fail your whole job. Leave `retries: 0` (the default) for quick one-off scripts or debugging, where you'd rather see the raw failure immediately than wait through automatic retry attempts.

## Which failures get retried

<Warning>
  The Node.js SDK retries a narrower set of status codes than some other ZenRows SDKs. Only these trigger a retry:
</Warning>

| Status code | Meaning              |
| ----------- | -------------------- |
| `422`       | Unprocessable Entity |
| `503`       | Service Unavailable  |
| `504`       | Gateway Timeout      |

Network-level errors (a rejected `fetch`, like a DNS failure or dropped connection) also trigger a retry. `429` and `500`/`502` responses are **not** retried automatically; they're returned as-is on the first attempt.

Between attempts, the SDK waits using exponential backoff: `2^attempt * 1000` milliseconds, so 1s, then 2s, then 4s, and so on for each subsequent retry.

<Tip>
  You aren't charged for retried attempts that fail. Only successful responses count toward your usage. See the [Retry Failed Requests](/zenrows-academy/retry-failed-requests) guide for more on how this works.
</Tip>

## Handling failures

The SDK treats HTTP error status codes and network-level problems differently. Pick the tab that matches what you're dealing with:

<Tabs>
  <Tab title="HTTP error status">
    Even after all retry attempts are exhausted, the SDK resolves with the final `Response` object. It does **not** throw for a non-2xx status, matching how native `fetch` behaves. Always check the response before trusting its content:

    ```javascript theme={null} theme={null}
    const response = await client.get(url, { js_render: true });

    if (response.ok) {
      console.log(await response.text());
    } else {
      console.log(`Failed after retries: ${response.status} - ${await response.text()}`);
    }
    ```

    Unlike some HTTP libraries, native `fetch` (and this SDK) has no built-in `raise_for_status()` equivalent. Throw manually if you want an exception:

    ```javascript theme={null} theme={null}
    const response = await client.get(url);

    if (!response.ok) {
      throw new Error(`Request failed with status ${response.status}`);
    }
    ```
  </Tab>

  <Tab title="Network-level exceptions">
    Connection problems (DNS failures, dropped connections) reject the returned `Promise` instead of resolving with a response. Wrap calls in `try`/`catch` to handle those:

    ```javascript theme={null} theme={null}
    try {
      const response = await client.get(url);
      console.log(await response.text());
    } catch (error) {
      console.log(`Request failed: ${error.message}`);
    }
    ```
  </Tab>
</Tabs>

## Troubleshooting

* **Response still fails after retries are exhausted**: Check `response.status` and look it up in [API Error Codes](/api-error-codes). Some failures (like an invalid `apikey`) won't be fixed by retrying and need a code change instead.
* **A `429` isn't being retried**: This is expected. The Node.js SDK's retry list only covers `422`, `503`, and `504`, plus network errors. Handle `429` by lowering your `concurrency` or adding your own backoff, see [Concurrency](/universal-scraper-api/sdk/nodejs/async-concurrency).
* **Retries seem to take a long time**: That's the exponential backoff by design (`2^attempt * 1000` ms). Lower `retries` if latency matters more than success rate for your use case.
* **Getting a rejected promise instead of an error response**: That only happens for network-level failures (DNS errors, dropped connections), not HTTP error status codes. Wrap the call in `try`/`catch` as shown above.

## Pricing

Retried attempts that fail are **not** billed. Only the final successful response counts. This makes `retries` effectively free insurance against transient failures:

| Configuration                             | Cost multiplier vs. basic |
| ----------------------------------------- | ------------------------- |
| Basic request                             | 1x                        |
| `js_render: true`                         | 5x                        |
| `premium_proxy: true`                     | 10x                       |
| `js_render: true` + `premium_proxy: true` | 25x                       |

<Note>`404` and `410` responses count as successful and are billed, since the request itself completed correctly.</Note>

## FAQ (Frequently Asked Questions)

<AccordionGroup>
  <Accordion title="What's the default number of retries?">
    0. Retries are disabled unless you explicitly pass `retries` to the constructor.
  </Accordion>

  <Accordion title="Why doesn't the SDK retry 429 or 500 responses?">
    The current Node.js SDK's retry list only includes `422`, `503`, `504`, and network errors. This differs from some other ZenRows SDKs, so don't assume a `429` will be retried automatically here.
  </Accordion>

  <Accordion title="Am I charged for retried requests that eventually fail?">
    No. Only successful requests consume your ZenRows balance.
  </Accordion>
</AccordionGroup>

## Look up a specific error code

For a full breakdown of ZenRows error codes (like `AUTH002` or `RESP001`) and how to resolve each one, see the [API Error Codes](/api-error-codes) reference.
