> ## 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 Python SDK

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

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

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

## Basic usage

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

    ```python theme={null} theme={null}
    from zenrows import ZenRowsClient

    client = ZenRowsClient("YOUR_ZENROWS_API_KEY")
    ```
  </Step>

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

    ```python theme={null} theme={null}
    from zenrows import ZenRowsClient

    client = ZenRowsClient("YOUR_ZENROWS_API_KEY")
    url = "https://www.scrapingcourse.com/ecommerce/"

    response = client.get(url)

    print(response.text)
    ```

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

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

  <Step title="Add request parameters">
    Pass a `params` dictionary to enable Universal Scraper API features like JavaScript rendering or Premium Proxies:

    ```python theme={null} theme={null}
    from zenrows import ZenRowsClient

    client = ZenRowsClient("YOUR_ZENROWS_API_KEY")
    url = "https://www.scrapingcourse.com/ecommerce/"

    response = client.get(url, params={
        "js_render": True,
        "premium_proxy": True,
        "proxy_country": "us",
    })

    print(response.text)
    ```

    <Tip>
      `params` 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 raise an exception on non-2xx responses, always check the status before using the content:

    ```python theme={null} theme={null}
    if response.ok:
        print(response.text)
    else:
        print(f"Request failed with status {response.status_code}: {response.text}")
    ```

    See [Error Handling and Retries](/universal-scraper-api/sdk/python/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_code` 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).
* **`response.text` 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).
* **Request hangs for a long time**: Pass a `timeout` keyword argument, e.g. `client.get(url, timeout=60)`. We recommend not going below 3 minutes for protected sites using `js_render`.
* **`429 Too Many Requests`**: You've hit your plan's concurrency or rate limit. See [Async Requests and Concurrency](/universal-scraper-api/sdk/python/async-concurrency).

## Pricing

Cost depends on the `params` 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 params for every request?">
    No, `params` is optional. `client.get(url)` alone works for public pages with no anti-bot protection.
  </Accordion>

  <Accordion title="Why doesn't a failed request raise an exception?">
    By design, the SDK returns the `requests.Response` object even for error status codes. Check `response.ok` or call `response.raise_for_status()` yourself. See [Error Handling and Retries](/universal-scraper-api/sdk/python/error-handling-and-retries).
  </Accordion>

  <Accordion title="Can I reuse the same client for multiple requests?">
    Yes. Create one `ZenRowsClient` instance and call `.get()`/`.post()`/`.put()` 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 and PUT Requests" icon="send" href="/universal-scraper-api/sdk/python/post-put-requests">
    Submit forms or data payloads.
  </Card>

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

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