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

# E-commerce: products, prices and reviews

> Get product prices, stock and ratings from any online store with Zenrows, compare prices across countries, and track thousands of products.

Retail sites are a top source of web data. This page covers the jobs behind price tracking, catalog building and competitor research on public product pages, using Amazon as the worked example. The same code works on the other retailers Zenrows has prepared for Extract, such as Walmart, eBay and Best Buy.

**Uses:** [Fetch](/fetch/api-reference) with [Adaptive Stealth Mode](/fetch/features/adaptive-stealth-mode) (`mode=auto`), [Extract](/extract/introduction) (`extract=auto`, [`extract_fields`](/extract/setup#selecting-the-fields-you-need)), [`proxy_country`](/fetch/features/proxy-country), [Batch](/batch/introduction)

## 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 get the product title, current price, list price and stock status of
https://www.amazon.com/dp/B08Z6X4NK3 as a shopper in the US sees it.
```

To track a whole catalog, give the agent the full list of product URLs and ask for the same fields in one batch.

## Get a product's price and stock

Send the product URL with `extract=auto`. Zenrows returns the page's fields as JSON in `parsed`, and `extract_fields` keeps only the groups you read. `proxy_country=us` shows the page a US shopper sees, with US prices and delivery.

<CodeGroup>
  ```python Python (Requests) theme={"dark"}
  import json
  import requests

  params = {
      "apikey": "YOUR_ZENROWS_API_KEY",
      "url": "https://www.amazon.com/dp/B08Z6X4NK3",
      "mode": "auto",
      "proxy_country": "us",
      "extract": "auto",
      "extract_fields": "product.title,product.asin,pricing,availability.status",
  }

  response = requests.get("https://api.zenrows.com/v1/", params=params, timeout=180)
  response.raise_for_status()

  product = response.json()["parsed"]
  print(json.dumps(product, indent=2))
  ```

  ```javascript Node.js theme={"dark"}
  const params = new URLSearchParams({
    apikey: "YOUR_ZENROWS_API_KEY",
    url: "https://www.amazon.com/dp/B08Z6X4NK3",
    mode: "auto",
    proxy_country: "us",
    extract: "auto",
    extract_fields: "product.title,product.asin,pricing,availability.status",
  });

  const response = await fetch(`https://api.zenrows.com/v1/?${params}`, {
    signal: AbortSignal.timeout(180_000),
  });
  if (!response.ok) throw new Error(`${response.status}: ${await response.text()}`);

  const { parsed } = await response.json();
  console.log(JSON.stringify(parsed, null, 2));
  ```

  ```bash cURL theme={"dark"}
  curl "https://api.zenrows.com/v1/?apikey=YOUR_ZENROWS_API_KEY&url=https%3A%2F%2Fwww.amazon.com%2Fdp%2FB08Z6X4NK3&mode=auto&proxy_country=us&extract=auto&extract_fields=product.title%2Cproduct.asin%2Cpricing%2Cavailability.status"
  ```
</CodeGroup>

## Example output

```json theme={"dark"}
{
  "availability": {
    "status": "in_stock"
  },
  "pricing": {
    "currency": "$",
    "current_price": 84,
    "deal_badge": null,
    "discount_percent": null,
    "list_price": 89.99
  },
  "product": {
    "asin": "B08Z6X4NK3",
    "title": "Logitech G413 SE Full-Size Mechanical Gaming Keyboard - Black"
  }
}
```

Drop `extract_fields` to get every field the page provides. On Amazon product pages that includes `ratings_summary` (average rating and star breakdown), `review_insights`, `feature_bullets`, `technical_specs` and `variations`.

<Tip>
  Field names come from each website, and pages of different kinds on one site can carry different groups. Send one request without `extract_fields`, read the names in `parsed`, then pin the ones you need.
</Tip>

## Turn a search page into product URLs

Search and category pages parse too. On Amazon, `parsed.results` holds one entry per listing with its ASIN, title, price and rating, and `parsed.pagination` holds the next page. Build product URLs from the ASINs and skip sponsored slots:

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

params = {
    "apikey": "YOUR_ZENROWS_API_KEY",
    "url": "https://www.amazon.com/s?k=mechanical+keyboard",
    "mode": "auto",
    "proxy_country": "us",
    "extract": "auto",
    "extract_fields": "results.asin,results.title,results.price,results.is_sponsored,pagination.next_page_url",
}

response = requests.get("https://api.zenrows.com/v1/", params=params, timeout=180)
response.raise_for_status()
parsed = response.json()["parsed"]

product_urls = [
    f"https://www.amazon.com/dp/{item['asin']}"
    for item in parsed["results"]
    if item.get("asin") and not item.get("is_sponsored")
]
print(len(product_urls), "product URLs")
print("\n".join(product_urls[:5]))
print("Next page:", parsed["pagination"]["next_page_url"])
```

```text theme={"dark"}
16 product URLs
https://www.amazon.com/dp/B08Z6X4NK3
https://www.amazon.com/dp/B0D14N2QZF
https://www.amazon.com/dp/B0HDC5TFDP
https://www.amazon.com/dp/B0DRFGNWV1
https://www.amazon.com/dp/B09LK1P1RD
Next page: /s?k=mechanical+keyboard&page=2&xpid=Ze0woZxmPTwql&qid=1792018194&ref=sr_pg_1
```

Prefix `next_page_url` with `https://www.amazon.com` to request the next page. To collect every product link on a smaller store, let [Crawl](/academy/foundations/discover-every-product) walk the catalog for you.

## Compare prices across countries

Retailers price by storefront and by shopper location. Request each country's storefront with a matching `proxy_country` and compare:

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

storefronts = {
    "us": "https://www.amazon.com/s?k=logitech+g413",
    "gb": "https://www.amazon.co.uk/s?k=logitech+g413",
}

for country, url in storefronts.items():
    params = {
        "apikey": "YOUR_ZENROWS_API_KEY",
        "url": url,
        "mode": "auto",
        "proxy_country": country,
        "extract": "auto",
        "extract_fields": "results.title,results.price",
    }
    response = requests.get("https://api.zenrows.com/v1/", params=params, timeout=180)
    response.raise_for_status()
    first = response.json()["parsed"]["results"][0]
    print(country, first["price"], first["title"][:50])
```

```text theme={"dark"}
us 89.99 Logitech G413 SE Full-Size Mechanical Gaming Keybo
gb 59.99 G413 SE Full-Size Mechanical Gaming Keyboard - Bac
```

Each price is in the storefront's currency (USD and GBP here). `proxy_country` takes any ISO 3166-1 alpha-2 code.

## Track many products with Batch

For hundreds or thousands of product URLs, submit them as one [Batch](/batch/developer-guide-restapi) job. Every task uses the same `zenrows_params`, and `external_id` ties each result back to your SKU or ASIN.

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

API = "https://async.api.zenrows.com/v1"
headers = {"X-API-Key": "YOUR_ZENROWS_API_KEY"}

asins = ["B08Z6X4NK3", "B0D14N2QZF", "B09LK1P1RD"]

job = requests.post(f"{API}/jobs", headers=headers, json={
    "type": "regular",
    "status": "closed",
    "zenrows_params": {
        "mode": "auto",
        "proxy_country": "us",
        "extract": "auto",
        "extract_fields": "product.title,pricing.current_price,availability.status",
    },
    "tasks": [{"url": f"https://www.amazon.com/dp/{a}", "external_id": a} for a in asins],
}, timeout=60).json()
job_id = job["job_id"]

# Wait for the run to finish
while True:
    run = requests.get(f"{API}/jobs/{job_id}", headers=headers, timeout=60).json()["latest_run"]
    if run["status"] in ("completed", "failed", "stopped"):
        break
    time.sleep(10)
print(run["status"], run["stats"])

# Read every successful result
results = requests.get(f"{API}/jobs/{job_id}/results", headers=headers,
                       params={"status": "successful"}, timeout=60).json()
for row in results["results"]:
    parsed = requests.get(row["result_url"], timeout=60).json()["parsed"]
    print(row["external_id"], parsed)
```

```text theme={"dark"}
completed {'total': 3, 'completed': 3, 'successful': 3, 'failed': 0, 'spend': {'credits': 30, 'cost': 0.03}}
B09LK1P1RD {'availability': {'status': 'in_stock'}, 'pricing': {'current_price': 179}, 'product': {'title': 'Logitech MX Mechanical Wireless Illuminated Keyboard Tactile - Graphite'}}
B0D14N2QZF {'availability': {'status': 'in_stock'}, 'pricing': {'current_price': 64}, 'product': {'title': 'AULA F75 Pro Wireless Mechanical Keyboard,75% Hot Swappable Custom Keyboard with Knob,RGB Backlit,Pre-lubed Reaper Switches,...'}}
B08Z6X4NK3 {'availability': {'status': 'in_stock'}, 'pricing': {'current_price': 84}, 'product': {'title': 'Logitech G413 SE Full-Size Mechanical Gaming Keyboard - Black'}}
```

For more than one page of results, follow `next_cursor`, as shown in [Collect your results](/batch/developer-guide-restapi#download-content). To run the same job every day, make it a [scheduled job](/academy/batch/scheduled-jobs).

## Customize it

| Change | How |
| - | - |
| Another retailer | Swap the URL. Check coverage with `GET /v1/extract/domains`, see [Extract endpoints](/extract/endpoints) |
| Add a new store to Extract | `POST /v1/extract/prepared-domains` with example URLs, see [Getting a domain prepared](/extract/endpoints#getting-a-domain-prepared) |
| Your own fields from your own selectors | `css_extractor={"price":".price"}`, see [CSS extractor](/fetch/features/css-extractor) |
| Ratings and review highlights | Omit `extract_fields`, or add `ratings_summary,review_insights` |
| Results pushed to your server | Add a webhook to the Batch job, see [Get notified with webhooks](/academy/batch/webhooks) |
| Daily price history | See [Build a price monitor](/academy/build-price-monitoring) |

## Troubleshooting

* **`pricing` is null or availability says the item can't ship**: the request came from a location the store doesn't deliver to. Add `proxy_country` for the storefront's country, for example `us` for amazon.com.
* **`REQS004` with an "Available fields" list**: this kind of page uses different field names. Pick names from the list in the error, or omit `extract_fields`. See [REQS004](/api-error-codes#REQS004).
* **`REQS007`**: this domain is ready to be prepared. Send example URLs to `POST /v1/extract/prepared-domains`, see [REQS007](/api-error-codes#REQS007).
* **Search results include sponsored slots**: filter on `is_sponsored`, as in the search example.

## Related recipes

<CardGroup cols={2}>
  <Card title="Build a price monitor" icon="chart-line" href="/academy/build-price-monitoring">
    Track prices on a schedule and store the history.
  </Card>

  <Card title="Get structured JSON from any page" icon="wand" href="/academy/extract/auto-extraction">
    How extract=auto, extract\_fields and prepared domains work.
  </Card>

  <Card title="Scrape a list of URLs" icon="list" href="/academy/scrape-from-a-list-of-urls">
    Batch jobs from the API, SDKs and CLI.
  </Card>

  <Card title="Get content as seen from any country" icon="world" href="/academy/fetch/geotargeting">
    Geotarget any request with proxy\_country.
  </Card>
</CardGroup>


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