# KLEKT Sneaker & Apparel Resale Scraper (`abotapi/klekt-com-scraper`) Actor

Scrape sneaker and streetwear listings from KLEKT (klekt.com). Browse the catalog with filters and extract product details, prices, and availability for resale market research and price monitoring.

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

## Pricing

from $1.00 / 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

## KLEKT Scraper: Sneaker & Streetwear Resale Prices

KLEKT Scraper turns KLEKT (klekt.com), a sneaker and apparel resale marketplace, into structured product data. Get catalogue prices alongside the product page's own last sold price, lowest listing price and highest bid, plus brand, SKU, size, box condition and availability. Browse the site's own catalogue with real filters (brand, category, price, On sale), paste exact product URLs, or stream the entire catalogue from the official sitemaps, then export to JSON, CSV or Excel, or pull the results straight into your app through the API.

### Why This Scraper?

- **Three ways in.** Browse KLEKT's own catalogue with filters, paste exact product URLs, or stream the whole catalogue from the official sitemaps. No need to build URLs by hand.
- **Full market picture per product.** Every record carries the catalogue asking price plus the product page's own last sold price, lowest listing price and highest bid, each under its own field, so the numbers never get mixed up.
- **Listing-level catalogue cards.** Every catalogue row is an individual marketplace listing carrying its own price, size, box condition, condition badge, seller and likes count - and every listing URL opens its own product page for enrichment.
- **Only the filters KLEKT actually applies.** A filter the site would silently ignore, or apply to nothing, is dropped and reported in the run log instead of being sent for show.
- **Price-accurate rows.** In Browse mode the price on every record is the same number KLEKT's own price filter matched on, whether or not product-page enrichment is switched on.
- **Works with zero input.** Leave everything blank and it browses the full catalogue straight away.
- **Fails loudly, not quietly.** A run that could not reach the source at all stops with a clear error instead of returning an empty dataset that looks like "nothing matched".

### Use Cases

- **Resale price tracking:** compare catalogue asking prices against last sold price and highest bid to spot under or over priced listings.
- **New-release monitoring:** sort the catalogue by the freshest listings and watch prices move from day one.
- **Marketplace and arbitrage research:** compare KLEKT prices, by size and box condition, against other resale platforms.
- **Product catalogue enrichment:** pull structured brand, SKU, colorway and size data into a database or price-comparison tool.
- **Sale and pre-owned research:** track the On sale section and per-listing box condition and condition badges.

### Data You Get

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

| Field | Example |
| --- | --- |
| `id` | `"a1b2c3d4-1111-2222-3333-000000000000"` |
| `type` | `"catalog_listing"` (every catalogue row is an individual listing) |
| `name` | `"Sample Runner 1 'Example' (2024)"` |
| `brand` | `"Sample Brand"` (from the product page; catalogue rows carry no brand) |
| `sku` / `slug` | `"XX1234-000"` / `"sample-runner-example-2024"` |
| `url` | KLEKT link to the product or, on Used/On sale, the individual listing |
| `description` | product description text (detail enrichment only) |
| `colorway` | `"Sample/White"` (product page and listing page; detail enrichment only) |
| `size` | `"US 9"` (every catalogue card carries its own size) |
| `boxCondition` | `"good"` (on the catalogue card, from the listing) |
| `condition` | `"Deadstock / New"` (the listing's own condition badge) |
| `likesCount` / `postedAgo` | `424` / `"17m ago"` (catalogue card only) |
| `priceAmount` / `priceCurrency` | `134.0` / `"EUR"`: the catalogue asking price (in the two URL modes, the lowest live listing, else the last sale) |
| `lastSoldPrice` | `162.4`: the most recent completed sale (from the catalogue row when it carries one, refreshed from the product page) |
| `lowestListingPrice` / `lowestListingSize` / `highestBid` | `136.5` / `"US 9"` / `101.0` (detail enrichment only; `highestBid` only when the page shows a bid) |
| `averageDeadstockPrice` / `totalSales` / `priceFluctuation` | `244.37` / `520` / `4.0`: the model's average deadstock sale price, total completed sales and recent price fluctuation (new-product pages with price history; detail enrichment only) |
| `sizeAsks` | per-size market data: the lowest live ask for each size that has a listing, as `{sizes: {us, eu, uk, cm, ...}, lowestListingPrice}` (new-product pages; detail enrichment only) |
| `questions` | up to 10 buyer questions with KLEKT's answers: `text`, `askedAt`, `answer`, `answeredAt` (new-product pages; the asker's name is not included) |
| `releaseYear` / `releaseDate` / `imageUrl` | `"2024"` / `"2023-08-07"` / catalogue thumbnail (`releaseDate` is the exact release date from the product page, detail enrichment only; `releaseYear` is the year in the product link) |
| `images` | product-page photo list (detail enrichment only) |
| `seller` / `model` | `"KLEKT"` (KLEKT authenticates every sale itself) / product-page model name (detail enrichment only) |
| `reviews` | up to **Max reviews per product** community reviews (default 20, max 1000): `reviewer`, `rating`, `appearanceRating`, `qualityRating`, `sizeRating`, `sizeFeeling`, `reviewSize`, `text`, `date` (always null today: KLEKT shows no review date), `helpfulVotes`, `replyCount`, `reviewId`, `reviewImages` (product-page rows only); always an array, empty when none were requested or found; `reviewsSummary` holds `averageRating`, per-aspect averages and `reviewCount` (product-page rows only) |
| `detailScraped` | `true` once the product page was fetched, else `false` |
| `changeType` / `changedFields` / `firstSeenAt` / `lastSeenAt` | incremental mode only: `NEW`, `UPDATED`, `UNCHANGED`, `REAPPEARED` or `EXPIRED`, the fields that moved on an `UPDATED` row, and when the product was first and last seen; with compact updates, `priceChanges` holds the old and new value of each moved price field |

