# 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)
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 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?

Actors are web data automations that power AI and operations. They run on the Apify platform to scrape websites, process data, connect APIs, and automate workflows.
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.
Actors are written with capital "A".

## 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.
The best way to integrate Actors is as follows.

- **AI agents and MCP clients** — the [Apify MCP server](https://docs.apify.com/integrations/mcp.md) at `https://mcp.apify.com` (remote, streamable HTTP, OAuth on first use).
- **Agentic workflows and local Actor development** — [Agent Skills](https://apify.com/.well-known/agent-skills/index.json) with the [Apify CLI](https://docs.apify.com/cli/docs.md): `npm install -g apify-cli`, then `apify login`.
- **JavaScript/TypeScript projects** — the official [JS/TS client](https://docs.apify.com/api/client/js/docs.md): `npm install apify-client`.
- **Python projects** — the official [Python client](https://docs.apify.com/api/client/python/docs.md): `pip install apify-client`.
- **Any other language** — the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).

# README

## KLEKT Sneaker & Apparel Resale Scraper

Scrapes **KLEKT** (klekt.com) — Europe's leading authenticated sneaker and
apparel resale marketplace. Returns structured product records: brand, SKU,
description, market prices (last sold / lowest listing / highest bid),
size, box condition, availability and images.

### What it does

- **Browse mode (default)** — walks KLEKT's own catalogue page by page, with
  filters for section, brand, category, size, box condition, listing type,
  availability and price. Run it with no input at all and it returns trending
  products straight away.
- **Product URLs mode** — paste KLEKT product page URLs, get the full record
  for each (JSON-LD product data + the on-page market-price blocks).
- **Full catalogue mode** — stream the entire KLEKT catalogue from the official
  sitemaps (about 15,000 products) and enrich each product.
- **Send results into your apps (MCP connectors)** - optional.

### Input

| Field | Description |
| --- | --- |
| Scrape mode | "Browse the catalogue", "Product URLs" or "Full catalogue via sitemaps". Defaults to Browse. |
| Product URLs | KLEKT product page URLs, one per line (Product URLs mode). |
| Catalogue section | Which section Browse walks: Trending, Used / pre-owned, On sale, Drops, Upcoming, Gift cards. |
| Brand | Narrow to one brand (e.g. `Nike`), written the way KLEKT displays it. |
| Brand line / model family | Optionally narrow further inside a brand (e.g. `Dunk Low`). |
| Product category | Sneakers, Streetwear or Accessories. |
| Size category | Men, Women, Youth or Default. |
| Size system | Which system the Size below is written in (US, UK, EU, ...). Required together with Size — a size on its own is ignored by KLEKT. |
| Size | One size in the system chosen above (e.g. `10`, `44`, `M4/W6`). |
| Box condition | Good, Missing lid, Damaged, No box, Replacement box. |
| Listing type | Used or New with defect — only applies to the Used section. |
| Availability | Available, Express dispatch or Used available. |
| Minimum price (EUR) | Whole euros. 0 or empty = no lower bound. |
| Maximum price (EUR) | Whole euros. 0 or empty = no upper bound. |
| Fetch product details | On by default. **Browse mode only**: off there keeps the lighter catalogue row and skips the enrichment charge. Product URLs and Full catalogue mode have no catalogue row, so they always fetch the product page (the log says so if you switch it off) — otherwise you would be billed a product record for a row holding nothing but the URL you supplied. |
| Max items | Stop after this many products. 0 = unlimited. |
| Max catalogue pages | Optional safety bound on the Browse walk (20 products per page). 0 (the default) = no page bound: the walk stops by itself at the end of the section, or earlier when Max items is reached. |
| Resume from a previous run | Paste a previous run ID or dataset ID to continue a large crawl without returning, or charging for, records already collected there. |
| Incremental changes for scheduled runs | Turn on for recurring monitoring: the first run returns everything as NEW, later runs only NEW / UPDATED (and REAPPEARED) records. See the section below. |
| State key | Optional name for an incremental monitoring campaign; keeps state stable across input tweaks, or deliberately shares it. |
| Emit unchanged records | Off by default. On, incremental runs also return unchanged rows marked UNCHANGED (a full snapshot — returns and bills extra rows). |
| Emit expired records | Off by default. On, a run that fully scanned the tracked scrape also returns rows that vanished, marked EXPIRED. |
| Proxy configuration | KLEKT is open — the default Apify proxy is fine. Whichever proxy groups you select are the ones used, and turning Apify Proxy off really does connect directly. |
| MCP connectors | Optionally pipe results into Notion / Linear / Airtable / Apify. Never changes the dataset. |
| Notion parent page | The Notion page under which one page per product is created (Notion connector only). |
| Max products to export per connector | Cap on products written to each connector per run. Does not affect the dataset. |

Schema keys for API/task-input use: `mode`, `productUrls`, `strategy`, `brand`,
`brandLine`, `productCategory`, `sizeCategory`, `sizeMetric`, `size`,
`boxCondition`, `listingType`, `availability`, `priceFrom`, `priceTo`,
`fetchDetails`, `maxItems`, `maxPages`, `resumeFromRunId`, `incrementalMode`,
`stateKey`, `emitUnchanged`, `emitExpired`, `proxy`, `mcpConnectors`,
`notionParentPageUrl`, `maxNotifyListings`.

#### About the filters

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

Every filter offered here was checked against the live site, and the actor
refuses to pretend. If KLEKT would silently ignore a setting, the actor drops
it and says so in the run log rather than sending it and returning an
unfiltered result:

- **Size** is dropped unless a **Size system** is chosen too (KLEKT only
  applies the pair).
- **Listing type** is dropped outside the Used section, where KLEKT either
  returns nothing or ignores it.
- An **Availability** or **Catalogue section** value outside the offered list
  is replaced/dropped — KLEKT would otherwise return its entire catalogue.
- Non-numeric price bounds are dropped.

### Output

One record per product (Browse on Trending / Drops / Upcoming and the two URL
modes) or per marketplace listing (Browse on Used / On sale):

`name`, `brand`, `sku`, `slug`, `url`, `description`, `colorway`,
`priceAmount`/`priceCurrency`, `lastSoldPrice`, `lowestListingPrice`,
`lowestListingSize`, `highestBid`, `size`, `boxCondition`, `listingType`,
`availability`, `releaseYear`, `images`, `imageUrl`, `seller`,
`detailScraped`, `id`, `type`.

`priceAmount` is the **catalogue price** — the number KLEKT's own Min / Max
price filter matches on — so a price-bounded run always returns rows inside
the range you asked for, whether or not detail enrichment is on. For a Used or
On sale row that is the listing's own asking price. In the two URL modes,
where there is no catalogue row, it falls back to the product page's most
recent verified sale, otherwise the cheapest live listing.

The product page's own market numbers are always delivered alongside it, under
their own keys: `lastSoldPrice` (the most recent completed sale of the model —
frequently well above the current catalogue price), `lowestListingPrice` and
`highestBid`.

Sample values below are illustrative placeholders, not live listings.

```json
{
  "id": "00000000-0000-0000-0000-000000000000",
  "name": "Sample Runner 1 'Example' (2024)",
  "brand": "Sample Brand",
  "sku": "XX1234-000",
  "url": "https://klekt.com/product/new/sample-runner-example-2024",
  "priceAmount": 125.0,
  "priceCurrency": "EUR",
  "availability": "InStock",
  "detailScraped": true
}
```

### Resume and recurring updates (incremental mode)

Two related but different features live in the **🔁 Resume and recurring
updates** section:

- **Resume from a previous run** (`resumeFromRunId`) continues ONE specific
  interrupted run: paste its run ID (or dataset ID) and records already
  collected there are neither returned nor billed again.
- **Incremental changes for scheduled runs** (`incrementalMode`) is for running
  the same scrape again and again — for example daily. The first run returns
  everything as NEW. Later runs compare every scraped product against the
  previous run's saved baseline and return only what changed: NEW products,
  UPDATED ones (with the changed fields listed), and REAPPEARED ones for
  products that come back after vanishing. Unchanged products are suppressed
  entirely unless **Emit unchanged records** (`emitUnchanged`) is on.

With **Emit expired records** (`emitExpired`) on, a run that scanned the
tracked scrape to its natural end also returns every previously-seen product
it can no longer find, marked EXPIRED. That guarantee is deliberately strict:
a run stopped early by **Max items**, seeded from **Resume**, or interrupted
by a refused connection cannot tell "gone" from "not reached yet", so it
skips EXPIRED detection and keeps the previous state as-is. The run log
always states which of the two happened.

Incremental state is kept in the actor's own key-value store, keyed on the
scrape itself (mode, section, filters, detail setting) — two different
scrapes never mix state. Set **State key** (`stateKey`) to name a campaign
explicitly, e.g. to keep monitoring stable while tweaking an unrelated input,
or to deliberately share one baseline across differently-configured runs.

Records classified in incremental mode carry four extra fields:
`changeType` (NEW / UPDATED / UNCHANGED / REAPPEARED / EXPIRED), `changedFields`,
`firstSeenAt` and `lastSeenAt`. A normal (non-incremental) run's output shape
is untouched.

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

Optionally pipe scraped KLEKT products into the tools you already use via
Model Context Protocol connectors (Notion, Linear, Airtable, Apify). Authorize
a connector under Apify → Settings → API & Integrations, then select it in
`mcpConnectors`. Notion gets a rich page per product (set `notionParentPageUrl`
to the page they should be created under); other connectors get a best-effort
write or digest. Use `maxNotifyListings` to cap how many are
sent — that number is what is actually delivered, and the run log reports how
many each connector took and whether it accepted them.

This is an optional side channel: it never changes the dataset output, and a
connector error — including a connector that accepts the connection and then
goes silent — is logged as a warning without failing or stalling the run. The
export runs after every row is already in the dataset and is time-bounded, so
it can never hold the run open.

### Notes

- KLEKT authenticates every sale itself (fixed marketplace model), so records
  carry the platform as seller rather than individual seller contacts.
- Pricing fields reflect what the product page exposes: `lastSoldPrice` is the
  most recent verified sale; `lowestListingPrice` and `highestBid` appear when
  the page shows them.
- `brand` is only ever read from the product itself — it is never a copy of
  the Brand filter you typed. KLEKT's catalogue rows carry no brand, so with
  **Fetch product details** off the field is empty; enrichment fills it from
  the product page. Pre-owned listing pages (`/product/used/<id>`) do not
  publish a brand at all, so it can stay empty on Used / On sale rows.
- Filters that a section would silently ignore are dropped and reported in the
  run log instead of being sent for show. The Gift cards section ignores every
  catalogue filter, so a filtered Gift cards run returns the whole section and
  says so.
- No login required. The source is open to standard connections; a proxy is
  optional.

# 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 product page URLs to scrape (one per line). Only applies in Product URLs mode. New products are https://klekt.com/product/new/<slug>; pre-owned listings are https://klekt.com/product/used/<listing-id>.

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

Which KLEKT catalogue section to walk. Trending, Drops and Upcoming return product rows; Used and On sale return individual marketplace listings (these are the only sections carrying size, box condition and listing type); Gift cards returns the handful of gift-card products and is the one section that ignores EVERY filter below, so a filtered Gift cards run returns the whole section and the dropped filters are listed in the run log. Anything outside this list is ignored by KLEKT and would return the entire catalogue, so an unrecognised value is replaced with Trending and reported in the log. These filters apply only in Browse mode; Product URLs and Sitemap reads ignore them.

## `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 — use it together with Brand (e.g. Brand = Nike with Dunk Low, Air Force, Air Max 90, Air Max 1; Brand = Adidas with Samba, Campus, Gazelle, Forum, Stan Smith; Brand = Air Jordan with Jordan 1, Jordan 4, Jordan 11; Brand = New Balance with 550, 990, 2002, 9060). Written the way KLEKT displays it. Leave empty for the whole brand.

## `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. This is the complete set KLEKT publishes, and the four counts add up to the unfiltered total.

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

The size system the Size below is written in. KLEKT only applies a size filter when BOTH this and Size are set — either one on its own is ignored by the site, so the actor drops a half-filled pair and says so in the run log.

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

One size in the size system chosen above, exactly as KLEKT writes it — e.g. 10, 10.5, 44, M4/W6, 3.5Y, 4C. Free text because each size system has its own list (US alone has over a hundred values). Requires Size system: a size on its own is ignored by KLEKT, so it is dropped with a warning instead of pretending to filter.

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

Condition of the shoe box. This is the complete set — the five counts add up exactly to the unfiltered total. Most meaningful on the Used and On sale sections, where every row is an individual listing.

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

Split the Used section into genuinely used pairs and new-with-defect pairs. This only narrows when Catalogue section = Used / pre-owned; on the other sections KLEKT either returns nothing or ignores it entirely, so the actor drops it there and says so in the run log.

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

Limit to products KLEKT flags as available, express-dispatch, or available pre-owned. Keep to these options: KLEKT ignores any other value and would quietly return everything, so the actor drops unrecognised values. On the Used section only Express dispatch returns rows.

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

Only return products priced at or above this many euros. Whole euros, not cents. 0 (the default) or empty = no lower bound.

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

Only return products priced at or below this many euros. Whole euros, not cents. 0 (the default) or empty = 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. 0 = unlimited (stop only at the end of the catalogue). Counts records actually returned, not pages read.

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

Optional safety bound on how many catalogue pages Browse mode walks (20 products per page). Leave 0 (the default) for no page bound: the walk stops by itself at the end of the section, or earlier when Max items is reached.

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

Paste a previous run ID or dataset ID to continue a large crawl without returning, or charging for, records already collected there. For recurring monitoring of the same scrape use Incremental mode below instead.

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

Turn this on for daily or recurring monitoring of the same scrape. The first run returns everything as NEW. Later runs normally return only NEW and UPDATED records, plus REAPPEARED ones for products that come back after vanishing. State is kept separately for each combination of mode, catalogue section, filters and detail setting; use State key to name or deliberately share a monitoring campaign. To continue one specific interrupted run instead, use Resume from a previous run above.

## `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 let the actor derive a key from the scrape itself, so two different scrapes never mix state.

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

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

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

Off by default. Turn on to also return records that were present in a previous run but are no longer found, marked EXPIRED. Only produced once a run has fully scanned the tracked scrape, never when Max items capped it, when Resume was used, or when the walk was interrupted. This returns, and bills, extra synthetic rows.

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

## `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"
  ],
  "strategy": "trending",
  "brand": "",
  "brandLine": "",
  "productCategory": "",
  "sizeCategory": "",
  "sizeMetric": "",
  "size": "",
  "boxCondition": "",
  "listingType": "",
  "availability": "",
  "priceFrom": 0,
  "priceTo": 0,
  "fetchDetails": true,
  "maxItems": 20,
  "maxPages": 0,
  "resumeFromRunId": "",
  "incrementalMode": false,
  "stateKey": "",
  "emitUnchanged": false,
  "emitExpired": false,
  "proxy": {
    "useApifyProxy": true
  },
  "mcpConnectors": [],
  "notionParentPageUrl": "",
  "maxNotifyListings": 50
}
```

# Actor output Schema

## `overview` (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"
    ],
    "strategy": "trending",
    "brand": "",
    "brandLine": "",
    "size": "",
    "priceFrom": 0,
    "priceTo": 0,
    "fetchDetails": true,
    "maxItems": 20,
    "maxPages": 0,
    "incrementalMode": false,
    "emitUnchanged": false,
    "emitExpired": false,
    "proxy": {
        "useApifyProxy": true
    }
};

// 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"],
    "strategy": "trending",
    "brand": "",
    "brandLine": "",
    "size": "",
    "priceFrom": 0,
    "priceTo": 0,
    "fetchDetails": True,
    "maxItems": 20,
    "maxPages": 0,
    "incrementalMode": False,
    "emitUnchanged": False,
    "emitExpired": False,
    "proxy": { "useApifyProxy": True },
}

# 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"
  ],
  "strategy": "trending",
  "brand": "",
  "brandLine": "",
  "size": "",
  "priceFrom": 0,
  "priceTo": 0,
  "fetchDetails": true,
  "maxItems": 20,
  "maxPages": 0,
  "incrementalMode": false,
  "emitUnchanged": false,
  "emitExpired": false,
  "proxy": {
    "useApifyProxy": true
  }
}' |
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/kyjdV0ExvuVsG9yeU/openapi.json
