# Coupang (쿠팡) Product Search Scraper - Prices & Ratings (`lanternlab318/coupang`) Actor

Coupang product listings by keyword: product ID, title, URL, image, price, availability, rating, review count and rank. A keyword Coupang has no match for is reported as no_match instead of being filled with recommended products.

- **URL**: https://apify.com/lanternlab318/coupang.md
- **Developed by:** [Lantern Lab](https://apify.com/lanternlab318) (community)
- **Categories:** E-commerce
- **Security:** Limited permissions
- **Stats:** 2 total users, 1 monthly users, 85.7% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

$3.99 / 1,000 products

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#pay-per-event

## What's an Apify Actor?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

## Coupang Product Search Scraper

A Lantern Lab Actor. Give it Korean or English keywords and get Coupang's public product
listings back: product ID, title, URL, image, **price**, availability, **rating**, review
count and the rank Coupang listed it at. No Coupang login, no cookies.

**A keyword with no match is reported as having no match.** Coupang does not say "no
results" — it fills the page with recommended products instead, and that page is otherwise
indistinguishable from a page of real results. This Actor reads the page's own filter
facets to tell the two apart and **discards the recommendations**, so you never get
unrelated products delivered as matches for your keyword. When the evidence is missing it
says `unknown` and writes nothing rather than guessing.

**Price:** you pay per product collected. Keyword status rows and the run summary are
free, and so is a keyword that matched nothing or could not be finished.

### Quick example

Input:

```json
{
  "searchQueries": ["생수 2L 24개", "무선 이어폰"],
  "maxProductsPerQuery": 60
}
```

One product row (`recordType: "product"`):

```json
{
  "recordType": "product",
  "productId": "6239512369",
  "itemId": "18743957351",
  "vendorItemId": "86494124999",
  "title": "풀무원샘물 무라벨, 2L, 24개",
  "url": "https://www.coupang.com/vp/products/6239512369?itemId=18743957351&vendorItemId=86494124999",
  "imageUrl": "https://thumbnail.coupangcdn.com/thumbnails/remote/320x320ex/image/retail/images/65619175192380-c9d413ea-25b9-439c-949f-fa721087d86a.jpg",
  "price": 13500,
  "currency": "KRW",
  "availability": "InStock",
  "rating": 5,
  "reviewCount": 256375,
  "query": "생수 2L 24개",
  "rank": 1,
  "searchPage": 1,
  "partial": false,
  "partialReason": null,
  "collectedAt": "2026-10-24T05:41:08.112Z"
}
```

One keyword row (`recordType: "query_status"`, free) for a keyword Coupang has no match for:

```json
{
  "recordType": "query_status",
  "query": "qwzxlk9981nonexistentproduct",
  "outcome": "no_match",
  "productsReturned": 0,
  "productsCharged": 0,
  "productsOnPage": null,
  "titleKeywordCoverage": 0,
  "matchEvidence": {
    "found": true,
    "categoryTabs": 0,
    "priceRanges": 0,
    "ratingFilters": 0,
    "brands": 0,
    "attributeFilters": 0,
    "offerConditions": 0,
    "deliveryFiltersEnabled": 0
  },
  "partial": false,
  "statusText": "Coupang has no product matching this keyword. It answered with a page of recommended products instead, which this Actor discards: they are not results for your keyword, so no row was written and nothing was charged. This is an empty result, not a failed one."
}
```

### Limits

- **The rating is rounded.** Coupang's search listing rounds a product's rating to the
  nearest 0.5. Cross-checked against the products' own pages twice, 11 products in total:
  **0 matched exactly**, every difference was 0.1-0.3. Use the product's own page when you
  need the rating to a decimal.
- **`price` is the listing price, and the product page can show a lower one.** Coupang
  shows one price in search results and applies card or coupon discounts on the product
  page. In 3 of 5 products cross-checked, the product page's headline price was 3-15 %
  below the listing price - and in all 3, the listing price was exactly the product page's
  own reference price (`priceSpecification`). So `price` is what a buyer sees in the search
  results, which is the right number to compare listings with, and not necessarily what
  they pay at checkout.
- **`reviewCount` is exact, and it moves.** It matched the product page on 9 of 11
  products cross-checked; the two differences were +1 and +2 reviews added between the two
  reads, minutes apart. Treat it as the count at `collectedAt`.
- **No original price, discount, seller or Rocket delivery badge.** A Coupang search
  listing does not carry them for an ordinary result — only for the promotional carousel,
  and not on every page — so this Actor does not offer those fields at all rather than
  offering a column that is almost always null.
- **One page per keyword, 24-57 products.** Coupang server-renders results onto the first
  page of a search only; `&page=2` and later answer with a 0.14 MB promotional shell that
  carries no product list. When `maxProductsPerQuery` is above what the page holds, the
  keyword's status row says `pageLimitReached: true` — Coupang's ceiling, not yours.
- Coupang lists the same product more than once on one page — often 30 or more repeats of
  a 55-entry list. Each `productId` is written once, at the first rank it appeared at, so
  it is charged once, and `productsOnPage` counts the unique products the page offered.
- **20-30 seconds per keyword**, and the anti-bot layer answers a share of requests with
  HTTP 502 or with a page that is not the whole search page. Both are retried, up to 5
  attempts per keyword. A page that never arrives whole is reported as `upstream_error`
  and never parsed: a short search page has the product list but not the match evidence,
  which is the one shape that could pass unrelated products off as results. Keywords are
  collected one after another.
- **Coupang throttles its listing pages after sustained use.** Measured 2026-10-24: after
  about 70 search requests from one account the search and category pages answered 502
  through the proxy for tens of minutes - while coupang.com's own home page kept answering
  200 in under 3 seconds, so it is the listing endpoints and not the proxy. Keywords hit by
  it are reported `upstream_error`, never as having no products. Spread large keyword lists
  over several runs rather than one.
- Public listing data only. No login, no cookies, no account. No seller contact details,
  no buyer or reviewer data, no product reviews, no product detail pages.
- Prices and ratings are what the listing showed at `collectedAt`; Coupang changes them
  during the day.
- No scheduling or monitoring built in; use Apify schedules.

### How a keyword with no match is caught

This is the one thing worth reading before you rely on the output. Coupang's answer to a
keyword it has no product for is a normal-looking search page: same document shape, same
JSON-LD product list, 25-57 recommended products in it, and the Korean "no results"
sentence present only as an i18n dictionary entry that is on **every** page, matched or
not. Counting results does not help either — the page's own telemetry reports the same
constant on every page.

What does separate them is the filter sidebar, because Coupang computes it from the result
set and not from the recommendations it padded the page with. A matched keyword is offered
category tabs for the categories its results fall in, price buckets, star filters, brand
names, attribute filters and at least one selectable delivery filter. An unmatched keyword
is offered the "all" tab and nothing else, with every delivery filter disabled.

So every keyword's status row carries `outcome` plus the `matchEvidence` it was decided
from:

| `outcome` | What it means | Products written |
| --- | --- | --- |
| `matched` | The page offers facets derived from a result set. | yes, charged |
| `no_match` | The facets are there and all empty: Coupang has no match, and the products on the page are its recommendations. | no, free |
| `unknown` | The facets were not found, so whether the products are results is unknown. `partial: true`, `partialReason: "match_unverified"`. | no, free |
| `failed` | The page could not be read, or Coupang served a different search. `partial: true`. | no, free |

`titleKeywordCoverage` is reported next to that as a second, independent number: how much
of your keyword appears in the returned titles, averaged over the products. It is not what
decides the outcome — Korean compounds written without spaces keep it well under 1 even on
a good result set — but it lets you judge the result set yourself.

### What it does

- Searches Coupang for each keyword in order, one request per keyword.
- Returns the listing fields Coupang states: `productId`, `title`, `url`, `imageUrl`,
  `price`, `currency`, `availability`, `rating`, `reviewCount`, `rank`, plus Coupang's
  `itemId` and `vendorItemId` for the offer the listing links to.
- Tells a matched keyword from an unmatched one from the page's own filter facets, and
  discards the recommendations Coupang pads an unmatched keyword's page with.
- Checks that the page Coupang served is the search that was asked for, and marks the
  keyword `wrong_page` instead of writing another keyword's products.
- Deduplicates across the whole run: a product that matches two of your keywords is
  written and charged once, and the second keyword's row says how many it had already seen.
- Keeps the source identifiers (`productId`, `itemId`, `vendorItemId`) and our own
  collection time (`collectedAt`) as separate fields. A search listing states no publish
  time for a product, so there is none to report.
- Says what happened to every keyword. A keyword with no match, a keyword that could not be
  judged and a keyword that was never reached are different records and are never confused.
- Gets past Coupang's Akamai bot protection through Apify's Unblocker proxy with a Korean
  exit, and retries a refused or failed page up to 4 times with a short wait in between.
- Caps itself: `maxUpstreamRequests` is a hard ceiling on requests to Coupang for the whole
  run, retries included, derived from your input when you leave it empty, so a run is never
  unbounded.

### Input

| Field | Default | Meaning |
| --- | --- | --- |
| `searchQueries` | — | Keywords to search for, one per line, Korean or English. |
| `maxProductsPerQuery` | `60` | Upper bound on products per keyword. Coupang's own page holds 24-57. |
| `proxyConfiguration` | Apify Proxy, `UNBLOCKER`, `KR` | Leave at the default: nothing else is measured to get past Coupang's edge. |
| `maxUpstreamRequests` | derived | Hard ceiling on requests to Coupang for the whole run, retries included. |

### Output

One row per product, `recordType: "product"`, charged:

| Field | Meaning |
| --- | --- |
| `productId` | Coupang's product ID — the dedupe key. |
| `itemId`, `vendorItemId` | Coupang's identifiers for the offer the listing links to. |
| `title`, `url`, `imageUrl` | The listing's title, product page link and thumbnail. |
| `price`, `currency` | The price shown in the listing, as a number, and `KRW`. |
| `availability` | Coupang's schema.org availability, for example `InStock`. |
| `rating`, `reviewCount` | Average rating **rounded by Coupang to the nearest 0.5**, and the exact review count. |
| `query`, `rank`, `searchPage` | Which search produced the row, its position from 1, and the page it came from (always 1). |
| `partial`, `partialReason` | Always `false` / `null` on a product row: the row exists because the product was collected from a page whose results were established to be results. |
| `collectedAt` | Our own collection time. |

One row per keyword, `recordType: "query_status"`, free: `outcome` (`matched`, `no_match`,
`unknown`, `failed`), `matchEvidence`, `titleKeywordCoverage`, `keywordServed` (the keyword
the page says it answered), `productsReturned` / `productsCharged` / `duplicatesSkipped` /
`productsOnPage`, `pageLimitReached`, and `partial` / `partialReason`
(`match_unverified`, `wrong_page`, `upstream_request_cap`, `charge_limit_reached`,
`blocked`, `upstream_error`, `not_collected`) with `statusText`.

The last row is `recordType: "RUN_SUMMARY"` — what the keywords resolved to, products
collected and charged, keywords with no match, partial keywords by reason, requests sent
(retries included) and why collection stopped. The same object is also in the run's
key-value store under `RUN_SUMMARY`.

# Actor input Schema

## `searchQueries` (type: `array`):

Keywords to search Coupang for, one per line, Korean or English. A keyword Coupang has no product for gets its own status row with outcome "no_match" and is not charged: Coupang answers such a keyword with a page of recommended products, and this Actor discards them instead of reporting them as results for your keyword.

## `maxProductsPerQuery` (type: `integer`):

Upper bound on the products collected for each keyword. Coupang serves results on the first page of a search only - any later page answers with a promotional shell carrying no products - so one keyword yields what that page holds, measured between 24 and 57 unique products. When your limit is above that, the keyword's status row says pageLimitReached: true, meaning Coupang's ceiling ended the list and not yours.

## `proxyConfiguration` (type: `object`):

Leave this at the default. Coupang is behind Akamai: datacenter IPs and residential IPs - Korean exits included - are refused at the edge with HTTP 403 before any page loads, so the Apify Proxy UNBLOCKER group with a Korean exit is the only configuration measured to work. Any other choice is very likely to collect nothing, and the run logs a warning saying so.

## `maxUpstreamRequests` (type: `integer`):

Hard ceiling on the HTTP requests this run sends to Coupang, retries included. At the ceiling collection stops - the keywords not reached are written with partialReason "upstream_request_cap" and the run then ends normally. Leave empty and the Actor works a ceiling out from your input, so a run is never unbounded.

## Actor input object example

```json
{
  "searchQueries": [
    "생수 2L 24개",
    "무선 이어폰"
  ],
  "maxProductsPerQuery": 60,
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "UNBLOCKER"
    ],
    "apifyProxyCountry": "KR"
  }
}
```

# Actor output Schema

## `results` (type: `string`):

No description

## `runSummary` (type: `string`):

No description

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "searchQueries": [
        "생수 2L 24개",
        "무선 이어폰"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("lanternlab318/coupang").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = { "searchQueries": [
        "생수 2L 24개",
        "무선 이어폰",
    ] }

# Run the Actor and wait for it to finish
run = client.actor("lanternlab318/coupang").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print(f"💾 Check your data here: https://console.apify.com/storage/datasets/{run.default_dataset_id}")
for item in client.dataset(run.default_dataset_id).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "searchQueries": [
    "생수 2L 24개",
    "무선 이어폰"
  ]
}' |
apify call lanternlab318/coupang --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,lanternlab318/coupang"
        }
    }
}
```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/jCi29MODkY9i0mdof/builds/HaSHYFaj3pEbPpnAg/openapi.json