#### Output views

The dataset has four views in the Console: **Products** (catalogue price, size, condition), **Market detail** (last sold, lowest listing, highest bid, average deadstock price, total sales, price fluctuation, release date, colorway), **Reviews** (one row per review: reviewer, review stars, size feeling, size bought, text) and **Changes since the last run** (`changeType`, `changedFields`, `priceChanges`, first and last seen; incremental mode).

### How to Use

1. Pick a **Scrape mode**: Browse the catalogue (default), Product URLs, or Full catalogue via sitemaps.
2. For Browse, choose a catalogue section and any filters (brand, product category, price range). For Product URLs, paste KLEKT URLs, one per line (see the URL table below).
3. Set **Max items** to control run size and cost, then click **Start**.
4. Download the dataset as JSON, CSV or Excel, or read it through the API.

**Browse Trending, the default:**

```json
{
  "mode": "browse",
  "strategy": "trending",
  "maxItems": 20
}
```

**Browse the On sale section for one brand and price band:**

```json
{
  "mode": "browse",
  "strategy": "sale",
  "brand": "Nike",
  "priceFrom": 80,
  "priceTo": 200,
  "maxItems": 30
}
```

**Product URLs mode: supported URL shapes**

| URL shape | Example | What happens |
|---|---|---|
| New product | `https://klekt.com/product/new/yeezy-boost-350-v2-bone` | one product row |
| New product without `/new/` | `https://klekt.com/product/yeezy-boost-350-v2-bone` | KLEKT redirects it; same row as above |
| Pre-owned listing | `https://klekt.com/listing/stussy-x-dries-van-noten-hoodie-very-good-1bzz1x7p` | one listing row |
| Locale-prefixed (`/nl/`, `/it/`, ...) | `https://klekt.com/nl/product/new/yeezy-boost-350-v2-bone` | the prefix is removed, so it is the same product and the same `id` as the unprefixed URL |
| Browse grid | `https://klekt.com/browse` (also `/catalog`) | walks the catalogue like Browse mode |
| Browse page | `https://klekt.com/browse?page=2` | starts the walk at page 2; a partial scope, so incremental mode never marks products EXPIRED from it |
| Brand | `https://klekt.com/browse?brand=Nike` | walks that brand's catalogue |
| On sale | `https://klekt.com/catalog?strategy=sale` | walks the On sale section |
| Sorted / text search | `https://klekt.com/browse?sort=newest`, `https://klekt.com/browse?q=yeezy` | KLEKT applies sort and search in the browser only (measured: the server returns the same grid), so the whole grid is walked and a warning in the run log says so |
| Sitemap index | `https://klekt.com/sitemap.xml` | streams products from the official sitemaps |

The legacy `/product/used/<id>` form redirects to the pre-owned listing page and is scraped like a listing URL; unsupported URLs get a warning in the run log and are skipped. Left untouched, Product URLs mode now scrapes one example of each shape above (this field used to default to empty), and **Max items** is shared between the grids and the sitemap so none of them starves the others.

**Scrape specific product pages:**

```json
{
  "mode": "product-urls",
  "productUrls": [
    "https://klekt.com/product/new/sample-runner-example-2024"
  ]
}
```

**Stream the full catalogue from the official sitemaps:**

```json
{
  "mode": "sitemap",
  "maxItems": 500
}
```

#### Run it from your code

Python:

```python
from apify_client import ApifyClient

client = ApifyClient("<YOUR_APIFY_TOKEN>")
run = client.actor("abotapi/klekt-com-scraper").call(run_input={"mode": "browse", "strategy": "trending", "maxItems": 20})
for product in client.dataset(run["defaultDatasetId"]).iterate_items():
    print(product["name"], product.get("priceAmount"), product.get("priceCurrency"))
```

JavaScript:

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

