fastscraping
API reference · v2

Shopee Product API

Real-time Shopee product data — price, discount, stock, variants, sold count, rating and shop details — for eight Shopee markets through one asynchronous REST API.

Base URL
https://shopee-multi-region.fastscraping.com
Auth
X-API-Key header
Format
JSON over HTTP
Model
Async · submit, then poll

Overview

You send a Shopee product (shop_id, item_id, region) and get a job_id back immediately. Our infrastructure collects the product in parallel with other jobs, and you poll the job until it is completed. The finished job carries the product data under raw.

Asynchronous by design

No request holds a connection open while Shopee is scraped, so throughput grows with the number of jobs you send in parallel.

Typical completion time

Measured median from submit to result: 10–20 s for Thailand, Indonesia and the Philippines; 25–50 s for Singapore, Vietnam, Malaysia and Brazil. Taiwan usually finishes within 1–3 minutes.

Charged only for answers

A job is charged only when it returns a definitive answer within its TTL. Our own failures and expired jobs are never charged.

Anti-bot handled

Sessions, proxies and blocking are managed on our side. You call a plain REST API with one key.

Quickstart

Submit a job.

POST the product to /jobs. The response returns a job_id straight away.

Poll the job.

GET /jobs/{job_id} every 3–5 seconds while status is pending or processing.

Read the data.

When status is completed, the product data is under raw. result_status tells you whether the product is live, out of stock or unavailable.

# 1) submit
curl -s -X POST https://shopee-multi-region.fastscraping.com/jobs \
  -H "X-API-Key: YOUR_API_KEY" -H "Content-Type: application/json" \
  -d '{"shop_id":83276818,"item_id":23743605234,"region":"th","ttl":600}'

# 2) poll with the job_id from step 1
curl -s https://shopee-multi-region.fastscraping.com/jobs/JOB_ID -H "X-API-Key: YOUR_API_KEY"

Authentication

Every request needs your API key in the X-API-Key header. Authorization: Bearer YOUR_API_KEY is accepted as well.

HTTP header
X-API-Key: YOUR_API_KEY

A missing key returns 401 Missing API key; an unknown or disabled key returns 401 Invalid or inactive API key. Jobs are private to the key that created them.

Regions

Pass one of these codes as region. The blocks inside raw depend on the market; see Response data.

CodeMarketCurrencyBlocks in raw
twTaiwanTWDget_pc — full product page payload
brBrazilBRLget_pc — full product page payload
thThailandTHBbasic_pdp · get_pc · shop
idIndonesiaIDRbasic_pdp · get_pc · shop
myMalaysiaMYRbasic_pdp · get_pc · shop
phPhilippinesPHPbasic_pdp · get_pc · shop
vnVietnamVNDbasic_pdp · get_pc · shop
sgSingaporeSGDbasic_pdp · get_pc · shop
Prices are Shopee integers. Divide by 100,000 for the amount in local currency: 7386000 in Brazil is R$ 73.86; 999000000 in Thailand is ฿ 9,990.

Submit a job

POST/jobsX-API-Key

Creates one scrape job for one product and returns immediately, normally with status: "pending". Each call creates a new, separately charged job and runs a fresh scrape. One exception: a product we found delisted in the last few hours is answered at once as product_not_available, and the POST response then already shows status: "completed".

Request body

FieldTypeDescription
shop_idrequiredintegerShopee shop ID, the first number in shopee.xx/product/{shop_id}/{item_id}.
item_idrequiredintegerShopee item ID.
regionrequiredstringLowercase market code: tw, br, th, id, my, ph, vn or sg. Always send it: a request without region is not rejected but runs as Thailand (th). Upper-case codes such as "TH" return 400.
ttloptionalintegerSeconds the job has to finish. Range 300–3600 (5 minutes to 1 hour): lower values become 300 and higher values become 3600. Omitted or 0 means the default, 3600. The effective value is echoed in the response note. See TTL & billing.
all_voucheroptionalboolean or stringTaiwan. true, or one of "yes", "true", "1", "y", "on", also fetches the shop's vouchers into raw.get_pc.data.promotion_drawer.shop_vouchers. Other regions accept the flag without an error, but vouchers are not guaranteed there.
Request
POST /jobs
Content-Type: application/json
X-API-Key: YOUR_API_KEY

