> ## 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.

# Making your first request with the Node.js SDK

> Make your first request with the ZenRows Node.js SDK in a few lines of code.

This guide makes a single GET request with the ZenRows Node.js SDK: create a client, send the request, read the response.

<Note>Haven't installed the SDK yet? Follow [Installation](/universal-scraper-api/sdk/nodejs/installation) first.</Note>

## Basic usage

<Steps>
  <Step title="Create a client">
    Import `ZenRows` and instantiate it with your API key:

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

    const client = new ZenRows("YOUR_ZENROWS_API_KEY");
    ```
  </Step>

  <Step title="Send a GET request">
    Call `client.get()` with the URL you want to scrape:

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

    const client = new ZenRows("YOUR_ZENROWS_API_KEY");
    const url = "https://www.scrapingcourse.com/ecommerce/";

    (async () => {
      const response = await client.get(url);
      const html = await response.text();

      console.log(html);
    })();
    ```

    `response` is a standard `fetch` `Response` object.

    <Tip>See [The Response Object](/universal-scraper-api/sdk/nodejs/response-object) for everything available on it.</Tip>
  </Step>

  <Step title="Add request parameters">
    Pass a config object as the second argument to enable Universal Scraper API features like JavaScript rendering or Premium Proxies:

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

    const client = new ZenRows("YOUR_ZENROWS_API_KEY");
    const url = "https://www.scrapingcourse.com/ecommerce/";

    (async () => {
      const response = await client.get(url, {
        js_render: true,
        premium_proxy: true,
        proxy_country: "us",
      });

      const html = await response.text();

      console.log(html);
    })();
    ```

    <Tip>
      The config object accepts every parameter documented in the [Universal Scraper API setup guide](/universal-scraper-api/universal-scraper-api-setup), including `autoparse`, `css_extractor`, `wait_for`, `block_resources`, and `outputs`. They all work exactly the same way through the SDK.
    </Tip>
  </Step>

  <Step title="Check the result">
    Since the SDK doesn't throw an exception on non-2xx responses, always check the status before using the content:

    ```javascript theme={null} theme={null}
    if (response.ok) {
      const html = await response.text();
      console.log(html);
    } else {
      console.log(`Request failed with status ${response.status}: ${await response.text()}`);
    }
    ```

    See [Error Handling and Retries](/universal-scraper-api/sdk/nodejs/error-handling-and-retries) for how to configure automatic retries so you don't have to handle every transient failure manually.
  </Step>
</Steps>

## Troubleshooting

* **`response.status` is `401` or similar auth error**: Double-check your API key was copied correctly from the [ZenRows Request Builder](https://app.zenrows.com/builder) with no extra whitespace. See [`AUTH002`](/api-error-codes#auth002-invalid-api-key).
* **The response body is empty or looks like an error page, not the target site**: The target likely needs `js_render: true` and/or `premium_proxy: true`. See [`REQS002`](/api-error-codes#reqs002-request-requirements-unsatisfied).
* **`await response.text()` hangs or the script never finishes**: Make sure the call is wrapped in an `async` function and awaited; the SDK doesn't have a `timeout` option, so a hung request will wait indefinitely unless your own code aborts it (e.g. with `AbortController`).
* **`429 Too Many Requests`**: You've hit your plan's concurrency or rate limit. See [Concurrency](/universal-scraper-api/sdk/nodejs/async-concurrency).

## Pricing

Cost depends on the config you send, not on the SDK itself:

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

Only successful requests are billed. See [ZenRows Pricing](/first-steps/pricing) for exact per-plan rates.

## FAQ (Frequently Asked Questions)

<AccordionGroup>
  <Accordion title="Do I need a config object for every request?">
    No, the config argument is optional. `client.get(url)` alone works for public pages with no anti-bot protection.
  </Accordion>

  <Accordion title="Why doesn't a failed request throw an exception?">
    By design, the SDK resolves with the `Response` object even for error status codes, matching how native `fetch` behaves. Check `response.ok` or `response.status` yourself. See [Error Handling and Retries](/universal-scraper-api/sdk/nodejs/error-handling-and-retries).
  </Accordion>

  <Accordion title="Can I reuse the same client for multiple requests?">
    Yes. Create one `ZenRows` instance and call `.get()`/`.post()` on it as many times as needed, there's no need to recreate it per request.
  </Accordion>
</AccordionGroup>

## What's next

<CardGroup cols={3}>
  <Card title="POST Requests" icon="send" href="/universal-scraper-api/sdk/nodejs/post-put-requests">
    Submit forms or data payloads.
  </Card>

  <Card title="Concurrency" icon="bolt" href="/universal-scraper-api/sdk/nodejs/async-concurrency">
    Scrape many URLs in parallel.
  </Card>

  <Card title="Custom Headers" icon="tags" href="/universal-scraper-api/sdk/nodejs/custom-headers">
    Forward specific headers to the target site.
  </Card>
</CardGroup>