const client = new ApifyClient({ token: '<YOUR_APIFY_TOKEN>' });
const run = await client.actor('abotapi/klekt-com-scraper').call({ mode: 'browse', strategy: 'trending', maxItems: 20 });
const { items } = await client.dataset(run.defaultDatasetId).listItems();
```

Or connect it to Make, Zapier, n8n, Google Sheets or webhooks from the **Integrations** tab.

#### About the browse filters

**Working filters:** brand, product category, minimum and maximum price, and the On sale section. **No-op filters (dropped with a run-log note):** brand line, size category, size system, size, box condition, listing type and availability, plus the Trending, Used, Drops, Upcoming and Gift cards sections (they walk the full catalogue). Pasted `?q=` and `?sort=` URL parameters are likewise not applied by KLEKT's server.

The filters under **Browse filters** apply to Browse mode only; Product URLs mode and Full catalogue mode fetch specific product pages, so nothing in that section changes what they return. Since KLEKT rebuilt its storefront (October 2026), the catalogue narrows server-side on **Brand** (written the way KLEKT displays it, e.g. Nike), **Product category**, **Minimum/Maximum price** (whole euros) and the **On sale** section. The size, box-condition, listing-type, availability and brand-line options no longer have a server-side filter on the rebuilt catalogue, so setting one drops it with a note in the run log rather than sending it for show - the listing cards themselves still carry each listing's size, box condition and condition badge. Naming any section other than On sale (Trending, Used, Drops, Upcoming, Gift cards) walks the full catalogue and says so in the run log; an unrecognised section value is replaced with the default. Every drop is reported in the run log.

#### Tip: Max items, Max pages, and a fast-moving catalogue

**Max items** stops the run once that many products have been returned (0 = unlimited). **Max pages** is a safety bound on how many catalogue pages Browse walks (about 20 listing cards per page); the walk also stops on its own at the natural end of a section, or when a page repeats products already collected earlier in the same run.

That repeat check compares each page against every product seen so far in the run, not just the page before it. KLEKT's section order is stable while nothing changes, but if the section is re-ranked while a long Browse walk is in progress, or a burst of new listings lands ahead of the walk, a later page can come back holding only products the run already collected. The walk treats that as the end and stops, even though further pages may still hold new products. The deeper into a section the walk already is, the more likely this becomes, so it mainly affects large, unfiltered runs (a large **Max items** with no narrowing filters) and tends to cut off the tail of the section. The run log then says the page "repeated the previous page's products". If a run seems to stop short of **Max items**, narrow with filters to shorten the walk, or run it again.

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

Optionally pipe scraped KLEKT products into the apps you already use via Model Context Protocol (MCP) connectors (Notion, Linear, Airtable, Apify). This is an extra delivery step **after** the scrape: the Apify dataset is never changed.

**What gets written to the connector:** a condensed, human-readable summary of each product, not the full JSON. Each item becomes one entry with a title and its key fields flattened to plain text. The complete record always stays in the Apify dataset.

1. Authorize a connector once under **Apify → Settings → Integrations** (Notion, Linear, Airtable, or Apify).
2. Select it in the **"Pipe results into your apps"** input field. (If the picker is empty, you have not authorized a connector yet.)
3. For **Notion**, also set **Notion parent page** to the page where products should be created.

Use **Max products to export per connector** to cap how many are sent; that number is what is actually delivered. The connection is mediated by Apify's MCP proxy, so this actor never sees your third-party credentials, and a connector error is logged as a warning without failing or stalling the run. Leave the field empty to skip.

### Input Parameters

| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `mode` | string | `browse` | `browse`, `product-urls`, or `sitemap`. |
| `productUrls` | array | one example of each URL shape | KLEKT URLs, one per line (Product URLs mode); see the URL shapes table. |
| `strategy` | string | `trending` | Catalogue section to walk (Browse mode). Only `sale` narrows as its own section since the storefront rebuild; the other names walk the full catalogue and say so in the run log. |
| `brand` | string | `""` | Filter to one brand, written the way KLEKT displays it (Browse mode). |
| `brandLine` | string | `""` | Optional narrower model family inside a brand (Browse mode). |
| `productCategory` | string | `""` | `Sneakers`, `Streetwear`, or `Accessories`. Leave empty for any. |
| `sizeCategory` | string | `""` | `Men`, `Women`, `Youth`, or `Default`. Dropped with a run-log note (the rebuilt catalogue has no server-side size filter). |
| `sizeMetric` | string | `""` | Size system for `size` (e.g. `US`, `UK`, `EU`, `CM`). Dropped with a run-log note (no server-side size filter). |
| `size` | string | `""` | One size in the chosen `sizeMetric`, exactly as KLEKT writes it. Dropped with a run-log note (no server-side size filter). |
| `boxCondition` | string | `""` | `good`, `missing_lid`, `damaged`, `no_box`, or `replacement_box`. Dropped with a run-log note (no server-side box-condition filter). |
| `listingType` | string | `""` | `used` or `new_with_defect`. Dropped with a run-log note (no server-side listing-type filter). |
| `availability` | string | `""` | `0` (Available), `1` (Express dispatch), or `2` (Used available). Dropped with a run-log note (no server-side availability filter). |
| `priceFrom` | integer | (none) | Minimum price in whole euros. |
| `priceTo` | integer | (none) | Maximum price in whole euros. |
| `fetchDetails` | boolean | `true` | Fetch each product's own page for the full record. Browse mode only; always on in Product URLs and Full catalogue mode. |
| `maxItems` | integer | `50` | Stop after this many products (`0` = unlimited). |
| `maxPages` | integer | `0` | Safety bound on catalogue pages walked in Browse mode. `0` means no input limit (the walk ends at the end of the section or at **Max items**); a hard ceiling of 5000 pages is enforced in code. |
| `maxReviewsPerProduct` | integer | `20` | Community reviews attached to each product row, 20 per page; `0` = none, capped at 1000. |
| `proxy` | object | Apify Proxy | Connection settings. |
| `resumeFromRunId` | string | `""` | Run id or dataset id of an earlier run; products already in it are skipped (not returned, not billed). |
| `incrementalMode` | boolean | `false` | Return only NEW / UPDATED / REAPPEARED products compared with the last run of the same scope. |
| `stateKey` | string | `""` | Name a monitoring campaign (incremental mode). |
| `emitUnchanged` | boolean | `false` | Also return UNCHANGED products (incremental mode). |
| `emitExpired` | boolean | `false` | Also return EXPIRED products after a complete scan (incremental mode). |
| `changeScope` | string | `all` | `all` or `priceAndStock` (incremental mode). |
| `compactUpdates` | boolean | `false` | Short UPDATED / EXPIRED rows with `priceChanges` (incremental mode). |
| `expireAfterMissedScans` | integer | `1` | Complete scans a product must be missing before it is EXPIRED. |
| `ignoreFieldsForChanges` | array | `[]` | Extra fields (or dotted paths) that never count as a change (incremental mode). |
| `mcpConnectors` | array | `[]` | Optional: send a summary of each product to apps you authorized under Integrations. |
| `notionParentPageUrl` | string | `""` | Notion connector only: page under which products are created. |
| `maxNotifyListings` | integer | `50` | Cap on products written to each connector per run. |

### Output Example

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

```json
{
  "id": "a1b2c3d4-1111-2222-3333-000000000000",
  "type": "catalog_product_variant",
  "name": "Sample Runner 1 'Example' (2024)",
  "brand": "Sample Brand",
  "sku": "XX1234-000",
  "slug": "sample-runner-example-2024",
  "url": "https://klekt.com/product/new/sample-runner-example-2024",
  "description": "Sample product description text.",
  "colorway": "Sample/White",
  "availability": "InStock",
  "priceAmount": 134.0,
  "priceCurrency": "EUR",
  "lastSoldPrice": 162.4,
  "lowestListingPrice": 136.5,
  "lowestListingSize": "US 9",
  "highestBid": 101.0,
  "averageDeadstockPrice": 244.37,
  "totalSales": 520,
  "priceFluctuation": 4.0,
  "releaseYear": "2024",
  "imageUrl": "https://images.klekt.com/sample/thumb.jpg",
  "images": [
    "https://images.klekt.com/sample/1.jpg",
    "https://images.klekt.com/sample/2.jpg"
  ],
  "seller": "KLEKT",
  "detailScraped": true
}
```

### Plan Requirement

KLEKT showed no block or challenge on a plain direct connection. The actor reads pages through your selected Apify Proxy and, if that connection is refused, continues on a backup connection. We have not measured whether Apify's datacenter proxy group alone is enough for large runs, so treat it as unproven.

- **Free Apify plans do not include proxies.** The run log then shows: "The selected connection is unavailable to this run. Apify free plans do not include residential proxies. Continuing on a backup connection; some pages may still be unavailable." Pages served that way are noted as "Served over the backup connection (the default connection was unavailable)."
- **If nothing could be read** the run fails with a clear message instead of an empty dataset: "The proxy connection was refused for this account, so no page could be read..." (a paid plan or your own proxy URLs are needed), "No results: residential proxies are unavailable on this run ... and the backup connection could not load the requested KLEKT pages", or "Could not load any results page (the requests failed), so nothing was collected. This is a connection problem, not an empty search."
- A paid plan with a residential group gives the most headroom for large or frequent runs. Whichever groups you select under Proxy & connection are the ones used; turning Apify Proxy off connects directly.

### FAQ

#### How much does it cost?

Pay-per-event, with these events: **Actor Start** (`apify-actor-start`, once per run), **Product record** (`apify-default-dataset-item`, once per product row returned) and **Detail enrichment** (`detail-enrichment`, once per product-page row returned, only when the product page was fetched: always in Product URLs and Full catalogue modes, and in Browse mode when **Fetch product details** is on, which is the default). Switching **Fetch product details** off in Browse mode removes the **Detail enrichment** charge. Reviews are part of that product-page record: attaching up to **Max reviews per product** reviews adds no further charge. Rows that are not returned (duplicates, unchanged, skipped by Resume, failed pages) are never charged. The **Pricing** tab shows the current rates. Use **Max items** to cap the cost of any run.

#### Is it legal to scrape KLEKT?

This actor collects only publicly available marketplace data. You are responsible for how you use it: follow KLEKT's terms and the laws that apply to you, and get legal advice if you plan commercial redistribution. Product prices and availability are generally facts, but photos and descriptions may be subject to third-party rights.

#### Can I run this on a schedule to track price changes?

Yes. Schedule it from the **Schedules** tab and switch on **Incremental mode**. The first run returns every product as `NEW`; later runs return only what changed since the last run: `NEW`, `UPDATED` (with `changedFields` naming exactly what moved) and `REAPPEARED`.

- **What is compared:** asking price and currency, the market numbers (`lowestListingPrice`, `highestBid`, `lastSoldPrice`), `availability`, name, brand, size, box condition, seller, images and description. `postedAgo`, `likesCount`, `detailScraped`, `reviews`, `reviewsSummary`, `questions`, `sizeAsks`, `releaseDate`, `colorway`, `totalSales`, `priceFluctuation` and `averageDeadstockPrice` never count as a change (they tick by themselves) but are still returned on every row. Add more with **Ignore fields when detecting changes**; a dotted name such as `images.url` drops that key inside a nested value.
- **Only price and stock:** set **What counts as a change** to `Price and availability only` so that a changed description, image or seller never returns a row. Add **Compact update rows** for short `UPDATED` rows with `priceChanges` (old and new value of each moved price field).
- **State:** kept per scope (mode, section, brand, category, price range, product URLs, Fetch product details and the ignore settings). Set a **State key** to keep one baseline across changes to those settings; without one, changing the ignore fields or change scope starts a fresh baseline.
- **Expired:** switch on **Emit expired products** to also get `EXPIRED` rows, produced only after a complete scan (never when Max items or Max pages stopped the walk, when a request failed, or when Resume was used). `EXPIRED` means the listing dropped out of the scope, not that it sold; read `availability` for stock. **Expire after N missed full scans** (default 1) delays the tombstone.
- **Unchanged rows:** off by default; **Emit unchanged products** returns them too.

**Reviews and billing:** reviews ride the product row and add no separate charge: the **Detail enrichment** event stays one charge per product-page record returned, whatever the number of reviews attached. Set **Max reviews per product** to 0 to skip them.

**Billing in incremental mode:** you pay for the rows that are returned. An `UNCHANGED` row that is not returned is not billed, and neither is its product-page charge. The **Detail enrichment** event is charged once per product-page record that is actually returned: a product skipped as a duplicate within the run, skipped by **Resume from a previous run**, or not returned because it is unchanged is not charged. A compact `UPDATED` row carries no product-page data, so it does not trigger that charge. **Resume from a previous run** (a run id or dataset id) is a separate feature: it continues one interrupted run and returns only products not in that dataset.

#### Why did my run return fewer products than Max items?

A few reasons: the catalogue section, brand or size combination genuinely has fewer matching products than requested; a filter you set was dropped because KLEKT would have ignored it (check the run log for a note); or, rarely, the section was re-ranked or received a burst of new listings mid-run, so a later catalogue page came back holding only products already collected earlier in the same run, which the walk treats as reaching the end (the log says the page "repeated the previous page's products"). That last case mostly affects large, unfiltered runs deep into a section; re-running, or narrowing with filters, works around it.

#### Why does `brand` come back empty on some products?

KLEKT's catalogue rows carry no brand field at all. With **Fetch product details** on (the default in Browse, and always on in the other two modes), it is filled from the product page. With it off in Browse mode, catalogue-only rows have no brand. Pre-owned listing pages also do not publish a brand, so it can stay empty on Used and On sale rows even with detail fetching on.

#### Why did my run fail instead of returning an empty dataset?

If every request to KLEKT is refused, the run stops with a clear error so "no products matched" is never confused with "the source could not be reached". Run it again in a few minutes, or try a different proxy group.

#### Can I use it with AI agents or MCP?

Yes. Call it from any Apify integration or MCP client, and use the connector field to push results into Notion, Linear or Airtable.

### 🔗 Want more fashion data?

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

<table>
<tr><td>👗 <a href="https://apify.com/abotapi/goat-sneaker-scraper"><b>GOAT Scraper</b></a><br>Scrape GOAT (goat.com), the sneaker and streetwear resale marketplace. Search or paste...</td><td>👗 <a href="https://apify.com/abotapi/kream-scraper"><b>KREAM Scraper</b></a><br>Scrape KREAM (kream.co.kr) sneaker and fashion resale data by keyword search, filters...</td></tr>
<tr><td>👗 <a href="https://apify.com/abotapi/snkrdunk-com-scraper"><b>SNKRDUNK Scraper</b></a><br>Scrape SNKRDUNK (snkrdunk.com), Japan's sneaker and streetwear resale marketplace: JP and...</td><td>👗 <a href="https://apify.com/abotapi/stockx-market-data-scraper"><b>StockX Scraper</b></a><br>Scrape StockX market data by keyword, category or URL. Every row carries lowest ask...</td></tr>
<tr><td>🏷️ <a href="https://apify.com/abotapi/poshmark-scraper"><b>Poshmark Scraper</b></a><br>Scrape Poshmark fashion-resale listings: keyword search, brand and category browse, sold...</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>
</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/klekt-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`):