{
  "shop_id": 1665239494,
  "item_id": 58256703066,
  "region": "br",
  "ttl": 600
}

Response · 200

application/json
{
  "job_id": "8aabc85bf7724eceaec27e5f375ad905",
  "status": "pending",
  "shop_id": 1665239494,
  "item_id": 58256703066,
  "note": "Poll GET /jobs/8aabc85bf7724eceaec27e5f375ad905 for your result. This job expires in 600s if not fetched in time."
}

Possible errors: 400 unsupported region, 401 key problem, 422 a body that does not match the schema (for example a missing or non-integer shop_id), 429 rate, concurrency or quota limit. See Errors.

Get a job

GET/jobs/{job_id}X-API-Key

Returns the job's state. While it runs you get the envelope without data; once it completes, the product data is added under raw.

Response fields

FieldTypeDescription
job_idstringThe job you asked for.
statusstringpending, processing, completed, failed, expired or not_found. See Job lifecycle.
shop_id, item_idintegerThe product you submitted.
result_statusstring · nullWhat we found: success, out_of_stock, item_unavailable, product_not_available, shop_unavailable, failed, expired or not_found. null while the job runs.
billableboolean · nullWhether this job is charged. null while the job runs.
retriablebooleantrue when submitting the same product again is worthwhile: the job was not charged, or (rarely) a completed job whose data failed our integrity check and was withheld.
notestring · nullFree-text context, such as an internal retry, an expiry reason, or a notice that the data is no longer available. Informational only; it can be set in any status, so do not branch on it.
voucher_sourcestring · nullFor all_voucher jobs: live, cache or negative_cache (the shop has no vouchers).
rawobjectThe product data. Present on completed jobs while the data is retained (see TTL & billing); absent once it has aged out or when our integrity check withheld it, and can be empty for product_not_available. See Response data.

Completed jobs can also carry diagnostic fields such as http_status; they are safe to ignore.

{
  "job_id": "8aabc85bf7724eceaec27e5f375ad905",
  "status": "completed",
  "shop_id": 1665239494,
  "item_id": 58256703066,
  "result_status": "success",
  "billable": true,
  "retriable": false,
  "note": "",
  "voucher_source": null,
  "raw": {
    "get_pc": {
      "bff_meta": null, "error": 0, "error_msg": null,
      "data": {
        "item": {
          "item_id": 58256703066, "shop_id": 1665239494,
          "title": "Kit Mel Cola 1k Óleo Nutritivo Mel Natural 60 ml Trihair",
          "currency": "BRL",
          "price": 7386000, "price_before_discount": 7774000,
          "stock": 409, "total_stock": 1213,
          "historical_sold": 2609,
          "tier_variations": [ { "name": "Quantidade", "options": ["Kit Mel Cola 1kg + Óleo 60ml"], … } ],
          "models": [ { "model_id": 239439288108, "name": "Kit Mel Cola 1kg + Óleo 60ml", "stock": 1213, … } ],
          …
        },
        "product_price": {
          "price": { "single_value": 7386000, "range_min": -1, "range_max": -1 },
          "price_before_discount": { "single_value": 7774000, … },
          "discount": 5, …
        },
        "price_breakdown": {
          "discount_breakdown": [ { "price_source": "Product Discount", "discount_amount": 388000, … } ], …
        },
        "product_review": {
          "rating_star": 4.983, "rating_count": [1239, 1, 0, 5, 7, 1226],
          "historical_sold": 2609, "liked_count": 2494, …
        },
        "shop_detailed": { "shopid": 1665239494, "name": "Cades Cosméticos - Loja Oficial", "rating_star": 4.886, … },
        "product_images": { … }, "product_attributes": { … },
        "product_description": { … }, "promotion_info": { … }
      }
    }
  }
}

