# Shopee API — product catalogue, per-variant stock, seller profiles and shop vouchers across Indonesia, Vietnam, Thailand, the Philippines, Malaysia, Singapore and Taiwan

> Full Shopee product detail by shop id + item id, or by product link. Title, description, every image, every variant with its own live stock count, variant option axes, attributes, 3-level category path, brand, condition, currency, seller location, product videos with direct MP4 links, and the seller's active voucher codes with their minimum spend. Works the same on all seven markets. Two limits stated up front: Shopee removes PRICE, rating, review count and sold count from the public product page for everyone, so this action returns them as null (the shop_showcase action returns a real live price for one item per shop); and Shopee publishes no weight or dimensions field at all
> ReefAPI engine `shopee` · 4 endpoints · clean JSON, no scraping or browsers to manage.

## How to call
- **Endpoint:** `POST https://api.reefapi.com/shopee/v1/<action>` with a JSON body.
- **Auth:** header `x-api-key: <YOUR_REEFAPI_KEY>` — create one free (1,000 credits, no card): https://reefapi.com/signup
- **Response (every call):** `{ ok: boolean, data: ..., meta: { record_count, credits, ... }, error: { code, message } }` — branch on `ok`. Failed or blocked calls are free.
- **One key + one shared credit pool** across every ReefAPI API. Per-call credits are listed on each endpoint below.
- **Use it from an AI agent (MCP):** connect `https://api.reefapi.com/mcp` (remote streamable-http, `Authorization: Bearer <key>`) and your assistant can call these actions directly.

## Endpoints

### POST /shopee/v1/product — 2 credits
Full Shopee product detail by shop id + item id, or by product link. Title, description, every image, every variant with its own live stock count, variant option axes, attributes, 3-level category path, brand, condition, currency, seller location, product videos with direct MP4 links, and the seller's active voucher codes with their minimum spend. Works the same on all seven markets. Two limits stated up front: Shopee removes PRICE, rating, review count and sold count from the public product page for everyone, so this action returns them as null (the shop_showcase action returns a real live price for one item per shop); and Shopee publishes no weight or dimensions field at all

**Parameters:**
- `shop_id` (integer, optional) — Shopee shop id — the FIRST number in a product link, https://shopee.co.id/product/<shop_id>/<item_id>. A value that is not a positive whole number is reported as an invalid parameter before any request is made, so a typo costs nothing.
- `item_id` (integer, optional) — Shopee item id — the SECOND number in a product link, https://shopee.co.id/product/<shop_id>/<item_id>. There is no keyword search on this API (Shopee serves no listing data to anyone but its own app), so ids have to come from links, your own catalogue or your affiliate feed.
- `url` (string, optional) — A full Shopee product link instead of the two ids — both shapes work, /product/<shop_id>/<item_id> and the share link <product-slug>-i.<shop_id>.<item_id>. The market is taken from the link, so `market` is not needed. Overrides shop_id/item_id when both are given.
- `market` (enum, optional, default "id") — Which Shopee marketplace to read. The seven listed here are the ones this API is measured on; Shopee also runs Brazil, Mexico, Chile and Colombia, and those are rejected rather than half-served. Ignored when you pass `url` — the market is read from the link. Ids are per-market: the same item_id on a different market is a different product or nothing at all. [one of: id, vn, th, ph, my, sg, tw]

**Returns:** product{} — market, market_domain, item_id, shop_id (both ints, returned exactly as Shopee stores them), url, canonical_url, title, description (plain text), description_paragraphs[] (typed text/image blocks with usable image URLs), rich_description_paragraphs[], brand, brand_id, condition ("new"/"used"), currency, currency_scale, item_status, created_at, images[] {id,url}, videos[] {video_id,duration_seconds,thumbnail_url,url,formats[]} with direct MP4 links, categories[] and frontend_categories[] {id,name,level} (Shopee keeps two taxonomies; both are returned), attributes[] {id,name,value,value_id}, variants[] {model_id,name,stock,tier_index,sku_image_url,measurements,price}, variant_options[] {name,options[],images[]}, stock, stock_source, shop{}, shop_vouchers[], seller_services[], wholesale_tiers[], size_chart, logistics{}, flags{}. PER-VARIANT STOCK is the strongest field here: an integer on 2,186 of 2,186 variants measured across 195 products. `stock` is OUR SUM of those variant integers, flagged by stock_source — Shopee deletes its own total-stock field, so this is a computed total, not a separate upstream number. ALWAYS NULL, and each carries its reason under `unavailable`: price, price_before_discount, discount, rating, review_count, sold_count, weight, dimensions, shipping, related products, review text, and the shop NAME (fetch that from the shop action). Weight and dimensions are absent AT SOURCE — there is no such key anywhere in Shopee's product schema — so they are never guessed from description text. Variant prices are null on every variant and are never back-filled from a parent value. shop_vouchers[] is a real find and no competitor exposes it: voucher_code, type, discount_amount or discount_percent, min_spend, currency, claim and burn percentages and the validity window, in real currency — Shopee stripped the product price but left the seller's voucher economics on the page. Populated on ~46% of products, shop-level (the voucher applies to the seller, not this SKU), and the amounts are structurally coherent but were never test-redeemed. videos[] is populated on ~61% of products. source_removed_fields[] is Shopee's own redaction manifest for that product, passed through so you can see exactly what they withheld. Errors that mean something specific: NOT_FOUND = the ids name nothing; a coded SOURCE_UNRENDERABLE = the product is live but sits in one of Shopee's own bulk aggregation storefronts, whose product pages Shopee's renderer refuses to serve — the error names the shop and its listing count, and the shop action flags those shops in advance.

