# IKEA Multi-Country Product Catalog Extractor (`vexaloom-labs/ikea-multi-country-product-catalog-extractor`) Actor

Search, browse, or fetch IKEA products across 50+ country storefronts, with price, stock status, measurements, ratings, images, and color/size variants.

- **URL**: https://apify.com/vexaloom-labs/ikea-multi-country-product-catalog-extractor.md
- **Developed by:** [vexaloom-labs](https://apify.com/vexaloom-labs) (community)
- **Categories:** E-commerce, Automation, Developer tools
- **Features:** Limited permissions
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.49 / 1,000 results

This Actor is paid per event and usage. You are charged both the fixed price for specific events and for Apify platform usage.
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

## IKEA Multi-Country Product Catalog Extractor

IKEA sells through separate storefronts in more than 50 markets, and each storefront has its own currency, language, catalog, and stock signals. This actor reads those storefronts directly and returns one clean record per article: price history, rating, measurements, category path, images, color variants, and — on demand — designer, materials, care instructions, package dimensions, and live availability.

No API key, no login, and no proxy configuration are needed.

### What it does

- **Search by keyword** on any supported storefront, with real offset paging through every result IKEA reports.
- **Browse a category** — all products, furniture, sofas, storage, lighting, kitchen, outdoor, and 25+ other departments, also fully paged.
- **Look up article numbers** directly, including dotted and spaced article formats.
- **Fetch product URLs** in any locale; the URL's country and language are respected.
- **Enrich from product pages** to add designer, materials, care instructions, package measurements, availability, and a full description.
- **Filter** by price range, minimum rating, in-stock status, sale status, merchandising badges, or a keyword, and **sort** by relevance, price, rating, popularity, name, or dimensions.

### Input

| Field | Type | Default | Description |
|---|---|---|---|
| `mode` | string | `search` | `search`, `byCategory`, `byProductIds`, or `byUrls` |
| `searchQuery` | string | `bookcase` | Keyword for `search` mode, e.g. `sofa`, `BILLY`, `kallax` |
| `productIds` | array | `[]` | IKEA article numbers for `byProductIds`, e.g. `20522046` |
| `productUrls` | array | `[]` | Full product-page URLs for `byUrls`, one per line |
| `category` | string | – | Category key for `byCategory` |
| `country` | string | `us` | Market storefront, ISO 3166-1 alpha-2 (50 markets) |
| `language` | string | `en` | Storefront language; falls back to the market's primary language |
| `sortBy` | string | `RELEVANCE` | `PRICE_LOW_TO_HIGH`, `PRICE_HIGH_TO_LOW`, `NEWEST`, `RATING`, `MOST_POPULAR`, `NAME_ASCENDING`, `WIDTH`, `HEIGHT`, or `DEPTH` |
| `minPrice` | integer | – | Exclude products priced below this value in the storefront currency |
| `maxPrice` | integer | – | Exclude products priced above this value in the storefront currency |
| `minRating` | integer | – | Exclude products rated below 0-5; unrated products are kept |
| `inStockOnly` | boolean | `false` | Exclude products marked out of stock or not sellable online |
| `containsKeyword` | string | – | Keep only records whose text fields contain this substring |
| `onSaleOnly` | boolean | `false` | Keep only products with a previous price or a discount |
| `onlyNew` | boolean | `false` | Keep only products IKEA tags as new |
| `onlyBestsellers` | boolean | `false` | Keep only products IKEA tags as best sellers |
| `onlyLastChance` | boolean | `false` | Keep only products IKEA tags as last chance |
| `enrichFromPDP` | boolean | `false` | Fetch each product page for designer, materials, care, packages, and availability |
| `maxItems` | integer | `50` | Maximum records to emit, 1-10,000 |

### Output per product

- `productId`, `sku`, `mpn` — IKEA article identifiers
- `name`, `typeName`, `itemType`, `filterClass` — product naming and classification
- `description` — full product-page description when the page is fetched
- `brand` — always `IKEA`
- `color`, `colors` — selected finish and every advertised finish
- `designer` — product designer from the product page
- `materials` — list of `{ part, material }` entries
- `careInstructions` — list of care steps
- `goodToKnow` — list of `{ name, text }` assembly and usage notes
- `measurementsText` — compact measurement summary from listings
- `measurements` — structured `height`, `width`, `depth`, and `weight` when published
- `packageMeasurements` — list of package `width`, `length`, `height`, `weight`, and `volume`
- `numberOfPackages`, `minOrderQuantity`
- `price` — `{ amount, currency }` plus `previousAmount`, `discountPercentage`, `discountAmount`, and `previousPriceLabel` when on sale
- `rating`, `ratingCount`
- `availability` — online-sellable flag, delivery stock status, click-and-collect eligibility, discontinued/sold-out flags, and check time
- `stockStatus` — schema.org-style stock label from the product page
- `onlineSellable` — listing-level sellability flag
- `mainImageUrl`, `imageUrls`, `mainImageAlt`, `contextualImageUrl`, `contextualImageAlt`
- `productUrl`
- `categoryPath` — breadcrumb names from the listing or product page
- `badges`, `badgeText`, `lastChanceText`, `isBestseller`, `isNewArrival`, `isLastChance`
- `quickFacts` — type/value facts such as material or firmness
- `variants` — every color/size sibling with its own ID, URL, image, rating, price, and sellability
- `variantCount`
- `country`, `language` — storefront used for this record
- `scrapedAt` — ISO 8601 collection time

Empty fields are omitted at every depth; the actor never sends `null`, `""`, `[]`, or `{}`.

### Examples

Search with a price window and rating floor:

```json
{
  "mode": "search",
  "searchQuery": "sofa",
  "country": "us",
  "minPrice": 300,
  "maxPrice": 900,
  "minRating": 4,
  "maxItems": 100
}
```

Browse a category with full detail enrichment:

```json
{
  "mode": "byCategory",
  "category": "st002",
  "country": "de",
  "language": "de",
  "sortBy": "PRICE_LOW_TO_HIGH",
  "enrichFromPDP": true,
  "maxItems": 50
}
```

Fetch exact articles:

```json
{
  "mode": "byProductIds",
  "productIds": ["20522046", "00263850"],
  "country": "gb"
}
```

Fetch product URLs in their own locales:

```json
{
  "mode": "byUrls",
  "productUrls": [
    "https://www.ikea.com/us/en/p/billy-bookcase-white-20522046/",
    "https://www.ikea.com/de/de/p/billy-buecherregal-braun-nussbaumnachbildung-50508652/"
  ]
}
```

### Pagination and limits

Search and category results are requested page by page from IKEA's own listing API, using its `window.offset` cursor and `metadata.max` total. Requests stop when `maxItems` is reached, IKEA's own count is exhausted, or a page comes back empty. The practical ceiling is what the storefront reports for your query; broad terms such as `storage` can exceed 2,000 products and full catalog browse (`category: "products"`) exceeds 9,000 in the US. Results are de-duplicated by article number.

### Legacy inputs

Saved tasks that still send older key names keep working: `query`/`search` → `searchQuery`, `ids`/`productId`/`articleNumbers` → `productIds`, `urls`/`startUrls` → `productUrls`, `categoryId`/`categorySlug` → `category`, `countryCode` → `country`, `languageCode` → `language`, `sort`/`sortOrder` → `sortBy`, `limit`/`maxResults` → `maxItems`, `inStock`/`availableOnly` → `inStockOnly`, `keyword` → `containsKeyword`, and `enrich`/`fetchDetails` → `enrichFromPDP`. These aliases are mapped in code and are not visible schema fields.

### FAQ

**Do I need a proxy or an API key?** No. The actor calls IKEA's public storefront endpoints directly. If IKEA ever rate-limits a run, the actor retries with backoff.

**Why do some records have more fields?** Designer, materials, care instructions, package measurements, and availability come from a product's own page. They are always present for `byProductIds` and `byUrls`, and added to `search`/`byCategory` results only when `enrichFromPDP` is true.

**Why is availability missing on listing records?** Listing pages do not publish it. Enable `enrichFromPDP` to fetch each product page and fill it.

**What happens with an invalid language?** It falls back to the selected market's primary language rather than failing.

**Why did my category key stop working?** IKEA occasionally renames taxonomy keys. The schema lists the current top-level keys; a full `/cat/` URL is also accepted and normalized.

**Why are prices different across countries?** Each record uses its storefront's local price and currency. Run the same products across countries to compare.

**Is this affiliated with IKEA?** No. It reads publicly accessible storefront data and is not affiliated with, endorsed by, or sponsored by Inter IKEA Systems or any IKEA franchisee.

### Limitations

- Search returns only what a storefront reports for that query; there is no cross-market search in one request.
- A few category keys are market-specific; the schema lists keys that are valid in the major markets.
- `enrichFromPDP` adds one request per product, so very large enriched runs take longer.
- Availability is a point-in-time snapshot from the product page; it is not a live inventory reservation.

### Data source

IKEA's public storefront listing API and product pages. The actor is designed for catalog research, price monitoring, and assortment analysis.

# Actor input Schema

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

How to find products.

## `searchQuery` (type: `string`):

Free-text query, e.g. `bookcase`, `BILLY`, `sofa`, `kallax`. Used by Search by keyword. Results are paged from IKEA's own listing API.

## `productIds` (type: `array`):

IKEA article numbers (e.g. `20522046`). Dots, spaces, and full product URLs are accepted and normalized. One per line.

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

Full IKEA product-page URLs, e.g. `https://www.ikea.com/us/en/p/billy-bookcase-white-20522046/`. One per line. The country and language in each URL are used for that product.

## `category` (type: `string`):

IKEA category key. Required by Browse a category; ignored by the other modes.

## `country` (type: `string`):

IKEA market storefront (ISO 3166-1 alpha-2 code). Prices use this market's currency.

## `language` (type: `string`):

Storefront language; falls back to the country's primary language when the combination is not served.

## `sortBy` (type: `string`):

Sort order applied by IKEA's listing API.

## `minPrice` (type: `integer`):

Exclude products priced below this value (storefront currency). Products with no listed price are excluded when this filter is set.

## `maxPrice` (type: `integer`):

Exclude products priced above this value (storefront currency). Products with no listed price are excluded when this filter is set.

## `minRating` (type: `integer`):

Exclude products rated below this value (0-5). Unrated products are kept.

## `inStockOnly` (type: `boolean`):

Exclude products that IKEA marks as out of stock or not sellable online.

## `containsKeyword` (type: `string`):

Only keep products whose name, description, color, material, or category path contains this text (case-insensitive).

## `enrichFromPDP` (type: `boolean`):

Fetch each product's own page for designer, materials, care instructions, package measurements, availability, and a full description. Slower and more request-heavy.

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

Maximum number of product records to emit (1-10000). Paged requests stop at IKEA's own result count.

## `onSaleOnly` (type: `boolean`):

Keep only products with a previous price or a discount on the selected storefront.

## `onlyNew` (type: `boolean`):

Keep only products IKEA tags as new.

## `onlyBestsellers` (type: `boolean`):

Keep only products IKEA tags as best sellers.

## `onlyLastChance` (type: `boolean`):

Keep only products IKEA tags as last chance (being phased out).

## Actor input object example

```json
{
  "mode": "search",
  "searchQuery": "bookcase",
  "productIds": [],
  "productUrls": [],
  "category": "",
  "country": "us",
  "language": "en",
  "sortBy": "RELEVANCE",
  "inStockOnly": false,
  "enrichFromPDP": false,
  "maxItems": 3,
  "onSaleOnly": false,
  "onlyNew": false,
  "onlyBestsellers": false,
  "onlyLastChance": false
}
```

# Actor output Schema

## `products` (type: `string`):

Dataset containing every scraped IKEA product, one row per article.

# 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": "search",
    "searchQuery": "bookcase",
    "productIds": [],
    "productUrls": [],
    "country": "us",
    "language": "en",
    "sortBy": "RELEVANCE",
    "inStockOnly": false,
    "enrichFromPDP": false,
    "maxItems": 3,
    "onSaleOnly": false,
    "onlyNew": false,
    "onlyBestsellers": false,
    "onlyLastChance": false
};