Possible errors: 404 job not found, 403 not your job (the job belongs to another key), 401 key problem.

Latest result for a product

GET/result?shop_id=…&item_id=…&region=…X-API-Key

Returns the latest cached data for a product without a job_id. It works only for products your key already has a completed job for, and it reads the same product cache as GET /jobs/{job_id}: data is kept for 2 hours after the most recent completed scrape of that product. It is not charged.

Query parameters

ParameterTypeDescription
shop_idrequiredintegerShopee shop ID.
item_idrequiredintegerShopee item ID.
regionrequiredstringLowercase market code, the same one you used when you submitted the job. If omitted, Thailand (th) is assumed.

Response fields

FieldTypeDescription
shop_id, item_idintegerThe product.
statusstringThe result for this product: success, out_of_stock, item_unavailable or product_not_available (the same values as result_status on a job).
voucher_sourcestring · nullFor all_voucher results: live, cache or negative_cache.
rawobjectThe product data, in the same format as a completed job. See Response data.
Request and response · trimmed
curl -s "https://shopee-multi-region.fastscraping.com/result?shop_id=83276818&item_id=23743605234&region=th" \
  -H "X-API-Key: YOUR_API_KEY"

{
  "shop_id": 83276818,
  "item_id": 23743605234,
  "status": "success",
  "voucher_source": null,
  "raw": { "basic_pdp": { … }, "get_pc": { … }, "shop": { … } }
}

Errors: 404 no completed job for this product on your key when your key has not requested this product yet; 404 no result yet (pending/failed/expired) when the cached data has aged out (submit a new job); 401 key problem; 422 missing or non-integer shop_id/item_id. Completed results may also carry diagnostic fields such as http_status; they are safe to ignore.

Job lifecycle

A job normally moves from pending to processing and ends in one of four final states. It can go back from processing to pending while we retry it internally, and it can end without reaching processing (expired in the queue, or answered at once for a recently delisted product). Poll until status is no longer pending or processing.

pending→processing→ completed|failed|expired|not_found
statusresult_statusMeaningChargedRetry
completedsuccessLive product; full data in raw.yesno
completedout_of_stockThe product exists but has no stock. Data is still returned.yesno
completeditem_unavailableThe listing exists but cannot be bought right now.yesno
completedproduct_not_availableDelisted, banned or deleted on Shopee. raw can be partial or empty.yesno
failedshop_unavailable, failedWe could not complete the scrape.noyes
expiredexpiredThe job did not finish in time: its TTL ran out, or it could not be processed after repeated internal retries.noyes
not_foundnot_foundShopee does not recognise this shop and item pair.nono

Use the billable and retriable flags in your code rather than hard-coding this table; they are computed per job and always reflect the final decision.

Response data

Where to find the common data points in raw. Field names follow Shopee's format. A value that is not available for a product comes back as 0, an empty string or null; treat those as not available. Numbers inside basic_pdp can arrive as JSON strings.

Taiwan and Brazil · raw.get_pc.data

Data pointPath
Titleitem.title
Current priceproduct_price.price.single_value, also item.price
Price before discountproduct_price.price_before_discount.single_value
Discount %product_price.discount
Discount breakdownprice_breakdown.discount_breakdown[]
Stockitem.total_stock (sum across all variants). item.stock can be a smaller promotion allocation.
Variants and SKUsitem.models[] (model_id, name, stock), item.tier_variations[]. models[].price is not always the price a buyer pays; use product_price for the current price.
Sold countproduct_review.historical_sold, product_review.global_sold
Ratingproduct_review.rating_star; product_review.rating_count = [total, 1★, 2★, 3★, 4★, 5★]
Likesproduct_review.liked_count
Images and videoproduct_images
Categories and attributesitem.categories[] (category IDs; display_name is empty), product_attributes.attrs (can be empty)
Descriptionproduct_description.paragraph_list
Shopshop_detailed (shopid, name, rating_star, is_official_shop, is_shopee_verified). Counters such as follower_count and item_count are not filled in this block.
Shop vouchers (Taiwan, all_voucher)promotion_drawer.shop_vouchers[] (voucher_code, min_spend, start_time, end_time …)