Browse walks the KLEKT catalogue page by page using the strategy and filters below — this is the default and needs no other input. Product URLs scrapes the exact listing pages you paste. Full catalogue streams every product from the official sitemaps (about 15,000 products) and ignores the browse filters.

## `productUrls` (type: `array`):

KLEKT URLs to scrape (one per line), routed by shape: product pages https://klekt.com/product/new/<slug> (or /product/<slug>, with an optional /nl/ or /it/ prefix), pre-owned listings https://klekt.com/listing/<slug>-<id>, catalogue grids https://klekt.com/browse or /catalog (?page=N starts the walk at page N, ?brand=Nike, ?search=jordan and ?strategy=sale narrow server-side; /catalog/<brand>/<line> and the legacy /product/used/<id> also work; ?q and ?sort are not honoured by KLEKT's server and are reported in the run log) and the sitemap https://klekt.com/sitemap.xml. Left untouched, Product URLs mode scrapes one example of each shape, sharing Max items between them. Unsupported URLs are reported in the run log.

## `strategy` (type: `string`):

Which KLEKT catalogue section to walk. After KLEKT rebuilt its storefront (October 2026) only 'On sale' still narrows the catalogue as its own section; the other names are kept for saved tasks but walk the full catalogue (the run log says so when one is picked). 'Trending' (the default) walks the full catalogue.

