# Chairish Scraper: New Listings & Price Drops (USD) (`superslowsloth/chairish-listings`) Actor

Chairish scraper: search terms or list pages to one row per listing with USD price, price before reduction, discount percent, condition, designer, colour, material, size, category and seller. Sort newest first for a vintage furniture new-listing feed.

- **URL**: https://apify.com/superslowsloth/chairish-listings.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 $1.05 / 1,000 listing 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

## Chairish Scraper: New Listings & Price Drops (USD)

Search [Chairish](https://www.chairish.com) (the US marketplace for vintage and
designer furniture and decor) by keyword, or read a collection, keyword, maker,
shop or style page, and get one flat row per listing: the price in **USD**, the
price before the seller's reduction and the percent off, condition, designer,
colour, material, size, category, seller and the photo. Sort **Newest** and run
it on a schedule to get a **new-listing feed** for vintage furniture dealers and
interior designers; keep `onlyOnSale` on to get a **price-drop watch**.

No account, no API key, no login, no browser. One request returns 48 listings.

### Input

| Field | Notes |
|---|---|
| `searchTerms` | One or more search terms, one per line (`mid century lamp`, `travertine coffee table`). A term Chairish recognises is sent to its own list page (`lamp` goes to `/collection/lamps`), and that whole list is read; any other term is read as search results. A listing found by two terms is returned once, under the first. |
| `startUrls` | Chairish pages to read in full: `https://www.chairish.com/collection/new-arrivals` (the newest listings), any `/collection/...`, `/keyword/...`, `/maker/...`, `/shop/...` or `/style/...` page, or a search URL (`/search?q=...`, read as that term). A price filter copied from the site (`?price=100-200`) is kept. Product pages are refused. Give terms, URLs or both. |
| `maxItems` | Listings to read per term or page (default 100, up to 4,752). 48 per page. |
| `onlyOnSale` | Return only listings the seller has reduced (`on_sale`). Listings that are not reduced are read, not returned and not charged, and do not count toward `maxItems`. |
| `sort` | `relevance` (Chairish's Curated default), `newest`, `price-asc`, `price-desc`, `most-favorited`: the choices of the site's own sort menu. |
| `proxyConfiguration` | Apify datacenter proxy by default. |

Example input, a new-listing feed of lamps:

```json
{
  "searchTerms": ["lamp"],
  "maxItems": 200,
  "sort": "newest"
}
```

Example input, reduced mid-century pieces between $100 and $500:

```json
{
  "startUrls": [{"url": "https://www.chairish.com/style/mid-century-modern?price=100-500"}],
  "maxItems": 300,
  "onlyOnSale": true,
  "sort": "price-asc"
}
```

### Output

Prices are numbers in US dollars; `currency` is `USD` (every listing measured).

| Field | Notes |
|---|---|
| `query`, `position` | The search term or page URL that first found the listing, and its place in Chairish's list for that source and sort (1 is first). |
| `listing_id`, `url` | Chairish's listing id (`36744474`) and the listing link. |
| `title`, `image_url` | As the page shows them. `image_url` is the first photo (WebP). |
| `price` | The price Chairish shows now. |
| `original_price` | The price **before** the seller's reduction. **Null when the listing is not reduced**, never equal to `price`. |
| `on_sale` | True when Chairish shows the listing as reduced (a price before the reduction). |
| `discount_percent` | The percent off as Chairish prints it ("10% Off"), a whole number. Null when not reduced. Chairish rounds, so it can differ by one from working it out from the two prices. |
| `price_unit` | `item` or `set` when the page says how the price is quoted; null for most listings. |
| `category` | Chairish's category path, e.g. `Lighting/Lamps/Table Lamps`. |
| `status`, `purchasable`, `quantity`, `on_hold` | `Listed for Purchase` or `At Auction`; false for an auction lot; units for sale (always 1 so far); whether a buyer holds it. |
| `promoted`, `newly_made`, `made_to_order`, `local_pickup` | Chairish's flags: a promoted (ad) tile, a new piece from a maker, made to order, local pickup offered. |
| `ships_to` | Country codes the listing ships to (`["US"]`); empty when not sent. |
| `seller_id` | Chairish's seller id (`mjs6jz`); `https://www.chairish.com/shop/<seller_id>` is the seller's page. |
| `condition` | `new`, `used` or `refurbished`. |
| `seller_country` | The seller's country code. |
| `designer` | The designer or maker, when the listing names one. |
| `color`, `material` | As the seller entered them. |
| `width_in`, `depth_in`, `height_in` | Size in inches when the listing gives it. |
| `description` | The seller's description, line breaks kept. |

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

```json
{
  "query": "https://www.chairish.com/collection/new-arrivals",
  "position": 6,
  "listing_id": "37559143",
  "title": "1 of 2 Randolph & Hein English Roll Arm Down Filled Club Chair",
  "url": "https://www.chairish.com/product/37559143/1-of-2-randolph-hein-english-roll-arm-down-filled-club-chair",
  "image_url": "https://chairish-prod.freetls.fastly.net/image/product/sized/f12504b2-0c32-4239-834e-af8cb1d04d42/1-of-2-randolph-and-hein-english-roll-arm-down-filled-club-chair-7779",
  "currency": "USD",
  "price": 2950.0,
  "original_price": 5250.0,
  "on_sale": true,
  "discount_percent": 44.0,
  "price_unit": "item",
  "category": "Furniture/Seating/Accent Chairs/Club Chairs",
  "status": "Listed for Purchase",
  "purchasable": true,
  "quantity": 1,
  "on_hold": false,
  "promoted": false,
  "newly_made": false,
  "made_to_order": false,
  "local_pickup": true,
  "ships_to": ["US"],
  "seller_id": "08dgah",
  "condition": "used",
  "seller_country": "US",
  "designer": "Randolph & Hein",
  "color": "Beige",
  "material": "Chenille/Feather",
  "width_in": 40.0,
  "depth_in": 38.0,
  "height_in": 35.0,
  "description": "Randolph & Hein English Roll Arm Down Filled Club Chair. ..."
}
```

#### Honest nulls

A field the page did not send is `null`, never `0` or `""`. `ships_to` is an empty
list when the page sends none.

- **`condition`, `seller_country`, `designer`, `color`, `material`, the three
  sizes and `description` come from the page's structured data**, which names a
  group of colour variants of one design by its first member: ten tiles of one
  glass table can arrive as three listing ids. A tile whose own id the structured
  data does not carry gets null in these fields. It is never given a sibling
  colour's values. On ordinary pages this is one tile in 48 or none; on a search
  full of colour variants (`abacus`) it is most of them.
- **`original_price` and `discount_percent` are null on a listing that is not
  reduced.**
- **No listing date.** Chairish's page does not give one. `sort: newest` is the
  site's own "Newest" sort, ordered by the seller's most recent activity, so an
  older listing that was just relisted or reduced appears near the top. Keep the
  `listing_id`s you have seen and diff against them to find what is new.
- **No seller name or rating, no shipping price.** Not in the list pages.

### 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.00105** per listing row written (`listing-scraped`), i.e. $1.05 per
  1,000 listings. A row that is not written is not charged: rows filtered by
  `onlyOnSale`, and repeats of a listing another term already returned, are free.

### How it fetches

It reads the product grid that chairish.com's own search and list pages embed in
their page data, plus the page's structured data for the extra fields. Measured
2026-10-17: HTTP 200 HTML (0.4 to 1 MB a page) from a plain home connection and
from an Apify datacenter address, no key or cookie needed. A refusal (403, 429,
5xx), a 200 that is not a Chairish page, 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:

- **A search does not always return search results.** `lamp` is sent to
  `/collection/lamps`, `pagoda` to `/keyword/pagodas`; `abacus` and `mid century
  lamp` are answered in place. The actor reads whatever list the site lands on,
  all its pages, and logs where it went. `query` stays the term you typed. A
  broad term can therefore return a whole collection; `maxItems` bounds it.
- **Depth: 99 pages, 4,752 listings per term or page.** Chairish serves page 99
  of a list and refuses page 100 and above with an HTTP 403 whatever the
  address, so the actor never asks for it. The match count the site reports is
  exact below 10,000 and stops at 10,000 above that. A list with more than 4,752
  listings is read to its 99th page and logged as capped; narrow it with a price
  filter in a start URL (`?price=100-200`) or a more specific term.
- **Pages overlap a little.** On a ranked search the last few listings of one
  page can reappear at the top of the next (5 repeats in 3 pages of `mid century
  lamp`). A listing already returned is skipped and not charged, so `position`
  can jump by the number skipped.
- **The end of a list is an HTTP 404.** A page past the last one (page 4 of a
  106-match search) answers 404, not an empty page. The actor reads that as the
  end, not a failure.
- **The sort is checked, not trusted.** The site ignores a sort it does not know
  and answers in its default order. The page's sort menu says which sort it
  applied, and a run that did not get the sort it asked for fails that source
  rather than return the wrong order. The sort survives a term's redirect to a
  list.
- **Filters.** `price=<min>-<max>` in a start URL filters. `condition=` and
  `color=` were measured as ignored, so they are not offered.
- **Totals.** A search with no match is an empty result, logged, not an error.

If Chairish changes its page data, the run fails with a message instead of
returning empty rows.

# Actor input Schema

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

What you would type into the Chairish search box, one term per line (for example mid century lamp, brass floor lamp or travertine coffee table). Each term is read separately. A term Chairish recognises is sent to its own list page (lamp goes to /collection/lamps) and that whole list is read; any other term is read as search results. A listing that two terms both find is returned once, under the first.

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

Chairish pages to read in full, for example https://www.chairish.com/collection/new-arrivals (the newest listings), a /collection/..., /keyword/..., /maker/..., /shop/... or /style/... page, or https://www.chairish.com/search?q=lamp. A price filter copied from the site (?price=100-200) is kept. Product pages (/product/...) are not accepted. Leave empty to use search terms only.

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

Read at most this many listings for each search term or page. Chairish lists 48 per page, so 100 reads three pages. Chairish serves 99 pages of any list, so up to 4,752 are accepted; narrow a bigger list with a price filter in the start URL.

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

Return only listings the seller has reduced (the on_sale field: Chairish shows a price before the reduction). Listings that are not reduced are read but not returned and not charged, and do not count toward Max listings.

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

The order Chairish lists the listings in, the same choices as its own sort menu. 'Relevance' is its default (Curated). 'Newest' is the site's own newest sort: most recent seller activity first, which includes a relist or a price drop on an older listing.

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

Use Apify's automatic datacenter proxy (the default). chairish.com answered an Apify datacenter address with HTTP 200 when this actor was built; the residential group was not needed. A refused request is retried from a fresh address.

## Actor input object example

```json
{
  "searchTerms": [
    "mid century lamp"
  ],
  "startUrls": [],
  "maxItems": 100,
  "onlyOnSale": false,
  "sort": "relevance",
  "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 = {
    "searchTerms": [
        "mid century lamp"
    ],
    "startUrls": [],
    "proxyConfiguration": {
        "useApifyProxy": true
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("superslowsloth/chairish-listings").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 = {
    "searchTerms": ["mid century lamp"],
    "startUrls": [],
    "proxyConfiguration": { "useApifyProxy": True },
}

# Run the Actor and wait for it to finish
run = client.actor("superslowsloth/chairish-listings").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 '{
  "searchTerms": [
    "mid century lamp"
  ],
  "startUrls": [],
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}' |
apify call superslowsloth/chairish-listings --silent --output-dataset

```

## MCP server setup

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

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/yYbr2ie7WlfcrfGz8/builds/V9Cg6EWWZysDkluYT/openapi.json
