# Wescover Artist & Product Scraper (`artsiom_k/wescover-scraper`) Actor

Scrape Wescover.com functional/decorative art and design products (materials, price range, and the real venue each piece is installed at) and maker/creator profiles, with built-in delta mode. No login required.

- **URL**: https://apify.com/artsiom\_k/wescover-scraper.md
- **Developed by:** [Artsiom Kunitsyn](https://apify.com/artsiom_k) (community)
- **Categories:** Other, E-commerce
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $5.00 / 1,000 results

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.

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

## wescover-scraper

Scrapes both **products** (functional/decorative art and design pieces — furniture, tiles,
lighting, textiles, and more, with materials, price range, and the real venue each piece is
installed at) and **maker/creator profiles** from [Wescover.com](https://www.wescover.com), a
marketplace connecting independent makers with businesses and homeowners. No login required.

### Contents

- [Key features](#key-features)
- [Output](#output)
- [Input](#input)
- [Input examples](#input-examples)
- [Incremental (delta) mode](#incremental-delta-mode)
- [FAQ](#faq)

### 🔑 Key features

- **Two entity types, one Actor.** Set `entityType` to `artworks` (products) or `artists` (makers).
- **Real installation-venue data** — many products carry a `space` (the actual business a piece is
  installed at: name, city/state/country, business type like "restaurant"). Not just who made it,
  but where it lives — a genuinely different angle from a typical art-marketplace listing.
- **Choose which categories to include.** Set `categories` to one or more of the site's real
  top-level categories (Furniture, Tiles, Lighting, Textiles, and more) to filter the results, or
  leave it empty to include everything.
- **A real per-maker rollup with no extra fetch cost per product.** A maker's own page reports
  their complete product list and full bio/tags in one fetch — `entityType: "artists"` doesn't
  need to examine every product on the site to build this.
- **Real materials/color tags, production-time estimates, and a genuine "one of a kind" flag** —
  read from the same rich page data the site's own UI uses, not guessed from the description text.
- **Delta mode built in.** Every run classifies each item as `new`, `changed`, `unchanged`, or
  `delisted` against a persisted baseline — pay for what changed, not a full re-scrape every time.
- **Confirmed-dead listings are never re-fetched.** A removed product or maker silently redirects
  to a generic listing page rather than 404ing — detected and tracked in a persisted registry so a
  future run skips it without a wasted request.
- **`maxItems` defaults to 50** — a fast, cheap preview, and what keeps an unconfigured run within
  Apify's automated 5-minute QA check. Clear it (`null`) for a full crawl, or narrow `categories`
  instead.

### 📋 Output

One dataset item per product or maker, depending on `entityType` — see
[`.actor/dataset_schema.json`](.actor/dataset_schema.json) for the full field list.

**Example product record:**

```json
{
  "source": "wescover",
  "entity_type": "artworks",
  "external_id": "PSyk5hgvpwQZ",
  "url": "https://www.wescover.com/p/sun-tanned-poplar-tables-by-monkwood-studios-at-lord-stanley--PSyk5hgvpwQZ",
  "name": "Sun Tanned Poplar Tables",
  "category_path": "Furniture/Furniture/Tables",
  "materials": ["wood"],
  "colors": ["brown"],
  "is_one_of_a_kind": true,
  "production_time_weeks_min": 12,
  "production_time_weeks_max": 20,
  "currency": "USD",
  "price_min": 1500,
  "price_max": 3000,
  "status": "active",
  "maker_name": "Monkwood Studios",
  "maker_url": "https://www.wescover.com/creator/monkwood",
  "maker_tags": ["reclaimed_materials", "handmade", "sustainable"],
  "space_name": "Lord Stanley",
  "space_types": ["restaurant"],
  "space_location": "San Francisco, CA, United States",
  "change_type": "new"
}
```

**Example maker record:**

```json
{
  "source": "wescover",
  "entity_type": "artists",
  "external_id": "MSk7X7gTBS",
  "url": "https://www.wescover.com/creator/deco",
  "name": "Deco",
  "types": ["Tiles", "Architecture", "Wall Treatments"],
  "location": "Italy",
  "is_claimed": true,
  "product_count": 13,
  "change_type": "new"
}
```

### 🔧 Input

| Field | Type | Default | Description |
|---|---|---|---|
| `entityType` | string | `artworks` | `artworks` (products) or `artists` (makers). |
| `categories` | array | — | Only include items matching one of these categories. A push-time filter — the full site is still walked either way. Leave empty for everything. |
| `startUrls` | array | — | Specific product/maker URLs to scrape directly instead of the full sitemap. |
| `maxItems` | integer | `50` | Stop after pushing this many items. Set to `null` for a full crawl. |
| `mode` | string | `auto` | `auto` / `full` / `incremental` — see [Incremental mode](#incremental-delta-mode). |
| `concurrency` | integer | `5` | How many product/maker detail pages to fetch in parallel. Kept conservative — see FAQ. |
| `impersonate` | string | `chrome` | curl\_cffi TLS-impersonation target. |

Residential proxy is used automatically for every run — see FAQ below. It isn't an input option; there's no way to disable it.

### 📥 Input examples

**Default preview (50 products):**

```json
{ "entityType": "artworks" }
```

**Every tile product:**

```json
{ "entityType": "artworks", "categories": ["tiles"], "maxItems": null }
```

**Full maker directory:**

```json
{ "entityType": "artists", "maxItems": null }
```

### 🔁 Incremental (delta) mode

`auto` mode does a full scan the first time it runs for a given `entityType`/scope, then only pushes
new/changed items on later runs — including price and status changes (`price_min`/`price_max`/
`status` are the watched fields for products; `product_count` for makers). Only an uncapped run
using the same `categories` selection each time (no `maxItems` limit reached, no `startUrls`, no
item errors) can detect delistings or update the baseline.

### ❓ FAQ

**Why is `concurrency` only 5 by default?**
A real, deliberate caution: while building this Actor, a short burst at higher concurrency produced
a cluster of HTTP 429s, and retrying immediately afterward made it noticeably worse (timeouts
instead of clean rejections). Detail pages tested clean at higher concurrency after a cooldown, but
5 is kept as a safety margin — raise it carefully if you need more throughput.

**Does `categories` scope the crawl to just that category (faster/cheaper)?**
No — it's a filter applied after fetching each item, not a way to skip the rest of the site. The
site's own category-browse pages carry a much stricter rate limit than individual product/maker
pages, so this Actor deliberately never uses them.

**Why does a product sometimes have no `space` (installation venue)?**
A real, honest gap — not every product on the site is tied to a documented installation; some are
simply catalog items available for purchase. Nothing is guessed when the page itself has no venue
data.

**Does this need a proxy?**
Yes — Residential proxy is mandatory and always on, not an input you can toggle. A real large-scale
run with no proxy hit a 429 storm severe enough to fail the whole run; Datacenter proxy (tried first
as the cheaper tier) made it worse, since Cloudflare fingerprints datacenter IPs and returned instant
403s on ~95% of requests. Residential IPs read as ordinary ISP traffic and avoided both problems —
verified clean at 2,000 items, 0 errors.

# Actor input Schema

## `entityType` (type: `string`):

Scrape products (functional/decorative art and design pieces, with materials, price range, and the real venue they're installed at) or maker/creator profiles (bio, tags, category footprint, and a complete product list — no extra fan-out cost). See dataset\_schema.json for the field set of each.

## `categories` (type: `array`):

Optional: only push items whose real category matches one of these. This is a push-time filter, not a discovery scope — the full site is still walked either way (wescover.com's own category-browse pages carry a much stricter rate limit than product/maker pages, so this actor deliberately never uses them for discovery). Leave empty to include everything.

## `startUrls` (type: `array`):

Optional list of specific Wescover product (https://www.wescover.com/p/{slug}--{sid}) or maker (https://www.wescover.com/creator/{slug}) URLs to scrape directly, instead of discovering via the full sitemap — use whichever kind matches entityType. Overrides "Categories to include". A startUrls-scoped run cannot detect delistings or update the incremental baseline.

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

Stop after pushing this many dataset items. Defaults to 50 — a fast, cheap preview, and what keeps an unconfigured run within Apify's automated 5-minute QA check. The full site has ~60,000 products across ~6,300 makers. Raise this or clear it (set to null) for a full run, or narrow "Categories to include" instead.

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

"auto" (recommended): full scan on the first run for a given entityType/scope, incremental (new/changed only) afterwards. "full": always push every item and refresh the baseline — schedule this periodically to catch delistings and price changes. "incremental": always push only new/changed items. Only a plain, unscoped run (no startUrls, and the same "Categories to include" every time) can detect delistings or update the baseline.

## `concurrency` (type: `integer`):

How many product/maker detail pages to fetch in parallel. Defaults to 5 — deliberately conservative: a real rate-limit incident was hit while building this actor at higher concurrency (a burst at 30 got HTTP 429s, and retrying immediately at 10 made it much worse). Detail pages (unlike this site's category-browse pages) tested clean at concurrency 8 after a cooldown, but 5 is kept as a safety margin. Raise cautiously.

## `impersonate` (type: `string`):

curl\_cffi browser TLS-impersonation target. Defaults to "chrome" internally.

## Actor input object example

```json
{
  "entityType": "artworks",
  "maxItems": 50,
  "mode": "auto",
  "concurrency": 5
}
```

# Actor output Schema

## `results` (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 = {};

// Run the Actor and wait for it to finish
const run = await client.actor("artsiom_k/wescover-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 = {}

# Run the Actor and wait for it to finish
run = client.actor("artsiom_k/wescover-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 '{}' |
apify call artsiom_k/wescover-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,artsiom_k/wescover-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/motGiMwcNOzCg0nZh/builds/JDvzE3SxJ3Jm3mrGM/openapi.json
