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

> ## Agent Instructions
> The Zenrows API (Fetch, Extract via extract=auto, and Batch) is described in OpenAPI 3.1 at https://www.zenrows.com/openapi.json and listed in the RFC 9727 API catalog at https://www.zenrows.com/.well-known/api-catalog. Use the spec for exact parameter names and types.

# Lesson 1: Make your first request

> Start the Foundations course: get a Zenrows API key, fetch your first page with Python, and get it back as clean markdown.

This is the first of six lessons. Across the course you build one project, a **product-catalog tracker** for the <a href="https://www.scrapingcourse.com/ecommerce/" rel="nofollow">ScrapingCourse e-commerce demo store</a>, and each lesson adds one Zenrows primitive to it.

**What you'll build:** a Python script, `tracker.py`, that downloads the store's catalog page through Fetch and prints it as HTML and as markdown.

**What you'll learn:**

* How a Fetch request is shaped: one endpoint, your API key, the target URL, and parameters.
* How to read what a request cost and how much concurrency you have left.
* Why `mode=auto` is the default way to fetch, and how `response_type=markdown` changes the output.

**Time:** about 10 minutes. **You need:** Python 3.10 or later.

## Set up your project

<Steps>
  <Step title="Get your API key">
    [Sign up for a free Zenrows account](https://app.zenrows.com/register) and copy your API key from the [dashboard](https://app.zenrows.com/dashboard). The free trial includes enough credits for the whole course.
  </Step>

  <Step title="Create a project folder">
    ```bash theme={"dark"}
    mkdir catalog-tracker && cd catalog-tracker
    python -m venv .venv && source .venv/bin/activate
    pip install requests
    ```

    You'll keep every file from the course in this folder.
  </Step>
</Steps>

## Fetch the catalog page

Every Fetch request is a `GET` to `https://api.zenrows.com/v1/` with two parameters: `apikey` and the `url` you want. Create `tracker.py`:

```python Python (Requests) theme={"dark"}
import requests

ZENROWS_API_KEY = "YOUR_ZENROWS_API_KEY"
CATALOG_URL = "https://www.scrapingcourse.com/ecommerce/"

params = {"apikey": ZENROWS_API_KEY, "url": CATALOG_URL}
response = requests.get("https://api.zenrows.com/v1/", params=params)
response.raise_for_status()

html = response.text
print("Status:", response.status_code)
print("HTML length:", len(html))
print("Products on the page:", html.count("woocommerce-loop-product__title"))
print("Cost:", response.headers["X-Request-Cost"])
print("Concurrency:", response.headers["Concurrency-Remaining"], "of", response.headers["Concurrency-Limit"], "free")
```

Replace `YOUR_ZENROWS_API_KEY` with your key and run it:

```bash theme={"dark"}
python tracker.py
```

You should see output like this:

```text theme={"dark"}
Status: 200
HTML length: 82776
Products on the page: 16
Cost: 0.001
Concurrency: 4 of 5 free
```

Zenrows fetched the page for you and returned the store's own HTML, all 16 products of the first catalog page included.

## Read the response headers

Every response carries headers that tell you what the request did:

| Header | What it tells you |
| - | - |
| `X-Request-Cost` | What this request cost, in USD. `0.001` is 1 credit. |
| `Concurrency-Limit` | How many requests your plan runs at the same time. |
| `Concurrency-Remaining` | How many of those slots were free when this request ran. |
| `X-Request-Id` | The request's ID. Quote it when you contact support. |
| `Zr-Final-Url` | The URL the page ended on, after any redirects. |

In [Lesson 4](/academy/foundations/scrape-at-scale), Batch uses that concurrency for you when you scrape many pages at once. The full list is in the [Fetch API reference](/fetch/api-reference#response-headers).

## Turn on Adaptive Stealth Mode

The demo store answers any client. Real stores often sit behind anti-bot systems or render products with JavaScript, and those need a headless browser, premium proxies, or both. You don't have to work out which: add `mode=auto` ([Adaptive Stealth Mode](/fetch/features/adaptive-stealth-mode)) and Zenrows starts with the cheapest configuration that works for the site, escalates when the page needs more, and bills you for the configuration that succeeds.

That makes `mode=auto` the right default for the tracker. Replace the contents of `tracker.py` with a small `fetch()` helper that sends it on every call:

```python Python (Requests) theme={"dark"}
import requests

ZENROWS_API_KEY = "YOUR_ZENROWS_API_KEY"
CATALOG_URL = "https://www.scrapingcourse.com/ecommerce/"

def fetch(url, **options):
    params = {"apikey": ZENROWS_API_KEY, "url": url, "mode": "auto", **options}
    response = requests.get("https://api.zenrows.com/v1/", params=params)
    response.raise_for_status()
    return response

response = fetch(CATALOG_URL)
print("Products on the page:", response.text.count("woocommerce-loop-product__title"))
print("Cost:", response.headers["X-Request-Cost"])
```

Run it again. The store is easy to reach, so `mode=auto` settles on the basic configuration and the cost stays at 1 credit:

```text theme={"dark"}
Products on the page: 16
Cost: 0.001
```

<Note>
  `mode=auto` manages `js_render` and `premium_proxy` for you, so leave those two out when you send it. When you want to pin the configuration yourself, drop `mode=auto` and set them directly, see [Adaptive Stealth Mode vs manual configuration](/fetch/features/adaptive-stealth-mode#adaptive-stealth-mode-vs-manual-configuration).
</Note>

## Get the page as markdown

HTML is great for a parser and noisy for a person or an LLM. Pass `response_type=markdown` and Zenrows returns the same page as clean markdown. Add these lines to the end of `tracker.py`:

```python Python (Requests) theme={"dark"}
markdown = fetch(CATALOG_URL, response_type="markdown").text
start = markdown.index("# Shop")
print(markdown[start:start + 500])
```

Run the script. The catalog now reads like a document (trimmed):

```markdown theme={"dark"}
# Shop

Default sorting Sort by popularity Sort by latest Sort by price: low to high Sort by price: high to low

Showing 1–16 of 188 results

- 1
- [2](https://www.scrapingcourse.com/ecommerce/page/2/)
- [3](https://www.scrapingcourse.com/ecommerce/page/3/)
- [4](https://www.scrapingcourse.com/ecommerce/page/4/)
- …
- [12](https://www.scrapingcourse.com/ecommerce/page/12/)
```

The store has 188 products spread over 12 catalog pages. Collecting them is the job of the next three lessons.

<Tip>
  **Do the same from the CLI.** With the [Zenrows CLI](/cli/fetch/introduction) installed (`npm install -g @zenrows/cli`), this prints the catalog as markdown. The CLI uses `mode=auto` by default:

  ```bash theme={"dark"}
  zenrows fetch https://www.scrapingcourse.com/ecommerce/ --output markdown
  ```
</Tip>

## Ask your AI agent

With the [Zenrows MCP server](/mcp/overview) connected to Claude, Cursor or any MCP client, paste this prompt. Your agent picks the right Zenrows tools and settings on its own.

```text theme={"dark"}
Use Zenrows to read https://www.scrapingcourse.com/ecommerce/ and tell me
how many products the store has and the names and prices of the first five.
```

## What you built

`tracker.py` now has a reusable `fetch()` helper that sends every request with Adaptive Stealth Mode. You can:

* Fetch any page with Fetch and read its HTML or markdown.
* Read `X-Request-Cost` and the concurrency headers to track spend and capacity.
* Explain why `mode=auto` is the default: it picks the configuration per site and bills the one that succeeds.

## Next lesson

<Card title="Lesson 2: Turn a product page into JSON" icon="arrow-right" href="/academy/foundations/extract-product-data">
  Skip HTML parsing: get product names, prices and links as JSON with Extract and CSS selectors.
</Card>


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.