## `brand` (type: `string`):

Filter to one brand, written the way KLEKT displays it (capitals and spaces, not a slug). Observed brands: Nike, Adidas, Air Jordan, New Balance, Yeezy, Asics, Nike SB, Crocs, On, Vans, Saucony, Supreme, Reebok, Converse, Puma, Travis Scott, Luxury, The North Face, Under Armour, Off White, Palace, Rick Owens, Stussy, Timberland, Mizuno, Karhu, Clarks, Fear Of God, Axel Arigato, Kaws, Ugg, Brooks, Brands Other. KLEKT's brand list differs per section, so this is free text; a brand that section does not carry simply returns no products.

## `brandLine` (type: `string`):

Optional narrower model family inside a brand. KLEKT's rebuilt catalogue no longer offers a server-side brand-line filter, so this is dropped with a note in the run log when set.

## `productCategory` (type: `string`):

Limit to one KLEKT product category. These three are the complete set — their counts add up to the unfiltered total. Leave on Any for everything.

## `sizeCategory` (type: `string`):

Limit to one KLEKT size category. KLEKT's rebuilt catalogue no longer offers a server-side size-category filter, so this is dropped with a note in the run log when set.

## `sizeMetric` (type: `string`):

The size system the Size field is written in. KLEKT's rebuilt catalogue no longer offers a server-side size filter, so this is dropped with a note in the run log when set.

