IKEA Scraper Lite
Pricing
from $1.00 / 1,000 results
IKEA Scraper Lite
Scrape IKEA products across 50+ markets. Extract names, prices, currencies, ratings, full reviews, colours, dimensions, images, categories, variants, badges and store availability. Supports search, category, item-number and URL inputs with filters and sorting.
Pricing
from $1.00 / 1,000 results
Rating
0.0
(0)
Developer
Abot API
Maintained by CommunityActor stats
1
Bookmarked
2
Total users
1
Monthly active users
3 days ago
Last modified
Categories
Security
Limited permissionsShare
IKEA Scraper Lite: Products, Prices, Availability and Reviews
IKEA Scraper Lite turns any of IKEA's country storefronts into structured product data. Search by keyword, list a category, look up exact item numbers, or paste product, category, search or room/department links, and get back one flat record per product with pricing, ratings, colours, measurements, images, category path, variants and markdown status. Turn on detail enrichment for the full marketing description and individual customer reviews, and turn on availability for national and per-store stock, then export to JSON, CSV or Excel, or pull the results straight into your app through the API.
Why This Scraper?
- Four ways to find products. Search by keyword, list a category, look up exact item numbers, or paste product, category, search and room/department links.
- 50+ country storefronts. One Market and language field picks the country, currency and product range; a pasted link in URL mode carries its own market instead.
- Specials and pricing built in. Every product already carries its markdown info, was-price, savings amount, discount percent and IKEA's own specials label, at no extra request cost.
- Room and department links just work. A collection page like a living room hub is expanded into the real categories it lists, and each one is scraped, so you don't have to hand-build category links.
- Detail enrichment on demand. Attach the full description and individual customer reviews (author, date, title, text, rating) per product. It consumes additional platform usage only for the products it reads; there is no enrichment event fee.
- Availability on demand. Attach national stock and per-store stock, click and collect and cash and carry status per product. Pick the stores you care about (for example IKEA Logan) and get one stock entry per store, even when it holds none.
- Built for monitoring. Incremental mode returns only new and changed products on recurring runs, and a fully blocked search, category, or listing/room link fails loudly instead of returning an empty dataset. A blocked item-number lookup or a blocked pasted product link is not detected this way; see the FAQ below.
Use Cases
- Price and markdown tracking: monitor a category or a list of item numbers for price drops, IKEA Family price markdowns and last-chance clearance.
- Catalogue and range building: pull a full category or room sweep into your own product database or marketplace feed.
- Review and ratings research: collect individual review text and ratings for sentiment analysis or competitor benchmarking.
- Stock and fulfilment checks: confirm national and per-store availability, click and collect and home delivery status before promoting a product.
- Cross-market comparison: compare price, currency and range differences for the same item numbers across several country storefronts.
Data You Get
Sample shape: values are illustrative placeholders, not from a live record.
| Field | Example |
|---|---|
kind | "product" |
id / itemNo | "00099999" |
name / typeName | "SAMPLE" / "Shelving unit" |
url | "https://www.ikea.com/us/en/p/sample-shelving-unit-white-00099999/" |
price / currency / priceFormatted | 148.0 / "USD" / "$148.00" |
onSale / isOnSpecial | true / true |
wasPrice / discountPercent / specialsLabel | 179.0 / 17.3 / "IKEA Family price" (null when the product carries no markdown) |
ratingValue / ratingCount | 4.4 / 812 |
color / colors | "slate gray" / [{ "name": "slate gray", "id": "90512", "hex": "5b6470" }] |
width / depth / height / measurementUnit | 27.5 / 9.75 / 70.25 / "in" |
material | "Particleboard, foil, plastic edging" |
categoryPath | ["Storage & organization", "Shelving units"] |
ancestorCategories | [{ "name": "Storage & organization", "key": "st001", "url": "https://www.ikea.com/us/en/cat/storage-organization-st001/" }], outermost first. key is IKEA's category id (usable in Category ids mode); the link opens that category. |
badge / isBestseller | "Best seller" / true |
lastChance / onlineSellable | false / true |
mainImage / mainImageAlt / images | product photo URL(s) on ikea.com, and the photo's alt text |
contextualImageUrl / contextualImageAlt | the in-room photo and its alt text (null when IKEA shows none) |
tag / tagText / features | IKEA's listing tag and its label, e.g. "FAMILY_PRICE" / "IKEA Family price", and the feature list when the listing carries one (null when none) |
numberOfVariants / variants | 3 / sibling colour or finish options, each with its own price and image |
market / language | "us" / "en" |
description | full marketing copy (when detail enrichment is on) |
reviewsCollected / reviews | 1 / each review's author, date, title, body and rating (when detail enrichment is on). Switch the dataset's Reviews view (next to Overview) to see one row per review instead of a nested array. |
availability | national block (click and collect and home delivery are national signals, not per store) plus per-store stock (when availability is on) |
changeType / changedFields | "UPDATED" / ["price"] (incremental mode only) |
previousPrice / priceChanges / variantChanges / storeChanges | the price before this run, and the old and new value of each moved price, variant and store field (compact update rows only) |
There is only one dataset record kind: a product. Reviews and availability are nested arrays/objects under it rather than separate rows, and firstSeenAt / lastSeenAt are added alongside changeType when incremental mode is on. With Compact update rows on, UPDATED and EXPIRED rows are short (see Resume and incremental mode below). The dataset's Changes (incremental) view shows changeType, changedFields, previousPrice, priceChanges, variantChanges, storeChanges and otherChanges side by side. Each run also leaves a RUN_SUMMARY record in its key-value store (linked from the Output tab, never a dataset row, never billed); see "Reading the results" below.
Each search or category walked in a run also leaves one AVAILABLE_FILTERS record in the run's key-value store (linked from the run's Output tab). It is not a dataset row and is never billed. It holds one entry per walked search or category: the scope (type, value, market, sort and the facets that were applied), the walked category (name, key, link, picture), totalAvailable (how many products IKEA lists there) and availableFilters, the listing's own filter panel as flat rows of facet, facetParam, valueId, title, count and acceptedByFiltersInput (true when that facet can be passed in the Facet filters input). It is read from the page the walk already fetched, so it costs no extra request.
How to Use
- Pick a mode:
search(keywords),productIds(exact item numbers),url(paste product, category, search or room/department links, or a bare letter-prefixed category id), orcategory(legacy category-only mode, superseded byurl). - Choose your Market and language (ignored in URL mode, since each link carries its own market).
- Fill in the fields for that mode, apply any filters, and switch on detail enrichment or availability if you need them.
- In
urlmode, leaving URLs untouched scrapes the built-in sample: eleven links that each mean something different (a product in two markets, a numeric and a letter-prefixed category, a filtered and sorted category, both search link forms with and without a sort, a room, a department hub, a bare category id, and an offers hub that is logged and skipped). Replace them with your own links for a real run. - Set Max products to control run size and cost, then click Start.
Search for a category of products:
{"mode": "search","market": "us/en","searchTerms": ["bookcase"],"sortBy": "PRICE_LOW_TO_HIGH","maxItems": 50,"fetchDetails": true,"maxReviews": 10,"includeAvailability": true}
List a category, no detail enrichment (cheaper, faster):
{"mode": "category","market": "us/en","categories": ["https://www.ikea.com/us/en/cat/bookcases-10382/"],"maxItems": 100,"fetchDetails": false}
Look up exact item numbers:
{"mode": "productIds","market": "us/en","productIds": ["20522046"],"fetchDetails": true,"includeAvailability": true}
Monitor a search link for changes on a schedule:
{"mode": "url","urls": ["https://www.ikea.com/us/en/search/products/?q=bookcase"],"incrementalMode": true,"emitExpired": true,"maxItems": 100}
Run it from your code
Python:
from apify_client import ApifyClientclient = ApifyClient("<YOUR_APIFY_TOKEN>")run = client.actor("abotapi/ikea-scraper-lite").call(run_input={"mode": "search", "market": "us/en", "searchTerms": ["bookcase"],})for product in client.dataset(run["defaultDatasetId"]).iterate_items():print(product["name"], product["price"], product["currency"])
JavaScript:
import { ApifyClient } from 'apify-client';const client = new ApifyClient({ token: '<YOUR_APIFY_TOKEN>' });const run = await client.actor('abotapi/ikea-scraper-lite').call({mode: 'search', market: 'us/en', searchTerms: ['bookcase'],});const { items } = await client.dataset(run.defaultDatasetId).listItems();
Or connect it to Make, Zapier, n8n, Google Sheets or webhooks from the Integrations tab.
Markets, and mixing them in one run
Search, category and productIds modes all use one Market and language value for the whole run. In URL mode, each pasted link is read on its own: a link whose path starts with a valid two-letter country and two-letter language (for example us/en or de/de) uses that market instead of the field above, so one URL-mode run can genuinely mix several country storefronts. Keep in mind that filters like Min price and Max price compare against each record's own local currency number, so the same numeric threshold means a different real amount in a run that mixes, say, US dollars and Japanese yen.
Room and department links
A collection page such as a "rooms" or department hub is not itself a category, so the actor reads the category list that page publishes and scrapes each one. The run log names every category it walked, so you can see exactly what was covered. Products that a room page only features without listing a category for it are not included, so this is a broad sweep of the room rather than a pixel-perfect copy of the page. A page that publishes no category at all (an offers, new-arrivals or campaigns hub, or the bare room index) is reported in the log and skipped instead of being guessed as a keyword search.
Pasted links keep their own filters and sort
A search or category link pasted in URL mode keeps the filters= and sort= it carries, for example .../cat/bookcases-10382/?filters=f-colors%3A10156&sort=PRICE_LOW_TO_HIGH. Facets that match a Facet filters key (colors, materials, type, price, category) and any supported sort apply to that link only. When the same facet or the sort is also set in the input, the link wins for that link; facets the link does not mention still come from the input, and a link with no filters or sort simply uses the input's. Facets IKEA offers beyond those five keys are not applied and are named in the log. A page number in a link (?page=2) is not used: every link is walked from its first page and Max products sets how far.
Filters, sort and specials scope
The optional Facet filters object (filters) takes the site's own facet ids for colors, materials, type, price (e.g. PRICE_0_10000) and category, comma-separated, in search and category listings only; unknown keys are ignored. Min price, Max price, Min rating, In stock only, Contains keyword and the Specials facet all apply in search mode, category mode, and to a search or category link pasted in URL mode, exactly the same way. They are not applied to Product item numbers mode, or to a single product link pasted in URL mode: those always return that exact item, unfiltered.
Resume and incremental mode
- Resume (
resumeFromRunId) skips product ids already collected by a previous run or dataset. For a fixed list of item numbers or pasted URLs, each id resolves on its own, so this always works cleanly. For a repeated category or search walk, the walk pages past the already-collected products as well as collecting the new ones you asked for, up to the site's own 400-product listing ceiling. Products ranked beyond that ceiling are not reachable by any run; narrow the search or change Sort by to reach them. - Incremental mode (
incrementalMode) is for running the same search, category, item-number list or URL set again and again (daily, weekly) and getting only the differences. Each product is classifiedNEW,UPDATED(withchangedFields),UNCHANGED(suppressed and not billed unlessemitUnchangedis on),REAPPEAREDorEXPIRED. For search, category and listing/room links, a product is only ever marked absent, and can only ever come back asREAPPEAREDlater, after a run that hademitExpiredon and completed a full, uncapped, non-Resume scan with no page refused or unreadable. The same holds forproductIdsmode and pasted single product links: a lookup that was refused or never answered counts as an unreadable scan, so those products are never markedEXPIRED(nor billed as such) on that run.stateKeynames or shares one monitoring campaign's stored state; leave it empty to let the actor derive one from the market, mode and filters automatically. If another field in your setup turns out to be noisy (changes between runs without the product itself really changing; IKEA'sisBreathTakinghot-deal flag orpriceValidFrom/priceValidToare candidates), list it inignoreFieldsForChangesto exclude it from triggeringUPDATED. Fields added to the record in later versions (such asancestorCategoriesorcontextualImageUrl) never count as a change on their own: a product saved by an earlier version staysUNCHANGEDwhen only those fields are new, and its saved copy is brought up to date once. EXPIRED rows count toward Max products: once the run's remaining budget is used, the rest stay tracked as present and are returned on the next complete run (Max products0means no cap). A saved url-mode campaign whose links carryfilters=orsort=starts a fresh baseline once, because those links are now scraped filtered and sorted as the page shows them. Adding a store tostoreIdson a saved campaign returns each tracked product once asUPDATEDwith that store's stock (old: nullin compact rows). A store whose stock a product has already returned, under any earlier selection, is never returned again just because it is selected again: removing, re-adding, reordering or alternating stores returns nothing by itself. A real stock change at a store that stays selected from one run to the next is reported asUPDATEDwithavailabilityinchangedFields, also in a run that adds or removes other stores. A change at a store that was not selected in the previous run is not reported when it is selected again (it was not being watched); only its changes from then on are. A selected store counts as returned once a stock answer for it has been returned, including an entry withquantitynull andinStockfalse (the store does not list the item).storeIdsis not part of the automatic state key, so two Tasks with the same search and differentstoreIdsshare one saved state; give them different State keys to keep them apart. When you start incremental mode together with Resume, the products that came from the resume source are tracked without their data (this run never read them). The first later run that reads them fills their saved copy silently: they are not returned and not billed, even withemitUnchangedon, and a real change after that is reported asUPDATEDas usual. A change that happened between the resume source and that first run is therefore not reported. With incremental mode off, output is exactly one row per product, with nochangeTypefield at all. - What counts as a change (
changeScope, incremental mode only).all(default) keeps the behaviour above: any change to the product record returnsUPDATED.priceAndStockreturnsUPDATEDonly for price changes (price,currency,wasPrice,savingsAmount,discountPercent,isOnSpecial,onSale, and each colour/size variant's ownprice) and availability changes (onlineSellable, the product page'sofferAvailability, the national click and collect and home delivery flags, and the in-stock, click and collect and cash and carry flags of each store chosen instoreIds); names, descriptions, images, badges, ratings and other details never do.sellingPriceAndStockworks likepriceAndStock, but a price change counts only when the selling price (price, for the product or a variant) or its currency moves; was prices, savings and special flags never return a row on their own. In both narrow options a store's unit count, its stock level label (messageType) and its aisle and bin never return a row on their own, and neither does the unfiltered top-stores list you get withoutstoreIds. IKEA's listing gives each variant its own item number, price and currency but no stock of its own: to watch a variant's stock, add its item number to Product item numbers mode. ChangingchangeScopeon a search with nostateKeycontinues from that search's saved baseline, so unchanged products are not returned or billed again. Each setting keeps its own saved baseline, though: switching back to a setting you already used before resumes that setting's older baseline, so a change that was already returned under the other setting since then is returned (and billed) once more. To avoid that, keep one setting per search, or set astateKey, which keeps one baseline whatever the setting. - Compact update rows (
compactUpdates, incremental mode only).UPDATEDandEXPIREDrows are short:id,itemNo,url,name,price,currency,onlineSellable,offerAvailabilityand theavailabilityblock, pluspreviousPrice(the price before this run, empty when it did not move),priceChanges(old and new of each moved price or availability field),variantChanges(one entry per colour/size variant that wasADDED,REMOVEDorCHANGED, with old and new price) andstoreChanges(one entry per chosen store whose availability moved, with old and newquantity,inStock,clickAndCollect,cashAndCarryandmessageType, plus onenational: trueentry when the national click and collect or home delivery flag moved; withoutstoreIds, the stores of the unfiltered top-stores list by store code; in All fields, alsoaisle,binandinRange; a store or national block never known before is listed withold: null) andotherChanges(old and new of every other changed field no section above covers, for examplenameorratingCount; the review list is given as its number of reviews). Every compactUPDATEDrow carries at least one of these sections.NEWrows always carry the complete record. Compact rows carry no review text, and they only report fields listed inchangedFields: a field you list inignoreFieldsForChanges, or one outside the chosenchangeScope, never appears inpriceChanges,variantChangesorstoreChanges(ignoringpricealso leavespreviousPriceempty). A compact row is billed exactly like the full row it replaces. - Expire after N missed full scans (
expireAfterMissedScans, withemitExpired). A product is markedEXPIREDonly after it has been missing from this many complete scans in a row (default 1), so a product that briefly drops out of a listing is not reported as gone. A product that comes back resets the count. A capped, resumed or partly unreadable scan never counts as a miss. - Start from an existing dataset (
baselineDatasetId, optional, incremental mode only): if you already scraped the catalogue with this Actor, pick that run's dataset in the input form and your first incremental run returns only products that changed since then, instead of the whole catalogue again. Pick the dataset unedited: it must be this Actor's own product output (a CSV or Excel export, renamed columns or another tool's data is refused, and the run stops before scraping with a message saying why). Compact rows andEXPIREDrows in it are skipped; each product is seeded from its full row. It is only used while the state key has no saved state; once a run has completed it is ignored. Products in the dataset that are unchanged are not returned or billed; changes since the dataset was made come back asUPDATED, new products asNEW, and withemitExpiredon, products no longer on the site asEXPIRED. It cannot be combined with Resume from a previous run, and it must be selected from the list (a pasted id may not be readable). - Coming from the IKEA Scraper? (
earlierHistoryStoreId, optional, incremental mode only). If you tracked the same State key (or the same search settings) there, pick that scraper's history store (ikea-com-incremental-state) in "Continue from the IKEA Scraper's history" and the first Lite run continues from that saved history, so you are not given the whole catalogue as NEW again. Your earlier history is only read, never changed, and nothing needs to be copied or run. The run stops with a message, before anything is scraped, when the picked store is not that scraper's history store, does not hold IKEA products, cannot be read, is too large, or holds saved history for this State key or these exact search settings in an unexpected format (nothing is partly imported). Without a State key, the history the IKEA Scraper saved for the same search under another What counts as a change setting is used too, the most recently saved one winning; history under such another setting that cannot be read is skipped with a warning while another one can be used, and stops the run only when none can. When this Actor has itself saved newer history for the same search under another setting, that newer history is used instead. When nothing is saved anywhere for this search, the run says so and starts fresh. It is ignored once the key already tracks products, and when Start from an existing dataset or Resume is set. - Missing products and unreadable checks. A product that is no longer found is only marked
EXPIREDwhenemitExpiredis on, afterexpireAfterMissedScanscomplete scans in a row; a scan that hit Max products, used Resume, or could not read a page or a product lookup never marks anythingEXPIRED. A stock check that could not be read this run is never reported as a change: the row (if anything else moved) showsavailabilityempty for this run,storeChangesstays empty, and the last stock that was read is kept, so a stock change that happened in the meantime is reported on the next run that can read it. A product page that could not be read (description, material, review and rating fields,offerAvailability) is handled the same way: its fields are not compared that run, the last values that were read are kept, so a change made while the page was unreadable (for exampleofferAvailabilitygoing fromInStocktoOutOfStock) is reported on the next run that can read it, and that product's comparison simply skips those fields. This holds in every What counts as a change option, for full and compact rows, and however many runs in a row the page or stock check stays unreadable. Turning Fetch full details off is not an outage: those fields are then simply not compared. A stock answer or product page that has never been read for a tracked product (unreadable the first time the product was seen) is returned once asUPDATEDthe first time it is read (old: nullin compact rows), so the first known stock is never lost; after that only real changes come back. A product whose stock lookup got no answer during the walk is read once more at the end of the run (up to 25 products per run), so it can appear later in the dataset than its listing position. When the stock service stops answering, the run reads each remaining product's stock once instead of retrying, and the run summary says how many could not be read. - What the availability fields mean.
availabilityempty (null): stock was not read this run, or the stock service does not know the item. A store entry withquantity0: the store confirmed zero stock. A store entry withquantitynull andinStockfalse: the store does not list the item.changedFieldsonly names the fields that changed; the old and new values are in the compact sections (priceChanges,variantChanges,storeChanges,otherChanges). Combination products (item numbers starting withs, such as sofas and wardrobe combinations) get their stock like any other product. - Runs that reach their time limit. A run stops looking up products about 75 seconds before its time limit, saves what it delivered, and says so in its status message; the products it did not reach are checked on the next run (no
EXPIREDrows from such a run). Incremental state is also saved during the run (after every 200 returned products, at most every 30 seconds, and less often for very large searches so saving stays a small part of the run), so a run stopped without warning returns again on its next run only the products it had returned since its last save. - Very large monitoring campaigns. The saved state of one search has a size limit. When a search grows past it, the run's status message says so and the state is not saved, so the next run returns those products again. Split such a search into smaller ones (by category, filter or market), each with its own State key.
Send results into your apps (MCP connectors)
Optionally pipe the scraped results into the apps you already use, via Model Context Protocol (MCP) connectors. This is an extra delivery step after the scrape: the Apify dataset is never changed.
What gets written to the connector: a condensed, human-readable summary of each product, not the full JSON. Each product becomes one entry with a title and its key fields flattened to plain text. The complete record always stays in the Apify dataset.
- Authorize a connector once under Apify โ Settings โ Integrations (Notion, Linear, Airtable, or Apify).
- Select it in the "Pipe results into your apps" input field. (If the picker is empty, you haven't authorized a connector yet.)
- For Notion, also set
notionParentPageUrlto the page where products should be created.
The connection is mediated by Apify's MCP proxy, so this actor never sees your third-party credentials. Leave the field empty to skip.
Input Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
mode | string | search | search, category, productIds, or url. |
market | string | us/en | The IKEA market and language. Ignored per link in URL mode. |
searchTerms | array | prefill ["bookcase"] | Keywords to search (search mode). |
sortBy | string | RELEVANCE | Order of results in a listing (search, category, or a listing link in URL mode). Includes Name Z-A and Customer rating. |
filters | object | {} | Facet ids per key: colors, materials, type, price, category. |
categories | array | prefill sample category link | Category listing URLs or bare ids (category mode, legacy; url mode now covers this too). |
productIds | array | prefill sample item number | Exact IKEA item numbers (productIds mode). |
urls | array | one example of every supported link shape (11 links) | Product, category or search links from ikea.com, or a bare letter-prefixed category id (url mode). A search or category link keeps its own filters and sort. |
minPrice | integer | (none) | Only keep products at or above this price. |
maxPrice | integer | (none) | Only keep products at or below this price. |
minRating | integer | (none) | Only keep products with an average rating at or above this value (1-5). |
inStockOnly | boolean | false | Keep only products currently sellable online. |
containsKeyword | string | (none) | Only keep products whose name, type or description contains this text. |
specialsCategory | string | none | Narrow a listing to family_price (IKEA Family price) or last_chance. |
fetchDetails | boolean | true | Attach the full description and individual customer reviews. |
maxReviews | integer | 10 | Cap on individual reviews captured per product (0 disables review text), at most 100000. The default stays at 10 so existing incremental campaigns keep their saved state. Reviews consume additional platform usage; there is no enrichment event fee. The actual limit is however many reviews IKEA includes on the product page. |
includeAvailability | boolean | false | Attach national and per-store stock per product. |
storeIds | array | [] | Optional, needs includeAvailability. Numeric store ids or store names (919 or Logan for IKEA Logan; names are known for Australia). Returns exactly these stores, one entry each, also at quantity 0 or when the item is not offered there. An unknown name stops the run before anything is collected. |
deliveryPostcode | string | empty | Optional, market au/en only, needs includeAvailability. A 4-digit Australian postcode such as 4209. Adds the postcode's home-delivery yes/no to availability.national, separate from the store quantities. Not a delivery date or price; stock at a store does not prove delivery. null in the answer means not confirmed this run (never false). Another format or market, or includeAvailability off, stops the run before anything is collected. |
maxItems | integer | 20 | Total products to return across all inputs (the run cap). |
maxPages | integer | 0 | Optional safety cap on result pages walked per search or category (0 = no limit). |
proxy | object | { "useApifyProxy": true } | Connection settings. |
mcpConnectors | array | (none) | Optional: send a summary of each product to apps you authorized under Integrations. |
notionParentPageUrl | string | (none) | Notion connector only: page under which products are created. |
maxNotifyListings | integer | 50 | Cap on products written to each connector per run. |
readProductsAtOnce | integer | 4 | Product pages read in parallel (1-8) to finish faster. Output, order and charges are identical at any value; 1 reads one at a time. Reading drops back to one at a time automatically while the site pushes back. |
resumeFromRunId | string | (none) | Continue one interrupted run without re-collecting or re-charging what it already has. |
incrementalMode | boolean | false | Return only new and changed products on recurring runs. |
stateKey | string | (none) | Name or share one monitoring campaign's incremental-mode state. |
baselineDatasetId | string | (none) | Incremental mode only. Pick the dataset of an earlier run of this Actor to use as the starting point, so the first incremental run returns only changes. Not combinable with Resume. |
earlierHistoryStoreId | string | (none) | Incremental mode only. Pick the IKEA Scraper's saved-history store (ikea-com-incremental-state) so the first Lite run for a State key, or for the same search settings, continues from that history instead of returning everything as NEW. Read only; used only while the key has no saved state, never with baselineDatasetId or Resume. |
ignoreFieldsForChanges | array | [] | Extra field names excluded from UPDATED change detection, on top of the built-in handling (a description/review/availability fetch that simply didn't land never counts as a change). |
emitUnchanged | boolean | false | Also return (and bill) unchanged products, marked UNCHANGED. |
emitExpired | boolean | false | Also return (and bill) products no longer found, marked EXPIRED, once a run has fully scanned the tracked scope. EXPIRED rows count toward Max products; the rest stay tracked and come out on the next complete run (0 = no cap). |
changeScope | string | all | Incremental mode only. priceAndStock makes only price changes (including each variant's price) and availability changes (online sellable, national click and collect and home delivery, and in-stock, click and collect or cash and carry at the stores in storeIds) return UPDATED. sellingPriceAndStock counts a price change only when the selling price moves. Unit counts and stock level labels alone never return a row in either. Changing it on a search with no stateKey continues from the saved baseline. |
compactUpdates | boolean | false | Incremental mode only. UPDATED and EXPIRED rows are short, with previousPrice, priceChanges, variantChanges and storeChanges (old and new of each moved field). NEW rows always carry the complete record. Billed exactly like full rows. |
expireAfterMissedScans | integer | 1 | Incremental mode only, with emitExpired. A product is marked EXPIRED only after it has been missing from this many complete scans in a row (1 to 30). |
Store stock for chosen stores
With includeAvailability on and storeIds set (for example ["919"] for IKEA Logan), availability.stores holds exactly the stores you listed, one entry each and sorted by store code (the order you typed them in does not matter):
{"availability": {"storesWithStock": 1,"stores": [{"storeId": "919","storeName": "Logan","quantity": 176,"inStock": true,"clickAndCollect": true,"cashAndCarry": true,"messageType": "HIGH_IN_STOCK","aisle": "16","bin": "01","inRange": true}]}}
A store that does not offer the item (missing from the stock answer, or answered without a count) still gets an entry with quantity null, inStock false and inRange false; a store that reports a count of 0 shows quantity 0. aisle and bin are filled when the store publishes the in-store location. Delivery PRICE and delivery DATE are not available. availability.national says whether home delivery is offered in the country; set deliveryPostcode to get a postcode-specific yes/no instead (see Home delivery to a postcode).
Home delivery to a postcode
Set deliveryPostcode (a 4-digit Australian postcode such as 4209) to also answer "can IKEA home-deliver this product to that postcode" for every product, separately from the store quantities. It needs market au/en and Include store availability; any other setup, or a value that is not four digits, stops the run before anything is collected. It adds no stock reads in the normal case: the postcode rides on the stock read the run already makes. If IKEA does not recognise the postcode, the first such answer switches the postcode off for the rest of the run (delivery stays null, with one warning), so only the few reads already under way at that moment (no more than the number of products read at once, plus one) are repeated, once, without the postcode.
Recommended setup for "Logan store stock plus delivery to 4209" on a schedule:
{"mode": "productIds","market": "au/en","productIds": ["00099999", "10099999"],"includeAvailability": true,"storeIds": ["919"],"deliveryPostcode": "4209","incrementalMode": true,"changeScope": "priceAndStock","compactUpdates": true}
With category mode the same three availability fields work unchanged. In each row, availability.stores is the Logan stock as before, and availability.national becomes the postcode's answer:
Sample shape: values are illustrative placeholders, not from a live record.
"national": { "country": "AU", "clickCollect": true, "homeDelivery": true, "clickCollectInRange": true, "homeDeliveryInRange": true, "stockStatus": "HIGH_IN_STOCK", "deliveryPostcode": "4209" }
deliveryPostcode: the postcode the answer was read for. The key is only present when you set the input; without it thenationalblock is exactly as before (the country-wide delivery flag).homeDelivery/homeDeliveryInRange:truewhen IKEA offers home delivery to that postcode,falsewhen it does not. This is a yes/no, not a delivery date or a delivery price (neither is available). Stock at a store does not prove delivery, and delivery to a postcode does not prove store stock.nullmeans "not confirmed this run, keep your previous value", neverfalse. It appears when the answer could not be read (the stock read failed) or when IKEA does not recognise the postcode. For an unrecognised postcode that product is read again without it, so the store stock still arrives; all later products are read without the postcode, and one warning is logged per run.- In incremental mode a flip such as
truetofalseis an ordinary change ofavailability.national(UPDATED, and anational: trueentry instoreChangesof compact rows). Anullis never a change and never replaces the last known answer; the first known answer afternullis reported once withold: null. Setting, changing or clearing the postcode never returns the catalogue again: the answer saved for another postcode is not compared, the new answer and postcode simply become the saved baseline without a row, and later changes are compared against the answer for the same postcode. The postcode is not part of the automatic state key. RUN_SUMMARYgainsdeliveryReads(attempted,answered,unreadable) next tostockReads, only when the input is set.
IKEA does not publish an EAN barcode through this actor.
Daily price and store stock monitor
Schedule a daily run with this input (your item numbers, or search, category or product links in URL mode):
{"mode": "productIds","market": "au/en","productIds": ["00099999", "10099999"],"includeAvailability": true,"storeIds": ["919"],"incrementalMode": true,"changeScope": "sellingPriceAndStock","compactUpdates": true,"emitExpired": true,"expireAfterMissedScans": 3,"stateKey": "logan-daily","fetchDetails": false,"maxItems": 0}
The first run returns every product in full as NEW. Later runs return a product only when its selling price (or a colour/size variant's price) moved, or when it went in or out of stock, click and collect or cash and carry at IKEA Logan, or online sellability or national click and collect or home delivery changed. Unchanged products are not returned and not billed. A compact row looks like this:
Sample shape: values are illustrative placeholders, not from a live record.
{"kind": "product","id": "00099999","itemNo": "00099999","url": "https://www.ikea.com/au/en/p/sample-shelving-unit-white-00099999/","name": "SAMPLE","currency": "AUD","price": 129.0,"onlineSellable": true,"availability": {"national": { "country": "AU", "clickCollect": true, "homeDelivery": true, "clickCollectInRange": true, "homeDeliveryInRange": true, "stockStatus": "HIGH_IN_STOCK" },"storesWithStock": 0,"stores": [{ "storeId": "919", "storeName": "Logan", "quantity": 0, "inStock": false, "clickAndCollect": false, "cashAndCarry": false, "messageType": "OUT_OF_STOCK", "aisle": "16", "bin": "01", "inRange": true }]},"changeType": "UPDATED","changedFields": ["availability", "price"],"firstSeenAt": "2026-11-01T00:00:00Z","lastSeenAt": "2026-11-03T00:00:00Z","previousPrice": 149.0,"priceChanges": { "price": { "old": 149.0, "new": 129.0 } },"variantChanges": null,"storeChanges": [{"storeId": "919","storeName": "Logan","national": false,"country": null,"changes": {"quantity": { "old": 12, "new": 0 },"inStock": { "old": true, "new": false },"clickAndCollect": { "old": true, "new": false },"cashAndCarry": { "old": true, "new": false },"messageType": { "old": "HIGH_IN_STOCK", "new": "OUT_OF_STOCK" }}}],"otherChanges": null}
With expireAfterMissedScans at 3, an item that IKEA stops listing comes back once as a short EXPIRED row after three daily runs in a row without it. Keep fetchDetails on if you also want the product page's own stock status (offerAvailability) compared; it consumes additional platform usage, with no enrichment event fee.
Output Example
Sample shape: values are illustrative placeholders, not from a live record.
{"kind": "product","id": "00099999","itemNo": "00099999","itemType": "ART","name": "SAMPLE","typeName": "Shelving unit","description": "A simple sample shelving unit with adjustable shelves.","url": "https://www.ikea.com/us/en/p/sample-shelving-unit-white-00099999/","price": 148.0,"currency": "USD","priceFormatted": "$148.00","onSale": true,"isOnSpecial": true,"wasPrice": 179.0,"wasPriceFormatted": "$179.00","savingsAmount": 31.0,"discountPercent": 17.3,"specialsLabel": "IKEA Family price","ratingValue": 4.4,"ratingCount": 812,"averageRating": 4.4,"reviewCount": 812,"color": "slate gray","colors": [{ "name": "slate gray", "id": "90512", "hex": "5b6470" }],"measurementText": "27 1/2x9 3/4x70 1/4 \"","width": 27.5,"depth": 9.75,"height": 70.25,"measurementUnit": "in","material": "Particleboard, foil, plastic edging","categoryPath": ["Storage & organization", "Shelving units"],"ancestorCategories": [{ "name": "Storage & organization", "key": "st001", "url": "https://www.ikea.com/us/en/cat/storage-organization-st001/" },{ "name": "Shelving units", "key": "st002", "url": "https://www.ikea.com/us/en/cat/shelving-units-st002/" }],"badge": "Best seller","isBestseller": true,"onlineSellable": true,"mainImage": "https://www.ikea.com/us/en/images/products/sample__0000001.jpg","mainImageAlt": "SAMPLE Shelving unit, slate gray","contextualImageUrl": "https://www.ikea.com/us/en/images/products/sample__0000002.jpg","contextualImageAlt": "Living room with the SAMPLE shelving unit","tag": "FAMILY_PRICE","tagText": "IKEA Family price","images": ["https://www.ikea.com/us/en/images/products/sample__0000001.jpg"],"numberOfVariants": 3,"variants": [{ "id": "00099998", "name": "SAMPLE", "color": "black", "price": 158.0, "currency": "USD" }],"market": "us","language": "en","reviewsCollected": 1,"reviews": [{"author": "Jordan R.","date": "2026-07-14","title": "Sturdy and easy to build","body": "Took about an hour to assemble and looks great in the living room.","rating": 5}],"availability": {"national": { "country": "US", "homeDelivery": true, "clickCollect": true, "stockStatus": "HIGH_IN_STOCK" },"storesWithStock": 37,"stores": [{ "code": "512", "cashCarry": true, "quantity": 48, "stockStatus": "HIGH_IN_STOCK" }]}}
Plan Requirement
IKEA's product and listing web pages sit behind an edge filter that refuses datacenter connections (HTTP 403 "Attention Required" page), while the same pages load normally over a residential connection. Search, category and availability data are read from lighter data endpoints first, and the run automatically retries with a different connection when one is refused, including a backup connection when the primary one is exhausted. Detail enrichment (the full description and individual review text) needs a residential connection and is skipped without one, so on a plan without residential proxy access expect results without descriptions or reviews. Pasted product links and pages that only load in full over residential are also affected. Choose a residential proxy group in the proxy settings for full coverage.
FAQ
How much does it cost?
You pay one product event per returned dataset row and one run-start event per run, plus Apify platform usage for compute, proxies, storage and data transfer. The Pricing tab shows the current rates for your plan. This Lite version has no enrichment or review event fees. Fetching full details and reviews, and store availability, still consume platform resources. Use Max products, Max pages, Max reviews per product and the platform run limits to bound work.
If you set a maximum total charge for a run, the run stops looking up products once that limit leaves room for no further product, and says so in its status message. Only products actually saved to the dataset are billed. In incremental mode a new or changed product (or an EXPIRED one) that did not fit under the limit is not marked as delivered: it is returned on the next run, so no change is lost and none is returned twice. A run stopped by the limit never marks products EXPIRED.
Reading the results
Every run writes a RUN_SUMMARY record to its key-value store: productsScanned, productsReturned (including EXPIRED rows; expiredRowsReturned gives their number), rowsByChangeType, stockReads (attempted, answered, unknownToStockService, unread), deliveryReads (attempted, answered, unreadable; only with deliveryPostcode), productPages (attempted, landed, unread), scanComplete (the same test that allows EXPIRED rows), stateCommitted and stateProblem (incremental mode), stoppedEarlyReason (maxItems, maxTotalCharge, timeLimit or empty) and stoppedAtChargeLimit. When fewer than 99% of the stock or product-page reads could be read, the run's status message says how many.
What changed in how many rows a run returns (November 2026)
These fixes change how many rows incremental runs return:
- Combination products (item numbers starting with
s) now get their stock. Each tracked one is returned once asUPDATEDthe first time its stock is read; in furniture searches this can be a large share of the tracked products, once. - A tracked product whose stock or product page had never been read is returned once as
UPDATEDwhen it first is. - Adding a store to
storeIdson a saved search returns each tracked product once with that store's stock, once per store ever: removing, re-adding, reordering or alternating stores returns nothing. - A colour/size variant list that only changes order is no longer returned as
UPDATED(fewer rows). - With a fixed State key, turning Include availability or Fetch full details off and back on returns nothing by itself when the option was turned off with this release; only real changes come back. State saved by an earlier release while the option was off does not record that it was off, so the first run with the option back on returns each tracked product once as
UPDATEDwith the newly read fields (for exampledescription,material,offerAvailabilityoravailability). (Without a State key these two options are part of the search's saved settings, so changing them starts a separate baseline, as before.)
Is it legal to scrape IKEA?
This actor collects only publicly available product data. You are responsible for how you use it: follow IKEA's terms and the laws that apply to you, and get legal advice if you plan commercial redistribution. Product names, images and marketing descriptions may be subject to IKEA's own rights.
Can I get only new or changed products on a schedule?
Yes. Schedule the actor from the Schedules tab and turn on Incremental mode. Each run then returns only new and updated products, and unchanged ones are not billed unless you also turn on Emit unchanged. Use What counts as a change to watch only prices and stock, and Compact update rows to get the old and new values instead of the full record (see "Daily price and store stock monitor" above).
Why did my run fail instead of returning an empty dataset?
For search, category and listing/room links, if every page this run tried to read was refused or never answered, the run stops with a clear error instead of finishing with 0 products, so "nothing found" is never confused with "nothing could be read". A genuinely empty listing, or a category id IKEA does not recognise, still finishes normally with 0 products and a status message explaining which case it was. For productIds mode and pasted single product links, a fully blocked run is not currently distinguished from a genuinely empty result: it finishes normally with 0 products and a generic "no products found" message, so treat a surprising empty result from a lookup as worth retrying before assuming the item numbers themselves are wrong.
Why is Resume returning nothing new against a category I've already collected a lot from?
Resume skips ids you already have and pages past them, but IKEA caps any listing at 400 products. Once you hold that many, narrow the search or change Sort by.
Can I use it with AI agents or MCP?
Yes. Call it from any Apify integration or MCP client, and use the connector field to push results into Notion, Linear or Airtable.
๐ Want more e-commerce data?
Pair this actor with these related scrapers from the same team:
| ๐ Target AU Scraper Scrape products and customer reviews from Target.com.au. Search by keyword or use... | ๐ MUJI Scraper Scrape MUJI products from the United States, Canada and Australia storefronts. Search by... |
| ๐ Kmart Scraper Scrape products and customer reviews from Kmart.com.au. Search by keyword or use... | ๐ Myer Scraper Scrape Myer (myer.com.au) department-store products: name, brand, price, was-price... |
| ๐ Harvey Norman Scraper Scrape Harvey Norman Australia products: name, brand, price, was-price / discount... | ๐ Costco AU Scraper Scrape Costco.com.au products by keyword or product, search and category URLs. Extract... |
๐ Browse all abotapi scrapers
๐ฌ Support
- ๐ Found a bug or a missing field? Open a ticket on the Issues tab. We usually reply within hours.
- ๐ ๏ธ Need another site, extra fields or a private build? Email contact@abotapi.com.
- โญ Enjoying it? A quick review on the actor page helps other users find it.