The useful result is not a page of product cards. It is a Dataset where each
row has a stable Shopee product identity, local price and currency, seller,
rating, availability evidence, and canonical URL.
In one bounded Indonesia test, the workflow below returned 10 unique, complete
IDR rows in about 34 seconds. Nine titles matched AirPods; Shopee also returned
one adjacent Apple iPhone 15 result. That is a good example of both the value
and the boundary: public marketplace search is useful, but it is not a promise
of perfect keyword precision.
Disclosure: I built the Apify Actor used in this guide. Successful runs
can generate pay-per-event revenue for the Actor developer. The evidence in
this article comes from one bounded owner-run test, not an external customer
case study.
What you will collect
The Actor accepts either public Shopee product URLs or keywords. This tutorial
uses keyword discovery and asks for no Shopee login, user cookies, or proxy
configuration in the input.
A complete product row can contain:
-
marketand localcurrency; -
product_idandshop_idas exact strings; - product
title,price, and canonical URL; - seller name and rating when the source identifies the rating scope;
- listing or variant availability evidence;
-
source_mode,source_fetched_at, and completeness metadata.
Availability evidence is not the same as exact stock quantity. A listing can
be observed as available while stock.quantity remains null.
Start with a small keyword run
Open Shopee Product Scraper on Apify.
Use this input:
{
"searchTerms": ["airpods"],
"market": "id",
"sort": "sales",
"maxItems": 10,
"maxPages": 10
}
Why start at 10 products?
- You can inspect every row before scaling.
- Runtime and maximum charge stay easy to understand.
- Relevance problems are visible instead of buried in a large export.
- A failed or incomplete candidate does not become a misleading Dataset row.
The input selector contains 14 official Shopee storefronts. Singapore is
Verified in the current Store contract. The other 13, including Indonesia,
remain Beta pending additional market-local canaries. The Indonesia result in
this tutorial is one point-in-time keyword proof, not blanket verification of
every Indonesia workflow.
Run it from the Apify Console
- Choose Indonesia as the storefront.
- Enter
airpodsunder keyword discovery. - Select Best-selling.
- Set the result limit to 10.
- Start the run.
- Open the Dataset when the run succeeds.
Check the first rows before downloading anything. Confirm that:
-
marketisid; -
currencyisIDR; - the product and shop IDs are present;
- the title matches the product URL identity;
- the seller and rating fields have the expected scope;
- the stock object explains its evidence instead of inventing a quantity.
In the cited run, all 10 rows passed those completeness checks. One title was
an adjacent Apple product rather than an AirPods listing, so downstream users
should still filter titles, IDs, sellers, or categories for their specific
research question.
Call the same Shopee product scraper with cURL
Load your Apify token from an environment variable. Never paste the real token
into source code, an AI prompt, a screenshot, or a public tutorial.
Create or copy the token in Apify Console,
then store it locally as APIFY_API_TOKEN before running the command.
curl -sS --request POST \
"https://api.apify.com/v2/acts/kazkn~shopee-product-scraper-no-login/run-sync-get-dataset-items?clean=true&format=json" \
--header "Authorization: Bearer ${APIFY_API_TOKEN}" \
--header "Content-Type: application/json" \
--data '{
"searchTerms": ["airpods"],
"market": "id",
"sort": "sales",
"maxItems": 10,
"maxPages": 10
}'
This endpoint waits for the run and returns Dataset items. For longer jobs,
start an asynchronous Actor run and retrieve its default Dataset separately.
Use JavaScript
Install the official Apify client in your project and keep the token in the
environment:
import { ApifyClient } from 'apify-client';
const client = new ApifyClient({
token: process.env.APIFY_API_TOKEN,
});
const run = await client.actor('kazkn/shopee-product-scraper-no-login').call({
searchTerms: ['airpods'],
market: 'id',
sort: 'sales',
maxItems: 10,
maxPages: 10,
});
const { items } = await client.dataset(run.defaultDatasetId).listItems({
clean: true,
});
const idrRows = items.filter((item) =>
item.market === 'id' && item.currency === 'IDR'
);
console.log(idrRows.map(({ title, price, seller, canonical_url }) => ({
title,
price,
seller,
canonical_url,
})));
Use the field names shown by the actual Dataset schema. If you transform rows,
keep product_id, shop_id, market, and currency alongside the values so
you do not merge different storefront products by title alone.
Export to CSV or Excel
From the Dataset page, choose CSV or Excel for spreadsheet work. JSON is better
when you need nested stock evidence or provenance fields.
Before comparing prices:
- filter to the market you intend to study;
- keep the local currency column;
- remove adjacent or irrelevant titles for your query;
- deduplicate by market, shop ID, and product ID;
- treat
source_fetched_atas the observation time; - do not compare IDR, SGD, MYR, or other local amounts without currency conversion.
What happens to incomplete products?
The Actor uses a fail-closed row contract. If required identity, price,
currency, seller, rating, or availability evidence cannot be proven, the
candidate is skipped before Dataset push.
That has two practical consequences:
- a run can return fewer products than the input limit;
- an incomplete candidate is not charged as a result event.
Apify platform usage is included in the active Actor price. Check the current
Store pricing table before a large run and keep the first test bounded.
Common questions
Does this use an official Shopee API?
No claim is made about an official Shopee API. This tutorial calls the Apify
Actor API. The Actor reads public or indexed product sources and validates rows
before emission.
Are all 14 storefronts equally reliable?
No. The selector exposes 14 official storefront options, but the current Store
contract marks Singapore Verified and the other 13 Beta pending market-local
canaries.
Is the keyword search perfectly precise?
No. The bounded Indonesia test matched 9 of 10 titles and included one adjacent
Apple product. Use the returned identities and fields to apply your own
downstream relevance rules.
Does in_stock mean an exact quantity is available?
No. Read the stock evidence field. Listing or variation availability can be
observed while the exact quantity remains unknown.
Run the bounded workflow
Start with one storefront, one keyword, and 10 products. Validate the Dataset,
then increase only the limit your workflow actually needs.
Copy-ready Python, JavaScript, cURL, and n8n examples are available in the
public GitHub repository.