## `size` (type: `string`):

One size, written the way KLEKT writes it (e.g. 10, 10.5, 44, M4/W6). KLEKT's rebuilt catalogue no longer offers a server-side size filter, so this is dropped with a note in the run log when set.

## `boxCondition` (type: `string`):

Condition of the shoe box. KLEKT's rebuilt catalogue no longer offers a server-side box-condition filter, so this is dropped with a note in the run log when set. The listing cards still carry each listing's own box condition in the dataset output.

## `listingType` (type: `string`):

Split into genuinely used pairs and new-with-defect pairs. KLEKT's rebuilt catalogue no longer offers a server-side listing-type filter, so this is dropped with a note in the run log when set.

## `availability` (type: `string`):

Limit to products KLEKT flags as available. KLEKT's rebuilt catalogue no longer offers a server-side availability filter, so this is dropped with a note in the run log when set.

## `priceFrom` (type: `integer`):

Only return products priced at or above this many euros. Whole euros, not cents. Leave empty for no lower bound.

## `priceTo` (type: `integer`):

Only return products priced at or below this many euros. Whole euros, not cents. Leave empty for no upper bound.

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

Fetch each product's own page for the full record (description, images, seller, last sold price, lowest listing price). Applies to Browse mode ONLY: switching it off there keeps the lighter catalogue row instead, with no per-product enrichment charge. Product URLs mode and Full catalogue mode have no catalogue row behind them — the product page is their only source of data — so detail fetching always runs in those two modes and the run log says so.

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

Maximum number of products to scrape (up to 1,000,000). 0 = no cap (stop only at the end of the catalogue). Counts records actually returned, not pages read.

## `maxPages` (type: `integer`):

Safety bound on how many catalogue pages Browse mode walks (20 products per page). 0 (the default) means no input limit: the walk stops at the end of the section or at Max items, and code enforces a hard ceiling of 5000 pages.

## `maxReviewsPerProduct` (type: `integer`):

Community reviews to attach to each product row (reviewer, ratings, size, text, helpful votes), 20 per page. 0 = none. Capped at 1000 per product to bound run time. Applies wherever the product page is fetched.

## `proxy` (type: `object`):

Whichever proxy groups you select here are the ones used — leave the groups empty to use your account's default pool. Turning Apify Proxy off connects directly, with no proxy traffic billed.

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

Optional. ID of a previous run of this Actor (or a dataset ID). Products already in that dataset are skipped, so this run returns only NEW products (a delta) and is not billed for the skipped ones. Combine both runs' datasets for the full set. Max items then counts only the new products. A resumed run never marks anything EXPIRED. For recurring monitoring of the same scope, use Incremental mode below instead.

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

