Kohl's Scraper - Products, Prices, Ratings & Reviews
Pricing
from $1.60 / 1,000 product records
Kohl's Scraper - Products, Prices, Ratings & Reviews
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.
Pricing
from $1.60 / 1,000 product records
Rating
0.0
(0)
Developer
Abot API
Maintained by CommunityActor stats
0
Bookmarked
1
Total users
1
Monthly active users
7 days ago
Last modified
Categories
Security
Limited permissionsShare
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:
from apify_client import ApifyClientclient = 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:
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:
{"mode": "search","searchTerms": ["womens jeans", "air fryer"],"maxItems": 50}
Walk a category:
{"mode": "urls","urls": ["https://www.kohls.com/catalog/mens-clothing.jsp"],"maxItems": 100}
One product, reviews capped:
{"mode": "urls","urls": ["https://www.kohls.com/product/prd-7327369/nike-v5-rnr-womens-running-shoes.jsp"],"maxReviews": 5}
Scheduled price monitor:
{"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. UsechangeScope=priceAndStockto narrow the comparison to price fields; availability and colours out of stock are tracked only on product-link rows.EXPIREDmeans a product dropped out of the scanned listing; without opening its page a listing drop and a delisting look the same, so readavailabilityfor 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 carrypriceLabel"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.
{"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:
| 👗 Nuuly Scraper Scrape Nuuly clothing rental products by category, brand or URL. Extract product names... | 👗 Fashion Nova Scraper Scrape Fashion Nova products by keyword, collection or URL. Extract product names... |
| 👗 Showpo Scraper Scrape Showpo women's fashion by keyword, collection or URL. Extract product prices... | 👗 Rent the Runway Scraper Scrape Rent the Runway (renttherunway.com) designer clothing and accessories by category... |
| 👗 ZOZOTOWN Scrape ZOZOTOWN (zozo.jp), Japan's largest fashion marketplace: name, brand, JPY price... | 🥦 El Corte Inglés Scraper Scrape El Corte Inglés elcorteingles.es products across fashion, electronics, home... |
💬 Support
- 🐞 Found a bug or a missing field? Open a ticket on the Issues tab. 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.