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

# Extract Product Data from Webpages

> Detect product pages and extract a normalized product record with name, price, billing frequency, pricing model, features, images, and SKU.

Context.dev's Product API takes any product page URL (Amazon, TikTok Shop, or any SaaS/D2C website) and gives you a structured product record:

* Name, description, category of the product
* Pricing models, listed price and ISO currency code
* List of features, the product's target audience, and more

<Prompt description="Integrate Context.dev's Product API in your app" icon="sparkles" actions={["copy", "cursor"]}>
  I'm integrating Context.dev's Product API into my app. Help me:

  1. Install the official SDK for my language (`context.dev` on npm / PyPI / RubyGems, `context-dev/context-dev-php` on Packagist, `github.com/context-dot-dev/context-go-sdk` for Go).
  2. Read the API key from the `CONTEXT_DEV_API_KEY` environment variable. Never hardcode it.
  3. Call `POST /brand/ai/product` with a single `url` to extract one product. Always check that `is_product_page` is true **and** that `product` is non-null before reading product fields — non-product pages (blog posts, category grids, listing pages) return `is_product_page: false` and `product: null`.
  4. Use `maxAgeMs` (body field, defaults to 7 days, max 30 days, `0` for fresh) when I want a cached result. Use `timeoutMS` (max 300000) to bound long-running calls.
  5. Branch error handling on `error_code` from the response body. The common ones are `INPUT_VALIDATION_ERROR` (400), `WEBSITE_ACCESS_ERROR` (400, target unreachable or blocked), `UNAUTHORIZED` (401), `REQUEST_TIMEOUT` (408), and `INTERNAL_ERROR` (500).

  Docs: [https://docs.context.dev/guides/extract-product-from-websites](https://docs.context.dev/guides/extract-product-from-websites)
</Prompt>

## Prerequisites

* **A Context.dev API key.** Sign up at [context.dev/signup](https://context.dev/signup), copy the key from the [dashboard](https://context.dev/dashboard) (prefix `ctxt_secret_`), and export it:

  ```bash theme={null}
  export CONTEXT_DEV_API_KEY="ctxt_secret_..."
  ```

* **An SDK (optional).** Install for your language, or skip the install and call directly with `curl`:

  <CodeGroup>
    ```bash TypeScript theme={null}
    npm install context.dev
    ```

    ```bash Python theme={null}
    pip install context.dev
    ```

    ```bash Ruby theme={null}
    gem install context.dev
    ```

    ```bash Go theme={null}
    go get github.com/context-dot-dev/context-go-sdk
    ```

    ```bash PHP theme={null}
    composer require context-dev/context-dev-php
    ```
  </CodeGroup>

## Extract a single product

`POST /brand/ai/product` takes a single product URL and returns whether the page is a product page, the detected ecommerce platform, and the extracted product record.

<CodeGroup>
  ```bash cURL theme={null}
  curl -X POST https://api.context.dev/v1/brand/ai/product \
    -H "Authorization: Bearer $CONTEXT_DEV_API_KEY" \
    -H "Content-Type: application/json" \
    -d '{"url": "https://www.allbirds.com/products/mens-wool-runners"}'
  ```

  ```typescript TypeScript theme={null}
  import ContextDev from "context.dev";

  const client = new ContextDev({ apiKey: process.env.CONTEXT_DEV_API_KEY });

  const response = await client.ai.extractProduct({
    url: "https://www.allbirds.com/products/mens-wool-runners",
  });

  if (response.is_product_page && response.product) {
    console.log(
      response.product.name,
      response.product.price,
      response.product.currency,
    );
  } else {
    console.log("Not a product page");
  }
  ```

  ```python Python theme={null}
  import os
  from context.dev import ContextDev

  client = ContextDev(api_key=os.environ["CONTEXT_DEV_API_KEY"])

  response = client.ai.extract_product(
      url="https://www.allbirds.com/products/mens-wool-runners",
  )

  if response.is_product_page and response.product:
      print(response.product.name, response.product.price, response.product.currency)
  else:
      print("Not a product page")
  ```

  ```ruby Ruby theme={null}
  require "context_dev"

  client = ContextDev::Client.new(api_key: ENV.fetch("CONTEXT_DEV_API_KEY"))

  response = client.ai.extract_product(
    url: "https://www.allbirds.com/products/mens-wool-runners",
  )

  if response.is_product_page && response.product
    p = response.product
    puts "#{p.name} #{p.price} #{p.currency}"
  else
    puts "Not a product page"
  end
  ```

  ```go Go theme={null}
  package main

  import (
      "context"
      "fmt"
      "os"

      contextdev "github.com/context-dot-dev/context-go-sdk"
      "github.com/context-dot-dev/context-go-sdk/option"
  )

  func main() {
      client := contextdev.NewClient(
          option.WithAPIKey(os.Getenv("CONTEXT_DEV_API_KEY")),
      )

      response, err := client.AI.ExtractProduct(context.TODO(), contextdev.AIExtractProductParams{
          URL: "https://www.allbirds.com/products/mens-wool-runners",
      })
      if err != nil {
          panic(err)
      }

      if response.IsProductPage && response.Product != nil {
          fmt.Println(response.Product.Name, response.Product.Price, response.Product.Currency)
      } else {
          fmt.Println("Not a product page")
      }
  }
  ```

  ```php PHP theme={null}
  <?php

  use ContextDev\Client;

  $client = new Client(apiKey: getenv('CONTEXT_DEV_API_KEY'));

  $response = $client->ai->extractProduct(
      url: 'https://www.allbirds.com/products/mens-wool-runners',
  );

  if ($response->isProductPage && $response->product) {
      echo $response->product->name, ' ', $response->product->price, ' ', $response->product->currency, PHP_EOL;
  } else {
      echo 'Not a product page', PHP_EOL;
  }
  ```
</CodeGroup>

<Badge color="blue" icon="coins">
  10 credits per successful call
</Badge>

### Request Parameters

| Parameter   | Type                   | Description                                                                                                                                             |
| ----------- | ---------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `url`       | string (URI)           | **Required.** The product page URL.                                                                                                                     |
| `maxAgeMs`  | integer (0-2592000000) | Return a cached result if one exists younger than this many milliseconds. Set to `0` to always scrape fresh. Max 30 days. Defaults to `604800000` (7d). |
| `timeoutMS` | integer (1000-300000)  | Abort with a 408 if the request exceeds this many milliseconds. Max `300000` (5 min).                                                                   |

### Response

```json expandable theme={null}
{
  "is_product_page": true,
  "platform": "generic",
  "product": {
    "name": "Men's Wool Runner - Natural Grey (Light Grey Sole)",
    "description": "The Allbirds Men's Wool Runner is a classic casual sneaker made from soft, responsibly-sourced Merino wool that is breathable, lightweight, and machine washable…",
    "price": 110,
    "currency": "USD",
    "billing_frequency": "one_time",
    "pricing_model": "flat",
    "url": "https://www.allbirds.com/products/mens-wool-runners",
    "category": "Footwear",
    "features": [
      "Soft Merino wool upper",
      "Machine washable (remove insoles and laces)",
      "Sugarcane-based SweetFoam® midsole for support and cushioning",
      "Breathable and lightweight",
      "Responsibly sourced materials"
    ],
    "target_audience": ["Men"],
    "tags": ["wool", "sneaker", "men's shoes", "casual", "sustainable"],
    "image_url": "https://media.brand.dev/34f4b265-7198-4eaa-a4a3-e0b87f40de29.png",
    "images": [
      "https://media.brand.dev/2177175e-aae3-4179-b59d-0df12fb1b59d.png",
      "https://media.brand.dev/04027441-7ec6-4928-94e5-5b74c21347b8.png",
      "https://media.brand.dev/f47cbb41-7c3b-4fa1-92c5-4764560dda3e.png"
    ],
    "sku": "MENS_WOOL_RUNNERS"
  }
}
```

| Field                       | Type           | Description                                                                                                   |
| --------------------------- | -------------- | ------------------------------------------------------------------------------------------------------------- |
| `is_product_page`           | boolean        | Whether the URL was recognized as a product detail page.                                                      |
| `platform`                  | string \| null | One of `amazon`, `tiktok_shop`, `etsy`, `generic`. `null` when not a product page.                            |
| `product`                   | object \| null | The extracted product record. `null` when not a product page or when extraction failed (e.g. bot protection). |
| `product.name`              | string         | Product name.                                                                                                 |
| `product.description`       | string         | Product description.                                                                                          |
| `product.price`             | number \| null | Numeric price for the listed currency. `null` when no price is shown.                                         |
| `product.currency`          | string \| null | ISO currency code (e.g. `USD`, `EUR`).                                                                        |
| `product.billing_frequency` | string \| null | One of `monthly`, `yearly`, `one_time`, `usage_based`.                                                        |
| `product.pricing_model`     | string \| null | One of `per_seat`, `flat`, `tiered`, `freemium`, `custom`.                                                    |
| `product.url`               | string \| null | Canonical product page URL.                                                                                   |
| `product.category`          | string \| null | Single category label.                                                                                        |
| `product.features`          | string\[]      | Bulleted feature claims, one sentence each.                                                                   |
| `product.target_audience`   | string\[]      | Audience tags (e.g. `Men`, `Small teams`).                                                                    |
| `product.tags`              | string\[]      | Free-form tags.                                                                                               |
| `product.image_url`         | string \| null | Primary image URL (CDN-hosted).                                                                               |
| `product.images`            | string\[]      | Up to 7 product image URLs (CDN-hosted).                                                                      |
| `product.sku`               | string \| null | Stock keeping unit. `null` for SaaS, services, and other non-inventoried offerings.                           |

## Handle errors

A non-2xx response returns an `{ message, error_code }` envelope:

| Status | `error_code`             | Meaning                                                                                                   | What to do                                                   |
| ------ | ------------------------ | --------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------ |
| 400    | `INPUT_VALIDATION_ERROR` | Missing or malformed `url`                                                                                | Validate input before the call.                              |
| 400    | `WEBSITE_ACCESS_ERROR`   | The target page was unreachable, blocked by a WAF, timed out during scraping, or served no usable content | Treat as "page cannot be extracted." Retry once; don't loop. |
| 401    | `UNAUTHORIZED`           | API key missing, invalid, or deleted                                                                      | Re-check the env var and the dashboard.                      |
| 408    | `REQUEST_TIMEOUT`        | Page didn't finish extracting before `timeoutMS` (or 5 min default)                                       | Retry once with backoff.                                     |
| 500    | `INTERNAL_ERROR`         | Transient server error                                                                                    | Retry once with backoff; if it persists, contact support.    |

A 400 from omitting `url`:

```json expandable theme={null}
{
  "error_code": "INPUT_VALIDATION_ERROR",
  "message": [
    {
      "code": "invalid_type",
      "expected": "string",
      "received": "undefined",
      "path": ["url"],
      "message": "Required"
    }
  ]
}
```

For the full catalog of error codes, see [Troubleshooting](/optimization/troubleshooting).

## Use cases

* **Build a printable product catalog**: Use a website's sitemap to find all products, run `extract_product` on each page
* **Procurement / ERP auto-fill**: when a buyer pastes a product URL into a procurement form, populate `sku`, `name`, `description`, `currency`, `price` and `features` automatically.

## Next steps

<CardGroup cols={2}>
  <Card title="Prefetch for Faster Response" icon="bolt" href="/optimization/prefetching">
    Hide cold-hit latency from your users.
  </Card>

  <Card title="Handle Rate Limits" icon="gauge-high" href="/optimization/rate-limits">
    Backoff strategies, client cache, and prefetch fallbacks.
  </Card>

  <Card title="Best Practices" icon="list-check" href="/optimization/best-practices">
    Caching, error handling, and key hygiene.
  </Card>

  <Card title="Troubleshooting" icon="bug" href="/optimization/troubleshooting">
    Status codes, retry patterns, and common errors.
  </Card>
</CardGroup>