Turn this on for daily or recurring monitoring. The first run returns every product as NEW. Later runs compare each product against the last run and return only NEW, UPDATED and REAPPEARED products; an UPDATED row's changedFields names exactly what moved. Compared: asking price and currency, the market numbers (lowest listing, highest bid, last sold), availability, name, brand, size, box condition, seller, images and description. Never compared (they tick by themselves): postedAgo, likesCount and detailScraped, which are still returned on every row. Switch on Emit unchanged or Emit expired only if you also want those rows (and their billing). State is kept per scope (mode, section, brand, category, price range, product URLs, Fetch product details and the ignore settings); use State key to name or deliberately share a campaign.

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

Optional. Name this monitoring campaign to keep its state stable, or to deliberately share state across differently configured runs. Leave empty to derive a key automatically from the scope; changing the ignore fields or change scope on an automatic key starts a fresh baseline (first run returns everything as NEW), so set a State key to keep one baseline across such changes.

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

Off by default. Turn on to also return products that have not changed since the last run, marked UNCHANGED. This returns (and bills) rows you already have, so leave it off unless you want the full snapshot every run.

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

Off by default. Turn on to also return products that were tracked before but are no longer found, marked EXPIRED. Only produced after a COMPLETE scan: never when Max items or Max pages stopped the walk, when any request failed, or when Resume was used. EXPIRED means the product dropped out of this scope's listing, not that it is sold; read availability for stock. Returns (and bills) extra rows.

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

All fields (default): any change to the product record, except the always-ignored postedAgo, likesCount and detailScraped, returns an UPDATED row. Price and availability only: only a change to the asking price, currency, lowest listing, highest bid, last sold price or availability returns an UPDATED row; name, images, description, seller and other details never do.

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

Off by default. When on, UPDATED and EXPIRED rows are short: id, url, name, current price and availability plus priceChanges (old and new value of each moved price field). NEW rows always carry the complete record. Compact rows carry no product-page data, so they do not trigger the detail-enrichment charge.

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

Only used with Emit expired products. A product is marked EXPIRED only after it has been missing from this many complete scans in a row (default 1). Raise it (e.g. 3) so a listing that briefly drops out is not reported as gone. A product that comes back resets the count.

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

Optional. Field names to exclude from UPDATED change detection, in addition to the built-in handling (postedAgo, likesCount and detailScraped never count). Use it when another field turns out to be noisy in your setup, e.g. imageUrl or images if the image host rotates. A dotted name such as images.url drops that key inside every element of a nested value. Ignored fields are still returned on every row.

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

Optionally send scraped KLEKT products into the apps you already use, via Model Context Protocol (MCP) connectors. Authorize one under Apify → Settings → API & Integrations, then select it here. Notion gets a rich page-per-product export; other connectors get a best-effort write/digest. Leave empty to skip; never changes the dataset output. Supported: Notion (https://mcp.notion.com/mcp), Linear (https://mcp.linear.app/sse), Airtable (https://mcp.airtable.com/mcp), Apify (https://mcp.apify.com).

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

URL (or id) of the Notion page under which one page per product is created. Required to enable the Notion export; ignored by other connectors.

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

Cap on products written to each connector per run. Does not affect the dataset.

## Actor input object example

```json
{
  "mode": "browse",
  "productUrls": [
    "https://klekt.com/product/new/yeezy-boost-350-v2-onyx",
    "https://klekt.com/listing/nike-air-max-1-black-cat-2025-us-8-brand-new-drfymgdk",
    "https://klekt.com/product/used/gytq6o25",
    "https://klekt.com/product/air-jordan-4-retro-black-cat-2025",
    "https://klekt.com/nl/product/new/yeezy-boost-350-v2-bone",
    "https://klekt.com/catalog",
    "https://klekt.com/catalog?page=2",
    "https://klekt.com/catalog?brand=Nike",
    "https://klekt.com/catalog?search=jordan",
    "https://klekt.com/catalog/hoka",
    "https://klekt.com/catalog/nike/air-jordan",
    "https://klekt.com/catalog?brand=Adidas&sort=price",
    "https://klekt.com/browse",
    "https://klekt.com/browse?page=2",
    "https://klekt.com/nl/catalog/adidas",
    "https://www.klekt.com/catalog/new-balance",
    "https://klekt.com/sitemap.xml"
  ],
  "strategy": "trending",
  "brand": "",
  "brandLine": "",
  "productCategory": "",
  "sizeCategory": "",
  "sizeMetric": "",
  "size": "",
  "boxCondition": "",
  "listingType": "",
  "availability": "",
  "fetchDetails": true,
  "maxItems": 50,
  "maxPages": 0,
  "maxReviewsPerProduct": 20,
  "proxy": {
    "useApifyProxy": true
  },
  "resumeFromRunId": "",
  "incrementalMode": false,
  "stateKey": "",
  "emitUnchanged": false,
  "emitExpired": false,
  "changeScope": "all",
  "compactUpdates": false,
  "expireAfterMissedScans": 1,
  "ignoreFieldsForChanges": [],
  "mcpConnectors": [],
  "notionParentPageUrl": "",
  "maxNotifyListings": 50
}
```

# Actor output Schema

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

No description

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

No description

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

No description

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

No description

# 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": "browse",
    "productUrls": [
        "https://klekt.com/product/new/yeezy-boost-350-v2-onyx",
        "https://klekt.com/listing/nike-air-max-1-black-cat-2025-us-8-brand-new-drfymgdk",
        "https://klekt.com/product/used/gytq6o25",
        "https://klekt.com/product/air-jordan-4-retro-black-cat-2025",
        "https://klekt.com/nl/product/new/yeezy-boost-350-v2-bone",
        "https://klekt.com/catalog",
        "https://klekt.com/catalog?page=2",
        "https://klekt.com/catalog?brand=Nike",
        "https://klekt.com/catalog?search=jordan",
        "https://klekt.com/catalog/hoka",
        "https://klekt.com/catalog/nike/air-jordan",
        "https://klekt.com/catalog?brand=Adidas&sort=price",
        "https://klekt.com/browse",
        "https://klekt.com/browse?page=2",
        "https://klekt.com/nl/catalog/adidas",
        "https://www.klekt.com/catalog/new-balance",
        "https://klekt.com/sitemap.xml"
    ],
    "strategy": "trending",
    "brand": "",
    "brandLine": "",
    "size": "",
    "fetchDetails": true,
    "maxItems": 50,
    "maxPages": 0,
    "maxReviewsPerProduct": 20,
    "proxy": {
        "useApifyProxy": true
    },
    "incrementalMode": false,
    "emitUnchanged": false,
    "emitExpired": false,
    "changeScope": "all",
    "compactUpdates": false,
    "expireAfterMissedScans": 1,
    "ignoreFieldsForChanges": []
};

