# Dhgate Scraper (`normdata/dhgate-scraper`) Actor

Source wholesale products from DHgate with real tiered pricing, MOQ, supplier feedback score, and active promo discounts - everything an importer needs to compare suppliers fast, in one search. 26 real fields per product, filters that actually work.

- **URL**: https://apify.com/normdata/dhgate-scraper.md
- **Developed by:** [Norm Data](https://apify.com/normdata) (community)
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $6.80 / 1,000 results

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?

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

![Norm Data](https://i.ibb.co/rGbhM5Y8/Chat-GPT-Image-Sep-8-2026-02-20-50-PM.png)

## 🛒 DHgate Scraper

Search **DHgate** wholesale listings with no login: real tiered pricing, minimum order quantity, free-shipping status, supplier store, feedback percent, recently-sold count, and active promotions, 26 fields per product from a single search.

### 🎯 Who uses it?

#### 📦 Importers & dropshippers

Compare tiered wholesale pricing and minimum order quantity across suppliers for a product category before committing to an order.

#### 📉 Price monitors

Track a product line's real price range and active promotions over time.

#### 🔍 Supplier vetters

Use feedback percent, tier level, and recently-sold count as a first-pass signal before reaching out to a supplier directly.

### ✨ What it does

Reads DHgate's own real search results directly and normalizes them into clean, flat rows.

- Search by one or more keywords, each getting its own fair share of the row cap.
- Filter by price range and free shipping, and sort by price (low to high or high to low).
- Every row carries real wholesale pricing (a genuine per-unit range, not a single display price), minimum order quantity, supplier signals (feedback percent, tier level, recently-sold count), and any currently active promotion with its real discount and date window.

Missing source values are returned as `null`, never invented.

### Why this scraper

- **26 real fields per product from one search**, more than double a typical competitor's field count, with no separate paid detail call needed to get them.
- **A price filter that's actually correct, not just narrower.** Every row is re-checked against its own real parsed price, so the price bounds mean what they say.
- **Sorting that actually sorts**, verified to return results in the order requested rather than passed through blindly.
- **Fair multi-keyword budgeting.** Search several keywords in one run and each gets its own real share of the row cap, so one fast keyword can't consume the whole budget and leave the others at zero.
- **Reaches further than a single flat search.** A plain keyword search on DHgate tops out well short of its full catalog for that term; this Actor keeps going to reach well beyond that ceiling.
- **No API key, no login, no browser, no proxy needed.**

### How it compares

Checked directly against the closest real competitor on Apify (their own declared input and output); DHgate scrapers overall are fragmented across 13+ actors, none with more than 3 users:

| Capability | This actor | Closest competitor |
|---|:--:|:--:|
| Fields per product | **26** | ~20 |
| Supplier signals (feedback %, tier level, recently sold) | **yes** | partial |
| Active promotion detail (discount %, start/end date) | **yes** | no |
| Price filter that's verified correct, not just narrower | **yes** | not verified |
| Working price sort | **yes** | not verified |
| Fair budget split across multiple keywords | **yes** | not documented |

### 📦 What data you get

| Entity | Useful fields |
| --- | --- |
| Product | Item code, title, URL, image, and the keyword that found it. |
| Pricing | Real tiered price range, currency, minimum order quantity and unit. |
| Shipping & promotions | Free-shipping flag, and any active promotion's discount and date window. |
| Supplier signals | Feedback percent, recently-sold count, review count, rating, and tier level. Not every field is populated on every listing, a real data gap in the source, not ours. |
| Supplier identity | Supplier ID, domain, and store URL. |
| Listing flags | Whether the listing has video and whether it's sponsored. |

### 💡 Use cases

#### 🔤 Basic keyword search

```json
{ "queries": ["bluetooth earbuds"] }
```

#### 🚚 Multiple keywords, free shipping only, cheapest first

```json
{ "queries": ["led strip light", "phone case"], "freeShippingOnly": true, "sortBy": "price_up" }
```

#### 💵 Within a price range

```json
{ "queries": ["watch"], "minPrice": 5, "maxPrice": 20 }
```

> **Apify Free plan:** every run is limited to a fixed 10-row sample. Upgrade your Apify plan to run your own settings.

### 🛡️ Limits & responsible use

This Actor reads only DHgate's own public search results. It never signs in and never accesses anything gated behind an account.

`feedback_percent`, `review_count`, and `rating` aren't populated for every listing; that's a real gap in DHgate's own data, not something this Actor fills in. `price_min` can equal `price_max` when a listing has one flat price rather than a quantity-tiered range, both real and handled honestly. Full product-page detail (specs, variants, full review text) isn't offered; DHgate's own individual product pages sit behind stronger protection than the open search results this Actor reads. A single keyword's own inventory can genuinely be exhausted before reaching a very large requested count; splitting a broad keyword into more specific ones reaches a different, additional pool of results.

Read live from DHgate on every run. It is not cached.

Is this an official DHgate tool? No. Independent, not affiliated with DHgate. It reads only publicly available pages.

### 📧 Contact

Need a scraper for a different site, or found something wrong with this one? norm.data.scrapers@gmail.com

### Local development

```bash
bun install
bun test                       # offline: parsing, normalization (real fixtures)
bun run src/main.ts             # reads storage/key_value_stores/default/INPUT.json
```

# Changelog

This Actor's version history is a separate document: https://apify.com/normdata/dhgate-scraper/changelog.md

# Actor input Schema

## `maxItems` (type: `integer`):

Caps how many rows this run writes. Starts at 10 for a quick sample; raise it for a full run. Split evenly across every keyword in "queries".

## `queries` (type: `array`):

One or more DHgate search keywords, e.g. "bluetooth earbuds", "led strip light", "phone case". Each keyword gets its own fair share of maxItems.

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

Only keep products priced at or above this. Checked against the product's own real price, not just passed to DHgate's own filter (confirmed live that DHgate's own price filter narrows the result count but doesn't reliably bound every returned price).

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

Only keep products priced at or below this. Same real re-check as minPrice.

## `freeShippingOnly` (type: `boolean`):

Only keep listings that ship free.

## `sortBy` (type: `string`):

How DHgate orders results within each keyword.

## Actor input object example

```json
{
  "maxItems": 10,
  "queries": [
    "bluetooth earbuds"
  ],
  "freeShippingOnly": false,
  "sortBy": "bestmatch"
}
```

# Actor output Schema

## `listings` (type: `string`):

Complete dataset with all scraped rows.

## `overview` (type: `string`):

Overview view with key fields displayed in a table.

# 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 = {
    "maxItems": 10,
    "queries": [
        "bluetooth earbuds"
    ],
    "sortBy": "bestmatch"
};

// Run the Actor and wait for it to finish
const run = await client.actor("normdata/dhgate-scraper").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 = {
    "maxItems": 10,
    "queries": ["bluetooth earbuds"],
    "sortBy": "bestmatch",
}

# Run the Actor and wait for it to finish
run = client.actor("normdata/dhgate-scraper").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 '{
  "maxItems": 10,
  "queries": [
    "bluetooth earbuds"
  ],
  "sortBy": "bestmatch"
}' |
apify call normdata/dhgate-scraper --silent --output-dataset

```

## MCP server setup

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

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/4rwkUkCuepJSSLge9/builds/ZCY3879sHnL1LnJkz/openapi.json
