# Kohl's Scraper - Products, Prices, Ratings & Reviews (`abotapi/kohls-com-scraper`) Actor

Scrape Kohl's (kohls.com) products by keyword search or category browsing. Extract product names, brands, prices, descriptions, ratings, customer reviews and detailed product information. Ideal for price comparison, competitor analysis, product research and retail market intelligence.

- **URL**: https://apify.com/abotapi/kohls-com-scraper.md
- **Developed by:** [Abot API](https://apify.com/abotapi) (community)
- **Security:** Limited permissions
- **Stats:** 1 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.60 / 1,000 product records

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#pay-per-event

## What's an Apify Actor?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

## Kohl's Product Scraper

Scrape Kohls.com product listings: keyword search, category browsing, full product details, prices, ratings and customer reviews. Kohl's is one of the largest US department-store chains (clothing, shoes, home, kitchen, bedding, toys, jewelry and more), and this actor turns its catalog into clean structured rows for price monitoring, assortment analytics and competitor research.

### Why This Scraper?

- **Two ways in.** Point it at keyword searches, or paste any storefront links - search pages, category/catalog pages and product pages all walk with the same grammar.
- **Deep product records.** Every listing row carries title, price (with sale and was-price ranges), rating, review count, badges and the full colorway list; detail enrichment adds the description, brand, per-variant SKUs, availability, sizes, image gallery and breadcrumbs.
- **Customer reviews included.** Rating summary, star distribution and up to 20 review texts per product, with verified-purchaser and fit/size feedback fields.
- **Scheduled-run friendly.** Incremental mode classifies every product NEW / UPDATED / UNCHANGED / EXPIRED across runs, so a daily monitor pushes only what moved, and resume mode continues an interrupted walk exactly where it stopped.
- **Fast and light.** The actor reads the storefront's own page data directly over US connections, a few seconds per page, and moves to a fresh connection automatically when one is refused.

### Use Cases

- **Who:** pricing teams tracking Kohl's promos on competitive assortments - sale and was-price ranges land per row, updated per scheduled run.
- **Who:** sellers and brands auditing their shelf - badges (Best Seller, clearance), colorway coverage, availability and review velocity per product.
- **Who:** analysts building category datasets - walk any catalog page with the same card grammar and export straight to CSV/Excel or a warehouse.

### Data You Get

> Sample shape: values are illustrative placeholders, not from a live record.

| Field | Example |
| --- | --- |
| `title` | Nike V5 RNR Women's Running Shoes |
| `brand` | Nike |
| `price` / `priceMax` | 25.99 / 39.99 (sale range) |
| `wasPrice` / `wasPriceMax` | 39.99 / null (regular price) |
| `priceLabel` | For Price, Add To Cart (MAP-hidden items) |
| `rating` | 4.2 |
| `reviewsCount` | 468 |
| `badges` | \["Best Seller"] |
| `colors` | \["White", "Pink Black", "Silt Red"] |
| `colorsOutOfStock` | \["White Grey Metallic"] |
| `sizes` | \["5", "5.5", "6", ...] |
| `sku` / `skus` | 48518782 / \[per-variant SKUs] |
| `availability` | InStock |
| `description` | Full product description text |
| `imageUrls` | Full image gallery |
| `breadcrumbs` | \[Women > Shoes > Running Shoes] |
| `department` / `category` / `subcategory` | Womens / Shoes / Sneakers (first three levels of the product-page breadcrumb trail, with the store root and the product's own leaf dropped; product-page rows only, null otherwise) |
| `reviewsAverage` / `reviewsRecommended` | 4.2 / 350 |
| `ratingDistribution` | \[{stars: 5, count: 300}, ...] |
| `reviews` | \[{rating, title, text, author, verifiedPurchaser, fit, ...}] |
| `changeType` / `changedFields` | NEW / UPDATED / \[price] (incremental mode) |
| `priceChanges` / `variantChanges` | old and new value per moved price field / per-variant changes (present only on compact UPDATED rows, `compactUpdates` on) |

Each run also writes a `RUN_SUMMARY` record to the run's key-value store: records pushed, product-page enrichments charged, review units charged, pages served and whether the run was partial.

### How to Use

#### Run it from your code

Python:

```python
from apify_client import ApifyClient

client = ApifyClient("YOUR_APIFY_TOKEN")
run = client.actor("abotapi/kohls-com-scraper").call(run_input={
    "mode": "search",
    "searchTerms": ["womens jeans"],
    "maxItems": 50,
})
for item in client.dataset(run["defaultDatasetId"]).iterate_items():
    print(item["title"], item["price"])
```

JavaScript:

```javascript
import { Actor } from 'apify';

const run = await Actor.call('abotapi/kohls-com-scraper', {
    mode: 'urls',
    urls: ['https://www.kohls.com/catalog/mens-clothing.jsp'],
    maxItems: 50,
});
const { default: dataset } = await Actor.openDataset(run.defaultDatasetId);
await dataset.forEach((item) => console.log(item.title, item.price));
```

**Search by keyword:**

```json
{
  "mode": "search",
  "searchTerms": ["womens jeans", "air fryer"],
  "maxItems": 50
}
```

**Walk a category:**

```json
{
  "mode": "urls",
  "urls": ["https://www.kohls.com/catalog/mens-clothing.jsp"],
  "maxItems": 100
}
```

**One product, reviews capped:**

```json
{
  "mode": "urls",
  "urls": ["https://www.kohls.com/product/prd-7327369/nike-v5-rnr-womens-running-shoes.jsp"],
  "maxReviews": 5
}
```

**Scheduled price monitor:**

```json
{
  "mode": "search",
  "searchTerms": ["womens jeans"],
  "maxItems": 200,
  "fetchReviews": false,
  "incrementalMode": true
}
```

#### Links mode

Leave `urls` empty-handed (omit it) and the actor walks its built-in three links: a keyword search page, a category page and a product page. An empty list also walks the built-in three links. On pasted search and category links, filter and sort parameters are kept on every page and a `WS` offset sets the start position; product links read that one product. A category link without its filter part (for example `/catalog/mens-clothing.jsp`) is matched to that category in the storefront's menu; a bare category link the menu does not know is skipped with a warning, so paste such links with their full address (including the `CN=` part). Links that are not search, category or product pages are skipped with a warning. A product link that no longer exists on the storefront is skipped with a warning and bills nothing. A search that matches nothing ends with the message "No results matched this run's inputs".

#### Tips

- Start from a keyword search for quick pulls; paste category links for complete assortment walks.
- The storefront semantic-matches nonsense queries to real results - a "no match" search is not reachable by input, so judge runs by row count and the run status message.
- What incremental mode compares: rows from search and category pages compare only the listing-card fields (title, price, price range, was price, discount, price label, rating, review count, image, badges and colours); pasted product links compare the full product-page record (availability, colours out of stock, sizes, specs, sellers, description and categories as well). Reviews, scrape time, grid position, the card slot id (`webId`, which moves with the ranking) and list name are never compared. Use `changeScope` = `priceAndStock` to narrow the comparison to price fields; availability and colours out of stock are tracked only on product-link rows. `EXPIRED` means a product dropped out of the scanned listing; without opening its page a listing drop and a delisting look the same, so read `availability` for stock. Changing the scope or the ignore list changes the automatic baseline, so the first run afterwards reports every row as NEW; set a State key to keep one baseline.
- Compact UPDATED rows no longer carry the product-page data, so the product-page enrichment event and review units are not charged for them.
- Scheduled incremental runs keep the walk cheap: unchanged rows are suppressed before any product page is re-read and bill nothing.
- Sale prices arrive as ranges (`price` + `priceMax`); MAP-hidden items carry `priceLabel` "For Price, Add To Cart" and no price until a detail read finds one.

### Input Parameters

| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `mode` | select | urls | `search` walks keyword searches; `urls` walks pasted storefront links. |
| `searchTerms` | string list | womens jeans | Keyword searches to walk (search mode). If a run sends search terms but leaves the sample links untouched, the search terms are walked and the sample links are ignored. |
| `urls` | URL list | 3 example links | Search pages, category/catalog pages and product pages (links mode). |
| `maxItems` | integer | 20 | Maximum records per run, up to 100000. 0 means the 100000 cap. |
| `fetchDetails` | checkbox | true | Read each product's detail page (description, SKUs, sizes, gallery). |
| `fetchReviews` | checkbox | true | Collect rating summary and review texts per product. |
| `maxReviews` | integer | 20 | Review texts collected per product, up to 1000. 0 skips texts. |
| `incrementalMode` | checkbox | false | Classify rows NEW/UPDATED/UNCHANGED/EXPIRED across scheduled runs. |
| `emitUnchanged` | checkbox | false | Also emit rows whose tracked fields did not change. |
| `emitExpired` | checkbox | false | Emit EXPIRED rows for products that left a fully-scanned scope. |
| `proxyConfiguration` | proxy | Automatic US | Automatic US proxies work for most runs. Residential US gives the most reliable results; it is your choice and uses more of your plan's proxy traffic. |
| `resumeFromRunId` | string | (hidden) | Internal: continues an interrupted run; collected records are skipped. |
| `stateKey` | string | (hidden) | Internal: names the incremental baseline; empty derives it from the scope. |
| `expireAfterMissedScans` | integer | 1 | With Emit expired records: mark a product EXPIRED only after it was missing from this many consecutive complete scans. |
| `changeScope` | select | all | `all` compares every field the row carries; `priceAndStock` marks a row UPDATED only when price, was price, discount or price label moved (availability and colours out of stock count on product-link rows only). |
| `compactUpdates` | checkbox | false | UPDATED and EXPIRED rows carry only identifiers, current price and availability, `priceChanges` and `variantChanges`. NEW rows stay full. |
| `ignoreFieldsForChanges` | string list | (hidden) | Ignore fields when detecting changes (incremental mode only): field names, or dotted paths such as `offers.price`, that never decide UPDATED. They stay in the output. |
| `mcpConnectors` | string list | (hidden) | Internal: connector ids that receive records as they push. |
| `notionParentPageUrl` | string | (hidden) | Internal: parent page for the Notion connector. |
| `maxNotifyListings` | integer | 50 (hidden) | Internal: cap on records sent to connectors per run. |

All fields are also accepted through the API exactly as documented, including the hidden internal ones.

### Output Example

> Sample shape: values are illustrative placeholders, not from a live record.

```json
{
  "recordId": "kohls:7327369",
  "productId": "7327369",
  "title": "Nike V5 RNR Women's Running Shoes",
  "brand": "Nike",
  "price": 95.0,
  "priceMax": null,
  "wasPrice": null,
  "currency": "USD",
  "rating": 4.2,
  "reviewsCount": 468,
  "badges": ["Best Seller"],
  "colors": ["Pink Black", "White", "Silt Red"],
  "availability": "InStock",
  "url": "https://www.kohls.com/product/prd-7327369/nike-v5-rnr-womens-running-shoes.jsp"
}
```

Product rows with the detail enrichment on, from a live category walk.

### Plan Requirement

This actor requires an Apify paid plan, because every page is read through Apify proxy connections and the Apify Free plan includes no proxy access. Automatic US proxies are the default and work for most runs; residential US proxies give the most reliable results (your choice, and they use more of your plan's proxy traffic). If the selected connection is refused for your account, the run says so in the log and continues on a backup connection where one is available; when none is, the run ends with a message asking you to upgrade at https://apify.com/pricing or supply your own proxy URLs.

### FAQ

#### Is it legal to scrape Kohls.com?

The actor collects publicly available product data from the storefront's own pages, at a polite pace, without logging into any account. Review your use case and the site's terms before running at scale.

#### Why does a run fail with a connection problem?

Individual proxy connections are occasionally refused. The actor retries each page on fresh connections (up to five per page) and stops early when the storefront refuses a long run of connections in a row, so a closed window ends quickly instead of running to the time limit. Retry later; the same input usually succeeds on the next run.

#### How much does a run cost?

You pay the actor's per-event pricing (see the Pricing tab) plus your Apify plan's proxy usage. Four things are billed. Apart from the run start, each is charged only for rows actually pushed to the dataset:

- **Actor start:** one per run.
- **Dataset item:** one per pushed row, every row.
- **Product-page enrichment:** one per pushed row whose product page was read: listing rows with details on, and every pasted product link (a product link always reads its page, even with details off).
- **Review unit:** one per pushed row that delivered review texts, regardless of how many (up to `maxReviews`, at most 1000).

Card-only listing rows (details off), suppressed rows, and unchanged listing rows in incremental mode bill no enrichment or review fee. A pasted product link is re-read on every run, so with Emit unchanged records on, its unchanged row is still billed the product-page enrichment and any review unit. Proxy traffic is billed separately by your Apify plan.

#### Can I track price changes daily?

Yes - schedule the run with `incrementalMode` on and the same inputs. Only NEW and UPDATED rows push, each with `changedFields` naming what moved.

### Send results into your apps (MCP connectors)

Connect the actor to Notion, Google Sheets and other MCP-capable apps from the Apify Console: records this run pushed are delivered as they land, capped per run. Connector settings ship hidden in the input and need no attention unless you use connectors.

### 🔗 Want more fashion data?

Pair this actor with these related scrapers from the same team:

<table>
<tr><td>👗 <a href="https://apify.com/abotapi/nuuly-com-scraper"><b>Nuuly Scraper</b></a><br>Scrape Nuuly clothing rental products by category, brand or URL. Extract product names...</td><td>👗 <a href="https://apify.com/abotapi/fashionnova-com-scraper"><b>Fashion Nova Scraper</b></a><br>Scrape Fashion Nova products by keyword, collection or URL. Extract product names...</td></tr>
<tr><td>👗 <a href="https://apify.com/abotapi/showpo-com-scraper"><b>Showpo Scraper</b></a><br>Scrape Showpo women's fashion by keyword, collection or URL. Extract product prices...</td><td>👗 <a href="https://apify.com/abotapi/renttherunway-scraper"><b>Rent the Runway Scraper</b></a><br>Scrape Rent the Runway (renttherunway.com) designer clothing and accessories by category...</td></tr>
<tr><td>👗 <a href="https://apify.com/abotapi/zozotown-scraper"><b>ZOZOTOWN</b></a><br>Scrape ZOZOTOWN (zozo.jp), Japan's largest fashion marketplace: name, brand, JPY price...</td><td>🥦 <a href="https://apify.com/abotapi/elcorteingles-es-scraper"><b>El Corte Inglés Scraper</b></a><br>Scrape El Corte Inglés elcorteingles.es products across fashion, electronics, home...</td></tr>
</table>

👉 [Browse all abotapi scrapers](https://apify.com/abotapi)

### 💬 Support

- 🐞 **Found a bug or a missing field?** Open a ticket on the [Issues tab](https://apify.com/abotapi/kohls-com-scraper/issues/open). We usually reply within hours.
- 🛠️ **Need another site, extra fields or a private build?** Email <contact@abotapi.com>.
- ⭐ **Enjoying it?** A quick review on the actor page helps other users find it.

# Actor input Schema

## `mode` (type: `string`):

Search mode walks keyword search result pages. Links mode walks the storefront links you paste: search pages, category pages and product pages.

## `searchTerms` (type: `array`):

Keyword searches to walk (search mode). Each term walks its result pages in order.

## `urls` (type: `array`):

Links to walk (links mode): keyword search pages, category/catalog pages and product pages. Bare search/catalog/product links all work.

## `proxyConfiguration` (type: `object`):

Automatic US proxies are the default and work for most runs. Residential US proxies give the most reliable results; choosing them is your call, and they use more of your plan's proxy traffic. The Apify Free plan includes no proxy access, so supply your own proxy URLs or use a paid plan.

## `fetchDetails` (type: `boolean`):

Read each product's detail page: description, brand, per-variant SKUs, availability, colors (with out-of-stock markers), sizes, image gallery, breadcrumbs and specs. Billed per enriched record.

## `fetchReviews` (type: `boolean`):

Collect customer reviews per product (rating summary, distribution and up to Max reviews review texts). Billed per record that delivered reviews.

## `maxReviews` (type: `integer`):

Cap on review texts collected per product (values above 1000 are treated as 1000). 0 skips review texts (rating summary still fetched with Fetch reviews on).

## `resumeFromRunId` (type: `string`):

Internal: continues an interrupted run. Records already collected by that run are skipped, and the walk picks up where it stopped.

## `incrementalMode` (type: `boolean`):

Track changes across scheduled runs: each product is classified NEW, UPDATED, UNCHANGED or EXPIRED against the previous run of the same scope, and unchanged rows are suppressed unless you ask for them. Rows from search and category pages compare only the fields on the listing card: title, price, price range, was price, discount, price label, rating, review count, image, badges and colours. Pasted product links compare the full product-page record (availability, colours out of stock, sizes, specs, sellers, description and categories as well). A change in a compared field marks the row UPDATED and lists it in changedFields. Never compared (kept in the output): reviews, scrape time, grid position and list name. Use Change scope to narrow the comparison to price and availability.

## `stateKey` (type: `string`):

Internal: names the incremental baseline so several differently-scoped runs can track separately. Empty derives the key from the run's scope.

## `emitUnchanged` (type: `boolean`):

Incremental mode: also emit rows whose tracked fields did not change (changeType UNCHANGED) instead of suppressing them.

## `emitExpired` (type: `boolean`):

Incremental mode: emit EXPIRED rows for tracked products that disappeared from a complete scan of the scope.

## `expireAfterMissedScans` (type: `integer`):

Incremental mode with Emit expired records: a product is marked EXPIRED only after it was missing from this many consecutive complete scans. 1 expires it at the first complete scan that misses it. EXPIRED means the product dropped out of the scanned listing; without opening its page a listing drop and a delisting cannot be told apart, so use the price and availability fields for stock.

## `changeScope` (type: `string`):

Incremental mode: all compares every field. priceAndStock marks a row UPDATED only when its price, was price, discount or price label moved; availability and colours out of stock are tracked only on pasted product-link rows (search and category rows carry no stock data when they are compared). Title, images, badges, descriptions and other metadata never create an UPDATED row. Changing the scope changes the automatic state key, so the first run under a new scope is all NEW; set a State key to keep one baseline.

## `compactUpdates` (type: `boolean`):

Incremental mode: UPDATED and EXPIRED rows carry only identifiers, the current price and availability, priceChanges (old/new per moved field) and variantChanges. NEW rows stay full. Product-page and review fees are not charged on a compact row, because it no longer carries that data.

## `ignoreFieldsForChanges` (type: `array`):

Incremental mode: field names that never decide UPDATED or appear in changedFields. They stay in the output. Use a plain name (rating) or a dotted path (offers.price) for a key inside every element of a list.

## `mcpConnectors` (type: `array`):

Internal: connector ids that receive the run's records as they push.

## `notionParentPageUrl` (type: `string`):

Internal: parent page for the Notion connector.

## `maxNotifyListings` (type: `integer`):

Internal: cap on records sent to connectors per run.

## `maxItems` (type: `integer`):

Maximum records this run may push. Values above 100000 are treated as 100000. 0 means the 100000 cap.

## Actor input object example

```json
{
  "mode": "urls",
  "searchTerms": [
    "womens jeans"
  ],
  "urls": [
    {
      "url": "https://www.kohls.com/search.jsp?search=womens+jeans&PPP=120"
    },
    {
      "url": "https://www.kohls.com/catalog/mens-clothing.jsp"
    },
    {
      "url": "https://www.kohls.com/product/prd-7327369/nike-v5-rnr-womens-running-shoes.jsp"
    }
  ],
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyCountry": "US"
  },
  "fetchDetails": true,
  "fetchReviews": true,
  "maxReviews": 20,
  "incrementalMode": false,
  "emitUnchanged": false,
  "emitExpired": false,
  "expireAfterMissedScans": 1,
  "changeScope": "all",
  "compactUpdates": false,
  "maxNotifyListings": 50,
  "maxItems": 20
}
```

# Actor output Schema

## `overview` (type: `string`):

All records this run produced.

## `detail` (type: `string`):

Records enriched from their product page (description, variants, gallery).

## `reviews` (type: `string`):

One row per review collected (unwrapped from the parent products).

## `changes` (type: `string`):

Incremental mode: NEW, UPDATED and EXPIRED rows from this run.

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "mode": "urls",
    "searchTerms": [
        "womens jeans"
    ],
    "urls": [
        {
            "url": "https://www.kohls.com/search.jsp?search=womens+jeans&PPP=120"
        },
        {
            "url": "https://www.kohls.com/catalog/mens-clothing.jsp"
        },
        {
            "url": "https://www.kohls.com/product/prd-7327369/nike-v5-rnr-womens-running-shoes.jsp"
        }
    ],
    "proxyConfiguration": {
        "useApifyProxy": true,
        "apifyProxyCountry": "US"
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("abotapi/kohls-com-scraper").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = {
    "mode": "urls",
    "searchTerms": ["womens jeans"],
    "urls": [
        { "url": "https://www.kohls.com/search.jsp?search=womens+jeans&PPP=120" },
        { "url": "https://www.kohls.com/catalog/mens-clothing.jsp" },
        { "url": "https://www.kohls.com/product/prd-7327369/nike-v5-rnr-womens-running-shoes.jsp" },
    ],
    "proxyConfiguration": {
        "useApifyProxy": True,
        "apifyProxyCountry": "US",
    },
}

# Run the Actor and wait for it to finish
run = client.actor("abotapi/kohls-com-scraper").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print(f"💾 Check your data here: https://console.apify.com/storage/datasets/{run.default_dataset_id}")
for item in client.dataset(run.default_dataset_id).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "mode": "urls",
  "searchTerms": [
    "womens jeans"
  ],
  "urls": [
    {
      "url": "https://www.kohls.com/search.jsp?search=womens+jeans&PPP=120"
    },
    {
      "url": "https://www.kohls.com/catalog/mens-clothing.jsp"
    },
    {
      "url": "https://www.kohls.com/product/prd-7327369/nike-v5-rnr-womens-running-shoes.jsp"
    }
  ],
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyCountry": "US"
  }
}' |
apify call abotapi/kohls-com-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,abotapi/kohls-com-scraper"
        }
    }
}
```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/lyDw8qaAXxHdhp2Fc/builds/oDxLkUz33wfaPRyGe/openapi.json