// Run the Actor and wait for it to finish
const run = await client.actor("abotapi/klekt-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": "browse",
    "productUrls": [
        "https://klekt.com/product/new/yeezy-boost-350-v2-onyx",
        "https://klekt.com/listing/nike-air-max-1-black-cat-2025-us-8-brand-new-drfymgdk",
        "https://klekt.com/product/used/gytq6o25",
        "https://klekt.com/product/air-jordan-4-retro-black-cat-2025",
        "https://klekt.com/nl/product/new/yeezy-boost-350-v2-bone",
        "https://klekt.com/catalog",
        "https://klekt.com/catalog?page=2",
        "https://klekt.com/catalog?brand=Nike",
        "https://klekt.com/catalog?search=jordan",
        "https://klekt.com/catalog/hoka",
        "https://klekt.com/catalog/nike/air-jordan",
        "https://klekt.com/catalog?brand=Adidas&sort=price",
        "https://klekt.com/browse",
        "https://klekt.com/browse?page=2",
        "https://klekt.com/nl/catalog/adidas",
        "https://www.klekt.com/catalog/new-balance",
        "https://klekt.com/sitemap.xml",
    ],
    "strategy": "trending",
    "brand": "",
    "brandLine": "",
    "size": "",
    "fetchDetails": True,
    "maxItems": 50,
    "maxPages": 0,
    "maxReviewsPerProduct": 20,
    "proxy": { "useApifyProxy": True },
    "incrementalMode": False,
    "emitUnchanged": False,
    "emitExpired": False,
    "changeScope": "all",
    "compactUpdates": False,
    "expireAfterMissedScans": 1,
    "ignoreFieldsForChanges": [],
}

# Run the Actor and wait for it to finish
run = client.actor("abotapi/klekt-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": "browse",
  "productUrls": [
    "https://klekt.com/product/new/yeezy-boost-350-v2-onyx",
    "https://klekt.com/listing/nike-air-max-1-black-cat-2025-us-8-brand-new-drfymgdk",
    "https://klekt.com/product/used/gytq6o25",
    "https://klekt.com/product/air-jordan-4-retro-black-cat-2025",
    "https://klekt.com/nl/product/new/yeezy-boost-350-v2-bone",
    "https://klekt.com/catalog",
    "https://klekt.com/catalog?page=2",
    "https://klekt.com/catalog?brand=Nike",
    "https://klekt.com/catalog?search=jordan",
    "https://klekt.com/catalog/hoka",
    "https://klekt.com/catalog/nike/air-jordan",
    "https://klekt.com/catalog?brand=Adidas&sort=price",
    "https://klekt.com/browse",
    "https://klekt.com/browse?page=2",
    "https://klekt.com/nl/catalog/adidas",
    "https://www.klekt.com/catalog/new-balance",
    "https://klekt.com/sitemap.xml"
  ],
  "strategy": "trending",
  "brand": "",
  "brandLine": "",
  "size": "",
  "fetchDetails": true,
  "maxItems": 50,
  "maxPages": 0,
  "maxReviewsPerProduct": 20,
  "proxy": {
    "useApifyProxy": true
  },
  "incrementalMode": false,
  "emitUnchanged": false,
  "emitExpired": false,
  "changeScope": "all",
  "compactUpdates": false,
  "expireAfterMissedScans": 1,
  "ignoreFieldsForChanges": []
}' |
apify call abotapi/klekt-com-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,abotapi/klekt-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/8ag46bcQs3Vkrn2Us/builds/PbcEMnPvchcJej1f1/openapi.json