// Run the Actor and wait for it to finish
const run = await client.actor("vexaloom-labs/ikea-multi-country-product-catalog-extractor").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": "search",
    "searchQuery": "bookcase",
    "productIds": [],
    "productUrls": [],
    "country": "us",
    "language": "en",
    "sortBy": "RELEVANCE",
    "inStockOnly": False,
    "enrichFromPDP": False,
    "maxItems": 3,
    "onSaleOnly": False,
    "onlyNew": False,
    "onlyBestsellers": False,
    "onlyLastChance": False,
}

# Run the Actor and wait for it to finish
run = client.actor("vexaloom-labs/ikea-multi-country-product-catalog-extractor").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": "search",
  "searchQuery": "bookcase",
  "productIds": [],
  "productUrls": [],
  "country": "us",
  "language": "en",
  "sortBy": "RELEVANCE",
  "inStockOnly": false,
  "enrichFromPDP": false,
  "maxItems": 3,
  "onSaleOnly": false,
  "onlyNew": false,
  "onlyBestsellers": false,
  "onlyLastChance": false
}' |
apify call vexaloom-labs/ikea-multi-country-product-catalog-extractor --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,vexaloom-labs/ikea-multi-country-product-catalog-extractor"
        }
    }
}
```

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/MCDfkGMZMsTofr18A/builds/nzIpuoawuuggFBZGd/openapi.json