**Example request body:**
```json
{
  "shop_id": 165796339,
  "item_id": 4669874676,
  "market": "id"
}
```

### POST /shopee/v1/batch — 3 credits
Up to 20 Shopee products in ONE call, fetched in parallel — the same data as the product action, at a lower per-product cost and in roughly the time a single product takes. Each entry succeeds or fails on its own: one dead id does not spoil the call

**Parameters:**
- `items` (array, required) — Up to 20 products. Each entry is either "<shop_id>/<item_id>" or a full Shopee product link — you can mix the two. A plain comma-separated string works as well as a JSON array. Entries beyond 20 are rejected rather than silently dropped, so a truncated list never looks like a complete answer.
- `market` (enum, optional, default "id") — Which Shopee marketplace to read. The seven listed here are the ones this API is measured on; Shopee also runs Brazil, Mexico, Chile and Colombia, and those are rejected rather than half-served. Ignored when you pass `url` — the market is read from the link. Ids are per-market: the same item_id on a different market is a different product or nothing at all. [one of: id, vn, th, ph, my, sg, tw]

**Returns:** results[] — one row per requested product, in the order you sent them: {input, ok, product{} | error{code,message}}. product{} is byte-for-byte the same object the product action returns, including per-variant stock and shop_vouchers. summary{requested, ok, not_found, unrenderable, failed} totals the outcomes so you can act on the shape of a run without walking every row. Billing counts DELIVERED products only — a row that came back NOT_FOUND or SOURCE_UNRENDERABLE is not charged. meta.record_count is that same delivered count. Failures are per-row and coded exactly as the single-product action codes them, so a mega-shop item in the middle of a list is still reported as unrenderable rather than as a missing product.

### POST /shopee/v1/shop — 1 credit
Seller profile for a Shopee shop by shop id or shop handle — name, description, logo and cover, country and city, listing count, followers, rating with its good/neutral/bad split, response rate and response time, order preparation time, cancellation rate, official-shop and verified-seller status, and when the shop opened. This is also where the product page's missing shop NAME comes from, and it tells you in advance whether a shop's product pages can be read at all

**Parameters:**
- `shop_id` (integer, optional) — Shopee shop id — the FIRST number in a product link, https://shopee.co.id/product/<shop_id>/<item_id>. A value that is not a positive whole number is reported as an invalid parameter before any request is made, so a typo costs nothing.
- `username` (string, optional) — Shop handle instead of shop_id — the name in a shop link, https://shopee.co.id/<username>. Verified to work on every shop endpoint. Give either this or shop_id.
- `market` (enum, optional, default "id") — Which Shopee marketplace to read. The seven listed here are the ones this API is measured on; Shopee also runs Brazil, Mexico, Chile and Colombia, and those are rejected rather than half-served. Ignored when you pass `url` — the market is read from the link. Ids are per-market: the same item_id on a different market is a different product or nothing at all. [one of: id, vn, th, ph, my, sg, tw]

**Returns:** shop{} — market, shop_id, user_id, username, name, description, url, country, location, portrait_url, cover_url, item_count, follower_count, following_count, rating{star,good,normal,bad}, response_rate_percent, response_time_seconds, preparation_time_seconds, cancellation_rate_percent, is_official_shop, is_shopee_verified, is_preferred_plus_seller, is_shopee_choice_shop, on_vacation, status, created_at, updated_at, last_active_at, shop_rating, seller_metrics, additional_fields{}. This merges Shopee's two seller endpoints because NEITHER is a superset of the other — location, country, official-shop status, listing count and the good/neutral/bad rating split come from one, and username, preferred-plus-seller status and the seller-metrics block come from the other. additional_fields{} carries everything else the two return that has no named home here, minus pure interface settings (A/B buckets, tab layout, theme colours). If one of the two endpoints fails the other is still returned, and meta says which answered. product_detail_supported is the useful flag: false means this is one of Shopee's own bulk aggregation storefronts (over 50,000 listings), whose product pages Shopee's renderer refuses to serve — check it once per shop for ~9 KB instead of discovering it product by product. An unknown shop id or handle returns NOT_FOUND.

