Pop Mart Scraper — prices, stock & drop dates
Pricing
from $1.05 / 1,000 products
Pop Mart Scraper — prices, stock & drop dates
Pop Mart Labubu US catalog prices in USD, stock level (0/1/2, not a unit count) & release date. A separate events dataset records sold out, restock and price changes, with the time range between two checks. Events start on the second run. PPE per product row and per stock-change.
Pricing
from $1.05 / 1,000 products
Rating
0.0
(0)
Developer
Noah Adler
Maintained by CommunityActor stats
0
Bookmarked
2
Total users
1
Monthly active users
17 days ago
Last modified
Categories
Share
Pop Mart product data from the US store: Labubu, Molly, SkullPanda, Crybaby, Dimoo, Hirono and the rest of the catalogue. One row per SKU with price in USD, live stock level, sold-out flag and the official release date.
Blind box and Whole Set are priced separately on Pop Mart, so they come out as separate rows — the way a reseller actually compares them.
Not a purchase bot. Drops sell out in seconds and this Actor does not touch checkout. It gives you the data: what dropped, when, at what price, and whether it is in stock.
Keywords: pop mart scraper · labubu price · popmart api · pop mart stock · blind box scraper
What it does
| Step | Action |
|---|---|
| 1 | Input: searchTerms (series or keyword) + maxItems. Default mode is snapshot. autoWatch is off |
| 2 | Reads the Pop Mart US storefront search (server-rendered payload, no browser) |
| 3 | Paginates until the catalogue for that term is exhausted or maxItems is hit |
| 4 | Flattens each product into its SKUs and pushes one row per SKU to the default dataset |
| 5 | Compares each SKU with the previous observation and appends any change to the events dataset |
Input
| Field | Required | Description |
|---|---|---|
searchTerms | Yes | Series or keywords as typed on popmart.com: labubu, molly, skullpanda, crybaby, dimoo, hirono… |
maxItems | No | 1–500 rows across all terms (default 100) |
includeSoldOut | No | true (default) keeps sold-out SKUs — needed to track restocks |
includeZeroPriceSkus | No | false (default) drops add-on placeholder SKUs with no real price |
mode | No | snapshot (default) or watch |
autoWatch | No | false (default). Snapshot only. If a SKU's releaseAt (UTC) is inside the next watch window, this same run keeps polling only those SKUs until the window ends. No second run and no schedule |
stateStoreName | No | Key-value store for the last observation. Default popmart-state. Different names do not overwrite each other. The same name still races if two runs use it at once |
watchProductIds | No | Watch only. Product IDs to keep. searchTerms is still required. Up to 40 |
pollIntervalSec | No | Watch and auto-watch. 15–300, default 60. This is the gap the run aims for between looks |
watchWindowMinutes | No | Watch and auto-watch. 30–60, default 45. The run exits when the window ends |
proxyConfiguration | No | RESIDENTIAL US recommended |
Example (Try it)
{"searchTerms": ["labubu", "skullpanda"],"maxItems": 60,"includeSoldOut": true,"includeZeroPriceSkus": false,"mode": "snapshot","autoWatch": false,"proxyConfiguration": {"useApifyProxy": true,"apifyProxyGroups": ["RESIDENTIAL"],"apifyProxyCountry": "US"}}
Output (one SKU per row)
| Field | Example |
|---|---|
productTitle | THE MONSTERS Hair Salon Series Figures |
skuTitle / skuCode | Single box · Whole Set |
price / discountPrice / currency | 17.99 · 17.99 · USD |
onlineStock / isSoldOut | 1 · false |
isAvailable | product-level sale status |
releaseAt | 2026-06-26T23:30:00Z — official online release, UTC |
url / imageUrl | https://www.popmart.com/us/products/{id} |
error | honest soft-fail row if the storefront blocks |
Zero-price placeholder SKUs are not pushed as success rows unless you ask for them.
onlineStock is a level, not a count of boxes: 0 (sold out), 1 or 2 (buyable). The default dataset is always this catalog shape. Stock changes are not mixed into these rows.
Snapshot and watch
Snapshot (default, including Try it) reads the catalogue once. It updates the remembered level for each SKU and appends any change since the previous run. It also logs SKUs whose releaseAt (UTC) falls in the next 24 hours. It does not start another run. autoWatch defaults to false, so Try it stays a single look.
autoWatch (only when mode is snapshot and the flag is true): after that full snapshot, if any SKU has releaseAt within the next watchWindowMinutes (default 45, UTC), this same run keeps polling only those SKUs every pollIntervalSec until the window ends. It does not spawn a second run and it does not create a schedule. If no releaseAt is inside that window, the snapshot ends as usual. A schedule you create every 30–60 minutes, plus autoWatch, is how drops get covered. releaseAt comparisons are UTC.
Watch stays inside this run. Pass the series in searchTerms. Optionally pass watchProductIds to keep only those products (the product page does not include the stock level, so the run reads search results and drops the rest). It looks again every pollIntervalSec until watchWindowMinutes is over, then exits. State is written after every poll, not only when the run ends, so a platform timeout does not leave the next run diffing a stale snapshot.
Start the watch run yourself shortly before releaseAt (UTC). The Actor does not sleep until the release, and it does not create a schedule. Set the Apify run timeout longer than the window (a 45 minute watch needs a timeout above 45 minutes). If the timeout is shorter, the run stops and the log says the window did not complete.
The default dataset receives the latest catalog once, at the end of the watch, so it is not filled with one copy per poll. Every transition still goes to events.
{"searchTerms": ["labubu"],"watchProductIds": ["8990"],"mode": "watch","pollIntervalSec": 60,"watchWindowMinutes": 45,"maxItems": 20,"includeSoldOut": true}
Events dataset (events)
One row per transition, in a dataset named events (not the default dataset). Rows accumulate across runs on the account that runs the Actor. The last observation for each SKU is kept in the key-value store named by stateStoreName (default popmart-state: level, price, time, title, URL). No personal data. Two parallel runs that share that name can overwrite each other's last observation.
Events show up starting on the second run. The first time a SKU is seen, the only event is new_sku. Sold-out, restock and other level changes start on the next observation.
event | When |
|---|---|
new_sku | First observation. No previous level |
stock_drop | Level 2 → 1 |
sold_out | Level 1 → 0 or 2 → 0 |
restock | Level leaves 0 (0 → 1 or 0 → 2) |
stock_rise | Level 1 → 2. This is not a restock |
price_change | Price changed. If the level changed too, this is a second row |
A change is noticed by comparing two looks. Times are UTC. The time on the row is only as precise as the gap between those two looks. That gap is changedBetween: [previous observation, this observation]. It is not the exact second the level moved. In watch mode the run aims to look every pollIntervalSec seconds, so the gap is about that long, and pollIntervalSec is filled in. In a one-shot snapshot, pollIntervalSec is null and the precision is changedBetween itself (however long it was since the previous run).
new_sku has no earlier look, so changedBetween is null.
For sold_out only, timeToSoldOut is two durations in seconds, computed from UTC timestamps: from releaseAt to the previous look, and from releaseAt to the look that first saw level 0. It is a range, not a US-local clock time. If releaseAt is missing, timeToSoldOut is null. There is never a single sold-out time.
| Field | Meaning |
|---|---|
sku / series / skuTitle / productId / url | Which SKU. series is the product title |
fromLevel / toLevel | Levels 0, 1 or 2. Null on new_sku and on price_change |
fromPrice / toPrice / currency | Set on price_change |
prevSeenAt / seenAt / scrapedAt | ISO times in UTC. scrapedAt is this observation, same as seenAt |
changedBetween | The two observation times (UTC). Null on new_sku |
timeToSoldOut | Two integers, seconds since releaseAt (UTC). Only on sold_out; otherwise null |
pollIntervalSec | Watch interval for this run, or null on a snapshot |
Pricing
Pay-per-event: the catalog row is the product charge (one per SKU in the default dataset). Each row in the events dataset is a separate stock-change charge, higher than the catalog row and still a cheap volume price. A named dataset is not covered by the catalog event. See Actor pricing on Apify.
Notes
- Coverage is the US store (
popmart.com/us), so prices are USD. - A handful of legacy products carry no release timestamp;
releaseAtis left empty rather than guessed. Those SKUs gettimeToSoldOut: nullif they sell out. onlineStockis a stock level, not a unit count: the storefront only exposes0(sold out),1or2(buyable). Read it withisSoldOut.- You can run snapshot on your own schedule if you want a daily history. This Actor does not create that schedule. Turn on
autoWatchon that schedule if you also want the run to stay and poll when areleaseAt(UTC) is inside the window. releaseAt,seenAtandtimeToSoldOutare UTC.timeToSoldOutis a range of durations in seconds, not a US local time.- If a run returns a single row with
error=truementioning Cloudflare, turn on RESIDENTIAL US and retry.