Thailand, Indonesia, Malaysia, Philippines, Vietnam, Singapore

Data pointPath
Titleraw.basic_pdp.name
Current priceraw.get_pc.data.item.price (range: price_min, price_max), including running promotions. raw.basic_pdp.price_min/price_max leave promotions out and can be higher.
Price before discountraw.get_pc.data.item.price_before_discount; also raw.basic_pdp.normal_price_min, normal_price_max
Discount %raw.get_pc.data.item.show_discount
Stockraw.basic_pdp.total_stock (a string, e.g. "311")
Variants and SKUsraw.basic_pdp.models[] (model_id, name, price, normal_price, stock, sold), tier_variations[]
Sold countraw.basic_pdp.sold
Ratingraw.basic_pdp.rating.rating_star; rating.rating_counts = [0, 1★, 2★, 3★, 4★, 5★] (index 0 is not the total)
Likes and commentsraw.basic_pdp.liked_count, cmt_count
Images and descriptionraw.basic_pdp.images (Shopee image IDs). Plain-text description: raw.get_pc.data.item.description; raw.basic_pdp.description is a JSON-encoded list of text and image segments.
Page item summaryraw.get_pc.data.item (title, categories, item_rating, tier_variations …)
Shopraw.shop (name, rating_star, follower_count, response_rate, shop_location, item_count …)
Example · Thailand, trimmed
{
  "basic_pdp": {
    "item_id": "23743605234", "shop_id": "83276818", "currency": "THB",
    "price_min": "1089000000", "price_max": "1089000000",
    "total_stock": "311", "sold": 4266,
    "rating": { "rating_star": 4.71, "rating_counts": [0, 52, 20, 55, 119, 1477] },
    "models": [ { "model_id": "186771150146", "price": "1089000000", "normal_price": "1749000000", "stock": 311, "sold": 4266, … } ],
    …
  },
  "get_pc": { "data": { "item": { "price": 999000000, "price_before_discount": 1749000000, "show_discount": 43, … } } },
  "shop": { "shopid": 83276818, "name": "samsung_thailand", "rating_star": 4.89, "follower_count": 1628625, "response_rate": 97, … }
}

TTL & billing

  • TTL is the time a job has to finish: 300 to 3600 seconds, default 3600.
  • A job is charged once when it completes with billable: true within its TTL or the 20-second grace after it. One job is one charge, whatever the size of the product data.
  • A job that has not completed by its TTL plus the 20-second grace becomes expired and is not charged. Keep polling until about TTL + 30 seconds before giving up.
  • Product data is kept for 2 hours after the most recent completed scrape of that product. It is stored per product, so if the same product is scraped again in that time, an earlier job returns the newer data under raw. After that the envelope still shows the final status and billing, with a note that the data is no longer available.
  • Submitting the same product again creates a new job, charged separately (see Submit a job for the delisted-product exception).

Rate limits

Each API key has its own limits, checked when you submit a job (POST /jobs). Polling and the /me endpoints are not limited. When a limit is reached, POST /jobs returns 429 with a message naming it.

LimitWhat it counts429 message
ConcurrencyYour jobs currently pending or processingConcurrency limit reached (N in-flight jobs)
Per minute, hour, dayCharged results in the last minute, the last hour, or since 00:00 UTCRate limit exceeded (per-minute), (per-hour), (per-day)
Total quotaCharged results over the key's lifetime (trial keys)Quota exceeded (lifetime)

