# Joybuy scraper 🛍️ (`borderline/joybuy-api`) Actor

Extract full product data from Joybuy (JD.com's European marketplace — FR, DE, BE, NL, LU, UK). Get prices, discounts, variants, ratings, reviews, Q\&A, specs, images and delivery estimates by product URL, search URL or keyword. Fast structured JSON

- **URL**: https://apify.com/borderline/joybuy-api.md
- **Developed by:** [borderline](https://apify.com/borderline) (community)
- **Categories:** Automation, Developer tools, E-commerce
- **Stats:** 16 total users, 1 monthly users, 100.0% runs succeeded, 2 bookmarks
- **User rating**: 5.00 out of 5 stars

## Pricing

from $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.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

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

## What's an Apify Actor?

Actors are web data automations that power AI and operations. They run on the Apify platform to scrape websites, process data, connect APIs, and automate workflows.
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.
Actors are written with capital "A".

## 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.
The best way to integrate Actors is as follows.

- **AI agents and MCP clients** — the [Apify MCP server](https://docs.apify.com/integrations/mcp.md) at `https://mcp.apify.com` (remote, streamable HTTP, OAuth on first use).
- **Agentic workflows and local Actor development** — [Agent Skills](https://apify.com/.well-known/agent-skills/index.json) with the [Apify CLI](https://docs.apify.com/cli/docs.md): `npm install -g apify-cli`, then `apify login`.
- **JavaScript/TypeScript projects** — the official [JS/TS client](https://docs.apify.com/api/client/js/docs.md): `npm install apify-client`.
- **Python projects** — the official [Python client](https://docs.apify.com/api/client/python/docs.md): `pip install apify-client`.
- **Any other language** — the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).

# README

## Joybuy Scraper 🛒

Extract full product data from **[Joybuy](https://www.joybuy.fr)** — JD.com's European marketplace (FR, DE, BE, NL, LU, UK) — from product URLs, search URLs, or plain keywords. Prices, variants, ratings, reviews, Q\&A, descriptions, specs and delivery estimates, as clean JSON.

***

### What you get

For every product:

- 🏷️ **Title, brand, model, category path** (with Joybuy's own category ids)
- 💶 **Price, list price, discount %, currency** — localized to a delivery address
- 🎟️ **Promotions and coupons**, with the campaign's start and end
- 📦 **Stock status**, **purchase limits**, and **delivery estimate**
- 🚚 **Shipping cost** — what it would cost, what you actually pay, and whether a threshold makes it free
- 🎨 **Variants** (storage, colour…) — each with its own sku, swatch image, and whether it can actually be bought
- ⭐ **Rating, review count, good-rating %, star histogram**, plus Joybuy's AI review summary
- 🖼️ **Full image gallery + videos**
- 📐 **Shipping weight and box dimensions**
- 🛡️ **Guarantees, warranty, protection plans** and **sold-count label** ("5k+ sold")
- ⚡ **EU energy labels** and product-information sheets
- 🏪 **Seller**, instalment terms, and Joybuy's own share link
- 📝 **Reviews** (text, stars, date, photos) — on by default
- ❓ **Customer questions & answers** — on by default
- 📄 **Description, specification table, "important information" images** — on by default
- 🧭 **Provenance** — which input produced each row, its rank in the search, and whether the result was an ad

Each product is one dataset item. Reviews and questions are attached to their product — and the Output tab has ready-made views: **🛍️ Products**, **⭐ Reviews** (one row per review), **❓ Questions**, and **📝 Descriptions & specs**.

***

### Why this Actor

- **Purpose-built for Joybuy** — every field is read from Joybuy's own data layer, so rows come back complete and consistently shaped.
- **Full product data** — price and discounts, variants, rating, reviews, Q\&A, specifications, images, delivery estimate.
- **Fast** — structured extraction, no browser to drive.
- **Localized** — pick the country site and, optionally, a delivery postcode: prices and delivery estimates match that address.
- **Zero setup** — no configuration, no login, no tokens.
- **Maintained** — kept working as the site changes.

***

### How to use

1. Click **Try for free**.
2. Paste **product URLs**, **search URLs**, or type **keywords** (e.g. `iphone`, `shoes`).
3. Narrow it down if you want — **price range**, **brand**, **minimum rating**, **in stock only**, **bestsellers**, **lightning offers**, **fast delivery**, and a **sort order**.
4. Pick your **country** (FR by default), optionally a **delivery postcode**, and run.

No setup, no login, no tokens — it just runs.

#### Input example

```json
{
  "startUrls": [
    { "url": "https://www.joybuy.fr/dp/apple-iphone-17-pro-max-512/10409186" }
  ],
  "keywords": ["shoes", "coffee grinder"],
  "maxItemsPerInput": 100,
  "minPrice": 20,
  "maxPrice": 200,
  "inStockOnly": true,
  "country": "FR",
  "sortType": "sort_dredisprice_asc",
  "postCode": "75001",
  "scrapeReviews": true,
  "maxReviews": 10,
  "scrapeQuestions": true,
  "scrapeDescription": true
}
```

#### Input options

| Field | What it does |
|---|---|
| `startUrls` | Joybuy product URLs (`/dp/…/{id}` or `/dg/{id}`) **and** search URLs. Search filters in the URL (sort, price, category…) are respected. |
| `keywords` | Search terms, one per line — each is searched separately. |
| `maxItemsPerInput` | Cap of products per URL / per keyword (default 50, `0` for everything). |
| `minPrice` / `maxPrice` | Keep only products inside a price range. |
| `brand` | Keep only one brand, e.g. `Apple`. |
| `minRating` | Keep only products at or above 2, 3 or 4 stars. |
| `inStockOnly` | Skip products Joybuy reports as out of stock. |
| `bestsellersOnly` | Keep only Joybuy's bestseller-ranked products. |
| `lightningOffersOnly` | Keep only products in a lightning offer. |
| `fastDeliveryOnly` | Keep only JoyExpress fast-delivery products. |
| `sortType` | Sort keyword searches: relevance, price ↑, price ↓. |
| `country` | `FR` · `DE` · `BE` · `NL` · `LU` · `GB`. Sets currency and region automatically. |
| `language` | Read any catalogue in English, French, German or Dutch. |
| `postCode` | Optional delivery location → localized price & delivery estimate. |
| `latitude` / `longitude` | Optional GPS, for a more precise delivery location. |
| `scrapeReviews` / `maxReviews` | Customer reviews, up to `maxReviews` per product (default **on**, 10; billed per review — raise it and the run costs more). |
| `scrapeQuestions` / `maxQuestions` | Customer Q\&A, up to `maxQuestions` per product (default **on**, 5; billed per question). |
| `scrapeDescription` | Description, spec table and info images (default **on**; billed separately). |
| `includePersonalData` | Include reviewer display names (on by default; set `false` to redact). |

Provide at least one of `startUrls` or `keywords`.

***

### Output example

One dataset item per product (trimmed — a real record carries 50+ fields):

```json
{
  "skuId": "10409338",
  "url": "https://www.joybuy.fr/dp/p/10409338",
  "title": "Apple iPhone 17 256 Go - Noir",
  "brand": "Apple",
  "model": null,
  "categoryPath": ["Électronique", "Téléphones", "Smartphones"],
  "categoryIds": ["1849", "5202", "5203", "5204"],
  "price": { "value": 889, "currency": "EUR" },
  "priceWas": { "value": 969, "currency": "EUR" },
  "discountPercent": 8,
  "promotions": [
    { "id": "80004353824", "type": "1", "price": { "value": 889, "currency": "EUR" },
      "startsAt": "1 juil. 2026, 10:01:22", "endsAt": "31 août 2026, 23:59:59" }
  ],
  "coupons": [],
  "unitPrice": null,
  "installmentText": "Paiement en 3 versements de €296.33",
  "inStock": true,
  "purchaseLimit": 1,
  "purchaseLimitText": "Limité à 1 articles",
  "minPurchaseQuantity": 1,
  "delivery": {
    "promise": "Livraison dès 25 août",
    "shippingText": "Frais d'expédition estimés €3.99 GRATUIT",
    "pickup": null
  },
  "freight": {
    "was": { "value": 3.99, "currency": "EUR" },
    "now": { "value": 0, "currency": "EUR" },
    "isFree": true,
    "text": "Frais d'expédition estimés €3.99 GRATUIT"
  },
  "fulfillmentMethods": ["Livraison", "Retrait"],
  "variants": [
    { "name": "Série", "value": "512GB", "skuId": "10409334", "selected": false, "available": null, "image": null },
    { "name": "Couleur", "value": "Noir", "skuId": "10409338", "selected": true, "available": true,
      "image": "https://images3.joy-sourcing.com/…" },
    { "name": "Couleur", "value": "Sauge", "skuId": "10409262", "selected": false, "available": false,
      "image": "https://images3.joy-sourcing.com/…" }
  ],
  "rating": 5,
  "reviewCount": 282,
  "goodRatePercent": 100,
  "starsBreakdown": { "5 star": 270, "4 star": 9, "1 star": 1 },
  "aiReviewSummary": "Buyers praise the screen and battery life…",
  "soldLabel": "5k+ vendus",
  "seller": "Joybuy",
  "sellerId": null,
  "venderId": "100000101",
  "spuId": "481553",
  "weightGrams": 330,
  "dimensions": { "length": 169, "width": 89, "height": 25 },
  "warranty": null,
  "guarantees": ["Retours gratuits sous 30 jours", "Assistance clientèle 24h/24 et 7j/7"],
  "protectionPlans": [
    { "name": "2 ans Dommage accidentel", "price": { "value": 35.08, "currency": "EUR" } },
    { "name": "3 ans Dommage accidentel, Extension de garantie et Vol", "price": { "value": 72.94, "currency": "EUR" } }
  ],
  "energyLabelImages": ["https://images3.joy-sourcing.com/…"],
  "energyLabelUrls": ["https://m.joybuy.fr/dg-comply?tabKey=productSheet&skuId=10409338"],
  "images": ["https://images3.joy-sourcing.com/…"],
  "videos": [],
  "thumbnailImage": "https://images3.joy-sourcing.com/…",
  "specifications": { "Système d'exploitation": "iOS", "Taille de l'écran": "6.3", "5G": "Oui" },
  "description": "…",
  "descriptionHtml": "<div>…</div>",
  "importantInfoImages": ["https://…/info-1.jpg"],
  "shareLink": "https://m.joybuy.fr/landingPage/dp/10409338?siteCode=FR-Site",
  "reviews": [
    { "stars": 5, "content": "Excellent téléphone…", "userNickName": "JD.com Client", "date": "2026-01-14", "usefulCount": 3 }
  ],
  "questions": [
    { "question": "Le chargeur est-il inclus ?", "answer": "Non, vendu séparément.", "askerNick": "…", "answerCount": 2 }
  ],
  "isSponsored": false,
  "country": "FR",
  "scrapingInfo": { "input": "iphone", "inputType": "keyword", "searchPage": 1, "searchIndex": 1 },
  "crawledAt": "2026-08-24T09:12:00.000Z"
}
```

> For a third-party listing, `seller` is the shop name (e.g. `"d-power"`) and `sellerId` its shop id.

Export as JSON, CSV, Excel, or pull it via the Apify API.

***

### Common use cases

- **Price & promo monitoring** — track prices, discounts, coupons and shipping cost across Joybuy, per delivery region.
- **Competitor & market research** — catalogue, ratings, sales signals ("5k+ sold").
- **Product feeds & catalogues** — titles, images, specs, variants for your own store or comparison site.
- **Review & reputation analysis** — ratings distribution, review text, Q\&A.
- **Delivery/availability checks** — delivery promise, shipping cost and per-variant availability by postcode.
- **Compliance & catalogue data** — EU energy labels and product-information sheets, weight and box dimensions.

***

### Pricing

Pay per result — you're charged only for what you actually get:

| Event | Free & Starter | Scale | Business and above |
|---|---|---|---|
| Product (full record) | **$4.99** | $4.49 | **$3.99** |
| Description + specs | $1.49 | $1.19 | $0.99 |
| Review | $2.29 | $2.09 | $1.99 |
| Question | $1.99 | $1.49 | $0.99 |
| Actor start | $0.02 / run | $0.02 / run | $0.02 / run |

Prices per 1,000, and they fall with your Apify plan — the bigger the plan, the lower the rate, automatically.

**What a run actually costs.** Only `product` is always charged — everything else depends on what you switch on:

| Setup | Free & Starter | Scale | Business and above |
|---|---|---|---|
| Products only (reviews, Q\&A and description off) | **$4.99** | $4.49 | **$3.99** |
| Product + description & specs | $6.48 | $5.68 | $4.98 |
| Default run — description on, up to 10 reviews and 5 questions per product | up to $39.33 | up to $34.03 | up to $29.83 |

That last figure is a ceiling, not a flat rate: reviews and questions are billed one by one, so a product with 3 reviews bills 3 — you're never charged for something Joybuy didn't return. `maxReviews` is the biggest lever on what a run costs; lower it, or turn reviews off, to bring the bill down.

No monthly fee, proxies included. (The platform pricing tab shows your plan's exact rate.)

***

### Good to know

- **Country & currency**: choose the `country` matching the Joybuy site you want — pricing, stock and delivery all differ per region, and each country is priced from an address in that country. Routing is selected automatically to match.
- **How much per keyword?** Everything Joybuy paginates for that term — a few hundred products for a narrow keyword, up to **~1,800** for a broad one like `chaussures`. Set `maxItemsPerInput` to keep a run to the size you want; `0` takes the lot. Product URLs have no limit at all.
- **A URL decides its own market.** `country` picks the storefront a run reads, but a start URL from a different one (a `joybuy.fr` link on a `LU` run) is read from **its own** market, because a catalogue only carries its own products — the log says when this happens. Belgium, the Netherlands and Luxembourg share one storefront, so a `joybuy.nl` link keeps whichever of the three you picked. To read a product from another country, paste its **sku id** instead of the full URL. Every row carries the market it was read from, shown as **Market** in the Products view. With a `postCode` set the run refuses the mix instead of redirecting — an address from one market means nothing on another.
- **Reviews/Q\&A/description** are optional (extra requests) — enable only what you need to keep runs fast and cheap.
- **No login required** — only publicly visible product data is collected.

***

### FAQ

**Do I need proxies or an API key?** No. Networking and country routing are handled inside the Actor.

**Can I scrape a specific product?** Yes — paste its URL (or just its numeric id) into `startUrls`.

**Can I keep the filters from a Joybuy search URL?** Yes. Paste the full search URL (with sort, price range, category…) and the Actor applies them.

**Which countries?** France, Germany, Belgium, Netherlands, Luxembourg, United Kingdom — all six fully, including keyword search.

**Is it legal?** The Actor collects only publicly accessible product information and requires no login. You are responsible for how you use the data (e.g. GDPR for reviewer names — use `includePersonalData: false` to exclude them).

***

### Support

Found a missing field or a product that didn't parse? Open an issue on the Actor's page — response is fast, and the Actor is actively maintained.

# Actor input Schema

## `startUrls` (type: `array`):

Joybuy product pages (<code>/dp/{slug}/{skuId}</code> or <code>/dg/{skuId}</code>) and search result pages. <b>Filters in a search URL are honoured</b> — sort order, price range, category, brand and page all carry over. Tracking parameters are ignored. A bare sku id works too.

## `keywords` (type: `array`):

Search terms, one per line. Each keyword is searched and the resulting products are scraped, up to <b>Max results</b>.

## `maxItemsPerInput` (type: `integer`):

Upper bound on products scraped for <b>each</b> start URL or keyword, so one broad keyword cannot starve the others. Set <code>0</code> for no cap — the run then takes everything Joybuy returns, which can be <b>1,800 products for a broad keyword</b>, all of them billed.

## `sortType` (type: `string`):

Sort order applied to <b>keyword</b> searches. Search URLs keep their own <code>sortType</code> from the URL.

## `scrapeDescription` (type: `boolean`):

Fetch the product description, specification table and 'important information' images. Adds one extra request per product; it can occasionally come back empty, in which case the rest of the product is unaffected. Enabled by default.

## `minPrice` (type: `integer`):

Skip products cheaper than this, in the site currency.

## `maxPrice` (type: `integer`):

Skip products more expensive than this, in the site currency.

## `brand` (type: `string`):

Keep only products of this brand (exact name, case-insensitive), e.g. <code>Apple</code>. Products Joybuy lists without a brand of their own are dropped, since they cannot be shown to match. Joybuy has no brand filter of its own, so this one runs over the search results, which makes the run walk further.

## `minRating` (type: `string`):

Keep only products rated at or above this many stars. Joybuy's own search does not offer a rating filter that works, so this is applied on our side — products that list no rating are kept.

## `inStockOnly` (type: `boolean`):

Skip products Joybuy reports as out of stock.

## `bestsellersOnly` (type: `boolean`):

Keep only products in Joybuy's bestseller ranking. Applied by Joybuy's own search, so it cannot be double-checked on our side the way price or brand can.

## `lightningOffersOnly` (type: `boolean`):

Keep only products carrying a lightning-offer deal. Applied by Joybuy's own search.

## `fastDeliveryOnly` (type: `boolean`):

Keep only products eligible for JoyExpress fast delivery. Applied by Joybuy's own search (measured: it cuts a 297-result search to 235).

## `scrapeReviews` (type: `boolean`):

Include customer reviews on each product. Reviews are billed separately.

## `maxReviews` (type: `integer`):

Upper bound on reviews collected per product. Reviews are paginated, so a high value is honoured — Joybuy itself stops at 1,000 per product. This is the single biggest driver of run cost — every review is billed on its own, so doubling this roughly doubles what a run costs.

## `scrapeQuestions` (type: `boolean`):

Include the customer Q\&A shown on the product page.

## `maxQuestions` (type: `integer`):

Upper bound on customer questions collected per product, when Scrape questions is on. Paginated the same way as reviews.

## `includePersonalData` (type: `boolean`):

Reviewer and question-author display names are public on Joybuy's product pages. Included by default; set to false to redact them (returned as <code>null</code>). The hashed account id, avatars and order ids are never collected either way.

## `country` (type: `string`):

The Joybuy site to read from: currency, pricing and delivery follow the country you pick. Belgium, the Netherlands and Luxembourg share Joybuy's Dutch storefront (NL-Site). Everything works on all six — search, product URLs, reviews, descriptions. A start URL from another storefront is read from <b>its own</b> market instead — a catalogue only carries its own products — and the run log says so; pass the bare sku id rather than the full URL to read a product from the country you picked here.

## `language` (type: `string`):

Language for titles, specifications and delivery wording. Defaults to the country's own language — pick English to read a French, German or Dutch catalogue in English. Each site only serves some languages; an unsupported one falls back to the site default.

## `postCode` (type: `string`):

Postcode used as the delivery address, so prices and delivery times match it. Leave empty to use the site default. It must belong to the <code>country</code> you picked: a start URL from another storefront is refused while a postcode is set, since the address would mean nothing on that market.

## `latitude` (type: `string`):

Optional GPS latitude for a more precise delivery location.

## `longitude` (type: `string`):

Optional GPS longitude for a more precise delivery location.

## Actor input object example

```json
{
  "startUrls": [
    {
      "url": "https://www.joybuy.fr/dp/apple-iphone-17-pro-max-512/10409186"
    }
  ],
  "keywords": [
    "iphone 17"
  ],
  "maxItemsPerInput": 50,
  "sortType": "sort_default",
  "scrapeDescription": true,
  "minRating": "0",
  "inStockOnly": false,
  "bestsellersOnly": false,
  "lightningOffersOnly": false,
  "fastDeliveryOnly": false,
  "scrapeReviews": true,
  "maxReviews": 10,
  "scrapeQuestions": true,
  "maxQuestions": 5,
  "includePersonalData": true,
  "country": "FR",
  "language": ""
}
```

# Actor output Schema

## `products` (type: `string`):

Scraped products: title, price, variants, rating, reviews, Q\&A, description, delivery and provenance. One row per sku.

# 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 = {
    "startUrls": [
        {
            "url": "https://www.joybuy.fr/dp/apple-iphone-17-pro-max-512/10409186"
        }
    ],
    "keywords": [
        "iphone 17"
    ],
    "maxItemsPerInput": 50
};

// Run the Actor and wait for it to finish
const run = await client.actor("borderline/joybuy-api").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 = {
    "startUrls": [{ "url": "https://www.joybuy.fr/dp/apple-iphone-17-pro-max-512/10409186" }],
    "keywords": ["iphone 17"],
    "maxItemsPerInput": 50,
}

# Run the Actor and wait for it to finish
run = client.actor("borderline/joybuy-api").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 '{
  "startUrls": [
    {
      "url": "https://www.joybuy.fr/dp/apple-iphone-17-pro-max-512/10409186"
    }
  ],
  "keywords": [
    "iphone 17"
  ],
  "maxItemsPerInput": 50
}' |
apify call borderline/joybuy-api --silent --output-dataset

```

## MCP server setup

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

```

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/eMLme0c6Wx9hQEjDI/builds/bxOAFvCf0aweNnfh7/openapi.json