**Example request body:**
```json
{
  "shop_id": 27651338,
  "market": "id"
}
```

### POST /shopee/v1/shop_showcase — 1 credit
The one product Shopee itself publishes for a shop — WITH a real, live price, discount, rating and sold count. This is the only place a Shopee price is available publicly, and it is shop-addressed, not product-addressed: Shopee picks the item, one per shop. Use it to price-sample a seller, not to price a product you name

**Parameters:**
- `shop_id` (integer, optional) — Shopee shop id — the FIRST number in a product link, https://shopee.co.id/product/<shop_id>/<item_id>. A value that is not a positive whole number is reported as an invalid parameter before any request is made, so a typo costs nothing.
- `username` (string, optional) — Shop handle instead of shop_id — the name in a shop link, https://shopee.co.id/<username>. Verified to work on every shop endpoint. Give either this or shop_id.
- `market` (enum, optional, default "id") — Which Shopee marketplace to read. The seven listed here are the ones this API is measured on; Shopee also runs Brazil, Mexico, Chile and Colombia, and those are rejected rather than half-served. Ignored when you pass `url` — the market is read from the link. Ids are per-market: the same item_id on a different market is a different product or nothing at all. [one of: id, vn, th, ph, my, sg, tw]

**Returns:** item{} — item_id, shop_id, url, name, currency, price, price_min, price_max, price_before_discount, price_min_before_discount, price_max_before_discount, price_raw, price_before_discount_raw, currency_scale, is_price_range, discount_percent, discount_percent_from_prices, discount_label, priced_variant_id, promotion_id, rating{star,count_total,count_by_star,with_text,with_image}, review_count, sold_recent, sold_historical, liked_count, in_stock, stock_raw, images[], variant_options[], category_id, brand, item_status, created_at. ⚠️ READ THIS BEFORE BUILDING ON IT: Shopee returns EXACTLY ONE item per shop and it chooses which one. There is no way to ask for a particular product — passing an item id to this action is rejected rather than quietly ignored, because Shopee ignores it and would return the same single item, which reads like an answer about the product you asked for and is not. Measured: one item on 24 of 24 shops across all seven markets, and 20 of 20 in an earlier pass. When price_min and price_max differ, that is a variant price RANGE on that one item (is_price_range is true), not a second item. Prices are converted from Shopee's fixed-point integers with the divisor named in currency_scale, and the untouched integers are returned as price_raw / price_before_discount_raw so nothing is lost to rounding. The conversion was checked against customer-supplied ground truth and cross-checked against an independent Shopee feed. An item with no strike-through price carries a sentinel (0 or -1) upstream and returns null here, never a zero price. discount_percent is Shopee's OWN percentage-off number, which is what the shopper sees; discount_percent_from_prices is the same figure recomputed from price and price_before_discount. They usually agree within a point and occasionally do not, because Shopee's badge can be based on a different variant or promotion — both are returned so neither is passed off as the other. discount_label is Shopee's raw display string, and its convention is market-dependent: "-46%" or "46%" on most markets, but a Taiwanese 折 multiplier on shopee.tw where "6.6" means you pay 66% of the original (discount_percent for that same item is 34). in_stock is a boolean, NOT a quantity: Shopee publishes only an availability flag on this route — the underlying number was a constant 1 on 34 of 34 shops across all seven markets — and it is returned as a flag rather than dressed up as "1 left". stock_raw carries the untouched value. Per-variant stock QUANTITIES are available from the product action. sold_recent is Shopee's recent-sales counter and sold_historical is the lifetime figure, which Shopee visibly rounds into buckets (1,000 / 10,000 / 30,000) — both are passed through exactly as Shopee publishes them. An unknown shop returns NOT_FOUND.

**Example request body:**
```json
{
  "shop_id": 27651338,
  "market": "id"
}
```

## More
- Try it live, no code: https://reefapi.com/playground?engine=shopee
- Human docs page: https://reefapi.com/docs/shopee
- Every ReefAPI API in one file (for your AI): https://reefapi.com/llms-full.txt