GET /me/usage always shows the limits on your key. The per-minute, per-hour, per-day and quota limits compare results already charged, so jobs you have in flight still run and are charged; the charged total can pass a limit by up to your in-flight jobs. Counters refresh every few seconds.

Usage

GET/me/usage?interval=30X-API-Key

Your key's limits, remaining credits, job counts and charged usage per region. interval (1–365, default 30) sets how many days daily covers; values outside that range return 422. Day buckets in totals, daily and monthly_breakdown follow the server's reporting day (Central European Time).

In monthly_breakdown, a month with no usage is null and a day with no usage is 0 instead of an object. jobs.total also counts processing and expired jobs.

Response · trimmed
{
  "owner": "your-company",
  "timezone": "UTC",
  "key": { "is_active": true, "created_at": "2026-09-25T10:20:20" },
  "limits": { "per_minute": 120, "per_hour": 1200, "per_day": 1000, "concurrency": 100 },
  "credits": { "total": 1000, "used": 37, "remaining": 963, "unlimited": false },
  "jobs": { "total": 41, "completed": 37, "failed": 1, "pending": 3, "not_found": 0, "billable": 37 },
  "totals": {
    "today": { "all": 12, "th": 9, "tw": 3 },
    "yesterday": { "all": 25, "br": 25 },
    …  day_before_yesterday, last_7_days, last_30_days, this_month, last_month, this_year, lifetime
  },
  "daily": [ { "date": "2026-09-25", "th": 9, "tw": 3, "all": 12 }, … ],
  "monthly_breakdown": { "2026-09": { "24": { "th": 9, "all": 9 }, "25": 0, …, "total": 37 }, "2026-08": null }
}

Job history

GET/me/jobs?limit=50&offset=0X-API-Key

Your jobs, newest first. limit is 1–500 (default 50); use offset to page. Out-of-range values return 422. created_ts is a Unix timestamp in seconds.

Response
{
  "limit": 2, "offset": 0,
  "items": [
    { "job_id": "a32d719e27744a1393722737e9209506", "region": "tw", "shop_id": 5597991, "item_id": 74412812,
      "status": "completed", "result_status": "success", "billable": true, "created_ts": 1790228814.34 },
    { "job_id": "8daf6d5a0e834572b0a0221e0089bbe8", "region": "tw", "shop_id": 37004578, "item_id": 7775080780,
      "status": "completed", "result_status": "success", "billable": true, "created_ts": 1790227317.52 }
  ]
}

Health

GET/healthno key

Returns HTTP 200 with "status": "ok" while the API is up. Use it for uptime monitoring.

Errors

Errors return a JSON body with a detail field.

Example
HTTP/1.1 429 Too Many Requests
{ "detail": "Concurrency limit reached (100 in-flight jobs)" }
CodedetailWhat to do
400Unsupported region 'xx'. Supported: [...]Use one of the region codes.
401Missing API key, Invalid or inactive API keySend a valid key in X-API-Key.
403not your jobRead the job with the key that created it.
404job not foundCheck the job_id.
404no completed job for this product on your keyGET /result only serves products your key has a completed job for. Submit a job first.
422Validation details (a list)The request did not match the schema: a missing or non-integer shop_id/item_id, a non-integer ttl, a body that is not JSON, or a query parameter out of range on /me/usage or /me/jobs.
429Rate, concurrency or quota messageWait for in-flight jobs to finish or slow down. See Rate limits.

Best practices

  • Send jobs in parallel. The system is built for volume: 20–50 jobs at once per region finish sooner overall than one-by-one submission. Keep in-flight jobs under your concurrency limit.
  • Poll every 3–5 seconds. Faster polling does not speed a job up.
  • Choose a TTL that fits your use. A short TTL gives you a quick, uncharged expired when a job cannot finish in time; the 1-hour default favours completion.
  • Retry only when retriable is true. Charged answers such as product_not_available are final.