# World Market Scraper - Home Decor & Furniture Prices (`superslowsloth/worldmarket-products`) Actor

World Market scraper: category pages, ids or search terms to one row per product with USD price and price range, markdown, Sale and Clearance flags, in-stock status, rating and reviews, sku, link and photo. For home decor and furniture price, clearance and stock watching.

- **URL**: https://apify.com/superslowsloth/worldmarket-products.md
- **Developed by:** [Superslow Sloth](https://apify.com/superslowsloth) (community)
- **Categories:** E-commerce, Automation
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.69 / 1,000 product scrapeds

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?

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

## World Market scraper: home decor and furniture price, clearance and stock tracker (USA)

This World Market scraper lists [World Market](https://www.worldmarket.com) (the
US home decor and furniture retailer) by category page or by keyword and returns
one flat row per product: the current price in **USD** (and the high end when
the product is priced as a range), the struck-through price before a markdown,
the Sale and Clearance labels, in-stock status, rating and review count, sku,
the product link and the photo. Run it on a schedule and diff the dataset to get
a **home decor and furniture price, clearance and stock watch**: which dining
chairs went to Clearance since last week, what a sofa line costs today, what is
out of stock.

No account, no API key, no login, no browser. One request returns up to 96 products.

### Input

| Field | Notes |
|---|---|
| `categories` | World Market category pages, one per line: the page address (`https://www.worldmarket.com/c/furniture/`, `https://www.worldmarket.com/c/bath/bath-sale/`) or the category id (`116715`, `sale-bath`). A department page such as `/c/furniture/` lists every product in the department (1,742 on 2026-10-21). A search page address (`https://www.worldmarket.com/search?q=candle`) is read as that search. |
| `searchTerms` | One or more search terms, one per line (`wine glass`, `dining chair`). Each is read separately, in the site's own order. A term with no match returns nothing and costs nothing. Terms containing `<` or `>` are rejected: World Market's firewall answers them HTTP 403. |
| `maxItems` | Products to read per category and per term (default 100, up to 10,000). 96 per page. |
| `sort` | The site's own sort: `default`, `top-sellers`, `new-arrivals`, `rating`, `price-low-to-high`, `price-high-to-low`. It decides which products a `maxItems` cut-off keeps. |
| `onlyOnSale` | Return only products that are marked down (`on_sale`) or labelled Sale or Clearance (`sale_tag`, `clearance_tag`). Other products are read, not returned and not charged, and do not count toward `maxItems`. |
| `onlyInStock` | Return only products with `in_stock` true. Others are read, not returned and not charged. |
| `proxyConfiguration` | Apify datacenter proxy by default. |

At least one category or search term is required. A product that two sources both
find is returned once, under the first, and charged once.

Example input:

```json
{
  "categories": ["https://www.worldmarket.com/c/decor-and-pillows/", "sale-bath"],
  "searchTerms": ["wine glass"],
  "maxItems": 300,
  "sort": "price-low-to-high",
  "onlyOnSale": true
}
```

### Output

Prices are numbers in US dollars; `currency` is `USD` (the en_US site).

| Field | Notes |
|---|---|
| `category_id`, `query` | Where the product was found: the category id (`116715`, `sale-bath`) for a category, the search term for a search. The other is null. |
| `position` | The product's place in World Market's own order for that source (1 is first), counted over every product of the pages read. Counted before duplicates and the `onlyOnSale` / `onlyInStock` filters. |
| `product_id` | The id at the end of the product link (`665798`, `SET131078`). A string. **One row is one product page**, so `product_id` is the unique key. |
| `sku`, `variant_skus` | The tile's default-variant sku and every variant sku the tile lists. **`sku` is not unique**: a collection tile carries a member item's sku, so two different products can share it (295 distinct skus over 301 products of one category, all 301 links distinct). Use `product_id` to key rows. |
| `name`, `url`, `image_url` | As the page shows them. `image_url` is the tile photo with the site's own resize parameters. |
| `categories` | The site's category trail for the product, most specific first. Empty if none. |
| `price` | The price a customer pays, as the tile shows it (the sale price when the tile shows one). For a **price range** (a product with several variants priced differently) this is the low end. For a set (`Set of 2`) it is the price of the set. Null if the tile shows no price. |
| `price_high` | The high end of a price range. **Null when the tile shows one price.** |
| `original_price`, `original_price_high` | The struck-through price before a markdown (low and high end of a range). Null when the tile shows no markdown. |
| `on_sale` | True when `price` is below `original_price` (or, for a range, the high end is below `original_price_high`). Worked out from the prices shown. |
| `discount_amount`, `percent_off` | Dollars off and whole percent off. **Null unless on sale and neither price is a range**, never 0. |
| `quantity` | Units the listing sells together: 2 for a `Set of 2`. The site's own per-tile price number is per unit (half of `price` for a set); `price` is what the tile shows and what is paid. |
| `availability`, `in_stock` | Stock as the tile states it, lower case: `in stock`, `out of stock` or `in store only`; and a flag: true for in stock, false for out of stock, **null for in store only** (not bought online). |
| `fulfillment` | The tile's fulfilment badge: `Shipping Only` or `Pick Up Only`. Null when none. |
| `price_label` | The site's label: `Sale` or `Clearance`. Null when none. |
| `sale_tag`, `clearance_tag`, `new_tag`, `best_seller_tag` | The site's flags as sent. They do not always agree with the prices: a tile labelled Sale with one price and no struck-through price was seen, and Clearance tiles carry no Sale tag. `on_sale` and these flags are reported apart; `onlyOnSale` accepts either. |
| `rating`, `review_count` | The average star rating (0 to 5) and how many reviews it is from. **`rating` is null for a product with no reviews** (the page sends 0.00, which is not a rating of 0); `review_count` is 0 then. |
| `currency` | `USD`. |

Example row (real values, 2026-10-21):

```json
{
  "category_id": "sale-bath",
  "query": null,
  "position": 7,
  "product_id": "657923",
  "sku": "657923",
  "variant_skus": ["657923"],
  "name": "Blanca Ivory Sculpted Leaf Bath Towel",
  "url": "https://www.worldmarket.com/p/blanca-ivory-sculpted-leaf-bath-towel-657923.html",
  "image_url": "https://www.worldmarket.com/dw/image/v2/BJWT_PRD/on/demandware.static/-/Sites-wm-master-catalog/default/dw8396116a/images/large/135267_XXX_v1.jpg?sw=385&sh=385&sm=fit&sfrm=jpg&q=80",
  "categories": ["Bath Towels", "Bathroom Towels & Linens", "Bath"],
  "currency": "USD",
  "price": 9.98,
  "price_high": null,
  "original_price": 19.99,
  "original_price_high": null,
  "on_sale": true,
  "discount_amount": 10.01,
  "percent_off": 50,
  "quantity": 1,
  "availability": "out of stock",
  "in_stock": false,
  "fulfillment": null,
  "price_label": "Clearance",
  "sale_tag": false,
  "clearance_tag": true,
  "new_tag": false,
  "best_seller_tag": false,
  "rating": 5.0,
  "review_count": 1
}
```

#### Honest nulls

A field the page did not send is `null`, never `0` or `""`. `variant_skus` and
`categories` are empty lists when there are none.

- **No product descriptions, dimensions or per-variant prices.** This is the
  category and search grid, one row per product page. A range shows its low and
  high end, not each variant.
- **No stock counts.** The site states in stock, out of stock or in store only;
  there is no quantity.
- **No store-level stock.** Results are the site's online view with no store selected.

### Pricing

Pay per event:

- **$0.002** per run (`actor-start`), charged only after your input is
  validated, so a run that fails on bad input costs nothing.
- **$0.00069** per product row written (`product-scraped`), i.e. $0.69 per
  1,000 products. A row that is not written is not charged: rows filtered by
  `onlyOnSale` or `onlyInStock`, and repeats of a product another source already
  returned, are free.

### How it fetches

It reads the product grid that worldmarket.com's own category and search pages
load (`Search-UpdateGrid`), an HTML fragment of product tiles whose `data-*`
attributes and price block carry the record. Measured 2026-10-21: HTTP 200 from a
plain home connection and from Apify datacenter and residential addresses (2 of 2
each), no key or cookie needed. A refusal (403, 429, 5xx), a 200 that is not a
product grid, or a dropped connection is treated as the address being blocked,
and the actor rotates to a fresh one and retries.

Things worth knowing, all measured:

- **Paging uses `start` and `sz`, and both are honoured.** The actor asks for 96
  per page; `start=10&sz=5` returns results 11 to 15, and `sz` above the match
  count returns everything in one body (a 301-product category, 2.6 MB). The grid
  carries the match count (`data-total-results`), and a `start` at or past it is
  HTTP 200 with no tiles, which is how a walk ends.
- **A category page address is turned into its id first.** The id is in the
  category page itself (`/c/furniture/` is 116715, `/c/bath/bath-sale/` is
  `sale-bath`, ids are not always numbers). The actor reads it with one extra
  request per category address. A category id the site does not know is answered
  like an empty one, so it shows as "no results", not as an error.
- **The tile's own price number is not the price.** It is empty on every tile priced
  as a range (91 of the first 200 furniture tiles) and per unit on a `Set of 2`.
  The price shown on the tile is what is read.
- **`sku` is not a key**, `product_id` (the id in the link) is: 1,740 of 1,740
  furniture tiles had distinct links, 1,635 distinct skus.
- **Sorting works and is validated.** The five sorts the site offers are sent as
  `srule`; the site silently ignores an unknown one, so the actor refuses it. The price
  sorts follow the site's own price index, which is close to but not exactly the price
  shown (cheapest first, not strictly ascending in `price`).
- **Rare unrendered tiles.** 2 of 1,740 furniture tiles came back as a server-side
  include that failed to render, with no product in it. They are skipped and counted
  in the log, not returned.
- **Totals drift** with stock changes during a long walk (a product that moves between two pages can be read twice or missed); a product read twice is returned once.

If World Market changes its grid, the run fails with a message instead of
returning empty rows.

# Actor input Schema

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

World Market category pages to list, one per line: the page address (for example https://www.worldmarket.com/c/furniture/ or https://www.worldmarket.com/c/bath/bath-sale/) or the category id from it (116715 for furniture, sale-bath). A department page such as /c/furniture/ lists every product in the department. A search page address (https://www.worldmarket.com/search?q=candle) is read as that search. Leave empty if you only use search terms.

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

What you would type into the World Market search box, one term per line (for example wine glass, dining chair or velvet pillow). Each term is read separately. A term the site does not match returns nothing and costs nothing. Terms containing < or > are refused by the site's firewall and rejected here. A product that two sources both find is returned once, under the first.

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

Read at most this many products for each category and each search term. World Market lists 96 per request here, so 250 reads three pages. Up to 10,000 are accepted.

## `sort` (type: `string`):

The order the site lists products in, which decides which ones a Max products cut-off keeps. Default is the site's own order. The price sorts put the cheapest or dearest first (by the site's own price index, so roughly rather than strictly in order); New arrivals puts the newest first.

## `onlyOnSale` (type: `boolean`):

Return only products that are marked down (on_sale: the price is below the struck-through price) or labelled Sale or Clearance by the site. Other products are read but not returned and not charged, and do not count toward Max products. A category with few markdowns reads many pages to find them.

## `onlyInStock` (type: `boolean`):

Return only products the site marks 'in stock' (in_stock is true). Out of stock and in-store-only products are read but not returned and not charged, and do not count toward Max products.

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

Use Apify's automatic datacenter proxy (the default). worldmarket.com answered Apify datacenter and residential addresses alike with HTTP 200 when this actor was built (2 of 2 each); the datacenter group is cheaper and was enough. A refused request is retried from a fresh address.

## Actor input object example

```json
{
  "categories": [
    "https://www.worldmarket.com/c/decor-and-pillows/"
  ],
  "maxItems": 100,
  "sort": "default",
  "onlyOnSale": false,
  "onlyInStock": false,
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}
```

# Actor output Schema

## `items` (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 = {
    "categories": [
        "https://www.worldmarket.com/c/decor-and-pillows/"
    ],
    "proxyConfiguration": {
        "useApifyProxy": true
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("superslowsloth/worldmarket-products").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 = {
    "categories": ["https://www.worldmarket.com/c/decor-and-pillows/"],
    "proxyConfiguration": { "useApifyProxy": True },
}

# Run the Actor and wait for it to finish
run = client.actor("superslowsloth/worldmarket-products").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 '{
  "categories": [
    "https://www.worldmarket.com/c/decor-and-pillows/"
  ],
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}' |
apify call superslowsloth/worldmarket-products --silent --output-dataset

```

## MCP server setup

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

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/Atia6iVClHLCn8Kri/builds/7JSwdu8SMCqNchHk0/openapi.json
