# 瓜子二手车 Guazi Used Car (`songd/guazi-scraper`) Actor

Scrape used car listings from en.guazi.com by keyword, brand/model, price, mileage, year, fuel and more. Buy-it-now, auctions, full specs, photos, inspection reports and VIN — per-event pricing.

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

## Pricing

from $2.00 / 1,000 listing enriches

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

## Guazi Used Car Scraper (Apify Actor)

Scrape used car listings from **Guazi** ([en.guazi.com](https://en.guazi.com/)) — China's used-car export and auction platform — by keyword, brand/model, price, mileage, year, fuel type and more. Get buy-it-now prices, sealed-bid and live auctions, plus full spec sheets, photo galleries, per-port FOB prices, Guazi inspection reports and VIN (billed per enriched listing). **Pay-per-event pricing** — you pay only for the results you get.

[![Apify Store](https://img.shields.io/badge/Apify-Store-blue)](https://apify.com) [![Python](https://img.shields.io/badge/Python-3.12-blue)](https://docs.apify.com/actors/development/python)

### Why scrape Guazi?

- **Import pricing research** — real FOB prices by destination port for used cars exported from China.
- **Used-car market analysis** — prices, mileage, age and grades for any brand/model across all trade types.
- **Auction monitoring** — track sealed-bid and live-auction inventory.
- **EV & battery data** — EV range, battery capacity and state-of-health for electrified vehicles.
- **Vehicle condition** — Guazi inspection grades (S–D), no-accident/no-water/no-fire flags, per-category check counts and VIN.

### Features

| Category | Capabilities |
|---|---|
| **Search** | Keyword, brand/model (auto-resolved to Guazi ids), or a ready-made Guazi URL |
| **Filters** | Price, mileage, model/registration/manufacture year, fuel, body type, seller type, condition grade, transmission, engine, horsepower, battery SOH, seats, country |
| **Trade types** | All, Buy It Now, Sealed-Bid Auctions, Live Auctions, New Cars, Fleet Cars |
| **Sorting** | Best match, price ↑/↓, newly listed, 1st-reg/mfg date, mileage ↑/↓ |
| **Enriched data** | Full spec sheet, full photo gallery, per-port FOB prices, inspection report, VIN (masked), location, trim name — fetched per listing |
| **Reliability** | Deduplication across the whole run, graceful stop at your spending limit |
| **Performance** | Fast concurrent page fetching (15 workers, tunable), results in platform page order, bounded-memory streaming (flat RAM even on huge searches), window pipelining (the next window's pages download while the current one enriches), long-lived worker sessions (the EdgeOne cookie challenge is solved once per worker, not once per window), live enrichment progress logs |

### Pricing (pay-per-event)

You are charged only for what you receive, event by event:

- **`listing`** — charged for **every listing saved to the dataset** (the search data: price, mileage, year, specs, grade, image, URL, …).
- **`listing-detail`** — charged **in addition** for every **enriched listing** (enrichment is controlled by `maxDetails`; `0` = unlimited, i.e. every listing is enriched by default). Enriched listings include much more content, so they cost more than a plain listing.

**Not charged:** duplicate listings (deduped across the whole run), unavailable pages, empty results, and listings whose detail page is gone (they are still returned with their search fields, and no `listing-detail` is charged).

**Maximum cost per run.** You can set a spending limit in the Apify Console or via the API (`maxTotalChargeUsd`). The actor checks the limit, caps its own work so it fits the budget, and **stops gracefully**, keeping everything scraped so far.

Exact prices are configured by the actor owner in Apify Console and are intentionally **not** listed here — check the pricing on the Apify Store page, where you can also pick a discount tier.

### Input

| Field | Type | Default | Description |
|---|---|---|---|
| `startUrls` | URL list | – | One or more Guazi used-cars listing URLs. When set, **all other search fields are ignored** — every URL filter parameter is supported (see [Start-URL example](#start-url-example)). |
| `keyword` | string | – | Free-text search term, e.g. `BMW X5` (matched against title, brand, model and specs). Ignored when `startUrls` is set. |
| `brand` | string | – | Brand name in English as shown on en.guazi.com (e.g. `BMW`, `Toyota`, `Mercedes-Benz`); auto-resolved to a Guazi `brandId`. |
| `model` | string | – | Model/series name (e.g. `X5` for BMW). Requires `brand`; auto-resolved to a `seriesId`. |
| `tradeType` | select | `buyItNow` | All listings / Buy It Now / Sealed-Bid Auctions / Live Auctions / New Cars / Fleet Cars. |
| `priceMin` / `priceMax` | integer | 0 | FOB price range in USD (buy-it-now vehicles). A max of `0` means no limit. |
| `mileageMinKm` / `mileageMaxKm` | integer | 0 | Mileage range in km. A max of `0` means no limit. |
| `yearFrom` / `yearTo` | integer | 0 | Model-year range (`carYear`). A max of `0` means no limit. |
| `firstRegYearFrom` / `firstRegYearTo` | integer | 0 | First-registration year range (`licenseYear`). A max of `0` means no limit. |
| `mfgYearFrom` / `mfgYearTo` | integer | 0 | Manufacture-year range (`mfgDate`). A max of `0` means no limit. |
| `fuelType` | select | `any` | Gasoline / Electric (BEV) / Range-extended (REEV) / Plug-in hybrid (PHEV) / Hybrid (HEV) / Diesel / Any. |
| `bodyType` | select | `any` | Sedan / SUV / Mini Van / Hatchback / Wagon / Pick up / Van / Truck / Any. |
| `vehicleSource` | select | `any` | Guazi Owned / Certified Dealer / Individual Seller / Any. |
| `conditionGrade` | select | any | Guazi inspection condition grade `S`–`D`, or any. |
| `transmission` | select | `any` | Manual (MT) / Automatic (AT) / Any. |
| `engineMin` / `engineMax` | number | 0 | Engine displacement range in liters. A max of `0` means no limit. |
| `horsepowerMin` / `horsepowerMax` | integer | 0 | Horsepower range (ps). A max of `0` means no limit. |
| `batteryHealthMin` / `batteryHealthMax` | integer | 0 | Battery state-of-health (SOH) range in %, for EVs. A max of `0` means no limit. |
| `seats` | string list | – | Seat-count codes as taken from a Guazi URL (e.g. from `&seats=2,5`). |
| `sortBy` | select | `bestMatch` | Best match / Lowest price / Highest price / Newly listed / Newest 1st reg. date / Newest mfg. date / Lowest mileage / Highest mileage. |
| `country` | string | – | Guazi country filter code(s) as taken from a Guazi URL (e.g. `CN`). Empty = all. |
| `maxPages` | integer | 1 | Result pages (20 items each) to scrape per search or start URL. **`0` = unlimited** — all available pages (derived from the API's total count). Stops earlier when results run out. Note: Guazi's API itself caps every search at 25 pages with the default `bestMatch` sort, or 80 pages with any other sort — see [Results limit per search](#results-limit-per-search-guazi-api). |
| `maxItems` | integer | 0 | Stop after pushing this many listings in total. **`0` = unlimited** (`maxPages` still applies). |
| `maxDetails` | integer | 0 | Enrich this many listings with the full product-page data (see [Enriched fields](#enriched-fields)). **`0` = unlimited** — every scraped listing is enriched. Each enriched listing is additionally charged as a `listing-detail` event. |

#### Start-URL example

```text
https://en.guazi.com/used-cars/?tradeType=buyItNow&keyword=BMW&price=5000,20000&carYear=2018,2022&sort=price+asc
```

Start URLs are copied straight from [en.guazi.com](https://en.guazi.com/used-cars/) (or built by hand) — the actor translates every supported filter into the underlying API call:

- **Filters** (ranges are `min,max`, e.g. `price=5000,20000`): `keyword`, `brandId`, `seriesId`, `price`, `roadHaul` (thousands of km), `carYear`, `mfgDate`, `licenseYear`, `fuelType`, `autoType`, `gearbox`, `drivetrain`, `carColor`, `seats`, `batteryHealth`, `batteryType`, `batteryCapacity`, `evRange`, `horsepower`, `engine`, `vehicleSourceClassificationCustomers`, `detectionLevels`, `country`
- **`tradeType`** — `buyItNow`, `sealedBid`, `liveAuction`, `newCars`, `fleetCars` or `all`
- **`sort`** — the sort string from the Guazi URL (e.g. `price+asc`)
- **`page`** — first page to start from

Multiple start URLs are scraped as separate searches and deduplicated across the whole run.

### Results limit per search (Guazi API)

Guazi's search API stops serving results after a fixed pagination depth, and every page beyond it returns a *successful* response with an empty list. The advertised `totalCount` (~176,000 for all buy-it-now cars) therefore cannot be reached by paging — by any client, from any IP; the site's own UI stops at the same point (verified against direct connections, real browsers and different IP classes). The actor detects this and ends the search cleanly; pages that return nothing are not charged.

| `sortBy` | Pages per search (20 items/page) | Max listings per search |
|---|---|---|
| `bestMatch` (default; unsorted) | 25 | **500** |
| any explicit sort — `priceAsc`, `priceDesc`, `newlyListed`, `regDateDesc`, `mfgDateDesc`, `mileageAsc`, `mileageDesc` | 80 | **1,600** |

The limit is **per search** (one filter combination / one start URL), so a run scraping several start URLs collects up to that many listings *per search*, deduplicated across the whole run. To surface more of the inventory, narrow the filters (brand, model, price, year, …) and split the search into several smaller ones, and/or pick an explicit `sortBy`, which raises the per-search ceiling from 500 to 1,600.

### Output (default dataset)

Every listing is pushed as one dataset item in platform page order:

| Field | Description |
|---|---|
| `id` | Guazi product id (unique key) |
| `url` | Listing page URL |
| `title` | Listing title |
| `brand` / `brandId` | Brand name / Guazi brand id |
| `modelId` / `seriesName` | Guazi series (model) id / resolved model name |
| `carId` | Trim/variant id (same as `variantId` when enriched) |
| `priceUsd` / `priceText` | Current price (number and original text) |
| `originalPriceUsd` / `discountAmountUsd` | List price and discount when promoted |
| `mileageKm` / `mileageText` | Mileage |
| `manufactureDate` / `manufactureYear` | Manufacture date / year |
| `firstRegistrationDate` | First registration date (`YYYY-MM-DD`) |
| `engine` / `transmission` / `fuelType` | Powertrain strings from the listing |
| `isElectrified` / `batteryCapacity` / `evRange` | EV flag and battery data |
| `conditionGrade` | Guazi inspection grade (S–D) |
| `sellerType` | Guazi Owned / Certified Dealer / Individual Seller |
| `tradeType` / `auctionType` | Listing category / auction label |
| `image` | Head photo URL |
| `tags` | Extra label texts from the listing |
| `scrapedAt` | UTC timestamp |

`seriesName` is resolved automatically; a small minority of trim-level ids have no entry and stay `null` in the search-only output (it is back-filled from the product page when the listing is enriched).

#### Enriched fields (when `maxDetails` covers the listing)

Added on top of the search fields when a listing is enriched:

| Field | Description |
|---|---|
| `vinMasked` | VIN (masked by Guazi, e.g. `LSY**********2037`) |
| `variantId` / `variantUrl` / `trimName` | Trim identity: variant id, its variant-page URL and the slug-derived trim name |
| `driveType` / `bodyType` / `exteriorColor` / `modelYear` / `steering` / `location` | Fields from the product-page spec table |
| `horsepowerPs` / `curbWeightKg` / `dimensionsMm` | Power (ps), curb weight, `{lengthMm, widthMm, heightMm}` |
| `images` / `imageCount` | Full photo gallery |
| `exteriorImages` / `interiorImages` / `detailImages` | Gallery split by category |
| `portPrices` | Per-port FOB prices: `[{port, code, usd, default}]` |
| `defaultPortPriceUsd` | FOB price at the default export port |
| `batteryHealthSoH` / `batteryCapacity` / `evRange` | Battery state of health (%) and numeric EV data |
| `specifications` | Full manufacturer spec sheet grouped by section, e.g. `{"Engine": {"Displacement (L)": "2", …}}` |
| `inspection` | Guazi inspection report: `grade`, `guaziReport`, `cleanCar` (`noAccident` / `noWater` / `noFire`) and `categoryChecks` (`{category: {normal, abnormal}}`) |
| `featureHighlights` | Feature highlight texts |
| `clueId` | Guazi internal clue id |

Note: `carId` (search) and `variantId` (detail) are the **same ID**; the per-vehicle unique key is `id`/`productId`.

### Support & Feature Requests 📬

If you encounter any issues or have ideas for new features, please don't hesitate to open an issue. We value your feedback and will do our best to improve the scraper based on your suggestions.

#### Requesting additional data fields

The scraper currently extracts the fields listed in [Output](#output-default-dataset) and [Enriched fields](#enriched-fields-when-maxdetails-covers-the-listing), but if you need additional data points not included in the output, please open an issue describing the required fields. We'll look into incorporating them into the actor.

### Legal note

Scraped data is provided as-is for personal and commercial use at your own risk. Respect Guazi's terms of service and applicable laws in your jurisdiction.

***

**SEO Keywords**: guazi scraper, en.guazi.com scraper, used car scraper, china used cars, guazi auction scraper, guazi buy it now, used car price data, vehicle inspection report, car VIN scraper, apify car actor, import car pricing, china car export

# Actor input Schema

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

One or more Guazi used-cars listing URLs to scrape, e.g. https://en.guazi.com/used-cars/?tradeType=buyItNow\&keyword=BMW\&price=5000,20000\&carYear=2018,2022\&sort=price+asc. All URL filter parameters are supported (keyword, brandId, seriesId, price, roadHaul, carYear, mfgDate, licenseYear, fuelType, autoType, gearbox, drivetrain, carColor, seats, batteryHealth, batteryType, batteryCapacity, evRange, vehicleSourceClassificationCustomers, keyword, sort, page). When provided, these URLs take priority and all other search fields below are ignored.

## `keyword` (type: `string`):

Free-text search term, e.g. 'BMW X5', 'Toyota RAV4'. Matched against title, brand, model and specs. Ignored when startUrls is set.

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

Brand name in English as shown on en.guazi.com (e.g. 'BMW', 'Toyota', 'Mercedes-Benz'). Resolved to a brandId via the Guazi brand API. Case-insensitive.

## `model` (type: `string`):

Model/series name (e.g. 'X5' for BMW). Requires 'brand'. Resolved to a seriesId via the Guazi brand API.

## `tradeType` (type: `string`):

Which Guazi listing category to search.

## `priceMin` (type: `integer`):

Minimum FOB price in USD (buy-it-now vehicles only).

## `priceMax` (type: `integer`):

Maximum FOB price in USD. 0 = no limit.

## `mileageMinKm` (type: `integer`):

Minimum mileage in kilometers.

## `mileageMaxKm` (type: `integer`):

Maximum mileage in kilometers. 0 = no limit.

## `yearFrom` (type: `integer`):

Earliest model year (carYear).

## `yearTo` (type: `integer`):

Latest model year (carYear). 0 = no limit.

## `firstRegYearFrom` (type: `integer`):

Earliest first-registration year (licenseYear).

## `firstRegYearTo` (type: `integer`):

Latest first-registration year (licenseYear). 0 = no limit.

## `mfgYearFrom` (type: `integer`):

Earliest manufacture year (mfgDate).

## `mfgYearTo` (type: `integer`):

Latest manufacture year (mfgDate). 0 = no limit.

## `fuelType` (type: `string`):

Fuel/energy type filter (Guazi fuelTypes codes 1-6, verified).

## `bodyType` (type: `string`):

Body type filter (Guazi autoTypes codes from the queryAutoType API, verified).

## `vehicleSource` (type: `string`):

Seller type filter (Guazi vehicleSourceClassificationCustomers codes, verified).

## `conditionGrade` (type: `string`):

Guazi inspection condition grade (detectionLevels, verified).

## `transmission` (type: `string`):

Transmission filter (Guazi gearboxes codes 1 = MT, 2 = AT, verified).

## `engineMin` (type: `number`):

Minimum engine displacement in liters (airDisplacementMin).

## `engineMax` (type: `number`):

Maximum engine displacement in liters (airDisplacementMax). 0 = no limit.

## `horsepowerMin` (type: `integer`):

Minimum horsepower (horsepowerMin).

## `horsepowerMax` (type: `integer`):

Maximum horsepower (horsepowerMax). 0 = no limit.

## `batteryHealthMin` (type: `integer`):

Minimum battery state of health in percent (batteryHealthStart), for EVs.

## `batteryHealthMax` (type: `integer`):

Maximum battery state of health in percent (batteryHealthEnd). 0 = no limit.

## `seats` (type: `array`):

Seat-count filter as Guazi codes, taken directly from a Guazi used-cars URL (e.g. from '\&seats=2,5').

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

Sort order of the search results.

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

Optional Guazi 'country' filter code(s) as taken from a Guazi used-cars URL (e.g. 'country=CN'). Leave empty for all.

## `maxPages` (type: `integer`):

How many result pages (20 items each) to scrape per search (or per start URL). 0 = unlimited: all available pages are scraped (derived from the API's totalCount). The run stops earlier if there are no more results.

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

Stop after pushing this many listings in total (0 = unlimited; maxPages still applies).

## `maxDetails` (type: `integer`):

Enrich this many listings with full product-page data: complete spec sheet, full photo gallery (exterior/interior/detail), per-port FOB prices, Guazi inspection report (grade, no-accident/no-water/no-fire, per-category checks), feature highlights, VIN (masked), location and trim name. 0 = unlimited: every scraped listing is enriched. Each enriched listing is additionally charged as a 'listing-detail' event because it requires an extra product-page fetch.

## Actor input object example

```json
{
  "startUrls": [],
  "keyword": "",
  "brand": "",
  "model": "",
  "tradeType": "buyItNow",
  "priceMin": 0,
  "priceMax": 0,
  "mileageMinKm": 0,
  "mileageMaxKm": 0,
  "yearFrom": 0,
  "yearTo": 0,
  "firstRegYearFrom": 0,
  "firstRegYearTo": 0,
  "mfgYearFrom": 0,
  "mfgYearTo": 0,
  "fuelType": "any",
  "bodyType": "any",
  "vehicleSource": "any",
  "conditionGrade": "",
  "transmission": "any",
  "engineMin": 0,
  "engineMax": 0,
  "horsepowerMin": 0,
  "horsepowerMax": 0,
  "batteryHealthMin": 0,
  "batteryHealthMax": 0,
  "seats": [],
  "sortBy": "bestMatch",
  "country": "",
  "maxPages": 1,
  "maxItems": 0,
  "maxDetails": 0
}
```

# Actor output Schema

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

One item per unique Guazi listing in the default dataset. Always present: id, url, title, brand, model, price, mileage, year, specs, grade, seller type, photo, tags. Present when the listing was enriched (maxDetails): VIN (masked), trim name, full spec sheet, photo gallery, per-port FOB prices, inspection report, battery data. Rows for invalid inputs carry error/errorMessage instead.

# 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": [],
    "keyword": "",
    "brand": "",
    "model": "",
    "tradeType": "buyItNow",
    "fuelType": "any",
    "bodyType": "any",
    "vehicleSource": "any",
    "conditionGrade": "",
    "transmission": "any",
    "sortBy": "bestMatch",
    "maxPages": 1
};

// Run the Actor and wait for it to finish
const run = await client.actor("songd/guazi-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 = {
    "startUrls": [],
    "keyword": "",
    "brand": "",
    "model": "",
    "tradeType": "buyItNow",
    "fuelType": "any",
    "bodyType": "any",
    "vehicleSource": "any",
    "conditionGrade": "",
    "transmission": "any",
    "sortBy": "bestMatch",
    "maxPages": 1,
}

# Run the Actor and wait for it to finish
run = client.actor("songd/guazi-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 '{
  "startUrls": [],
  "keyword": "",
  "brand": "",
  "model": "",
  "tradeType": "buyItNow",
  "fuelType": "any",
  "bodyType": "any",
  "vehicleSource": "any",
  "conditionGrade": "",
  "transmission": "any",
  "sortBy": "bestMatch",
  "maxPages": 1
}' |
apify call songd/guazi-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,songd/guazi-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/gEplZ55TGSdJSGpEB/builds/W6AI8eZbuLdKaog8t/openapi.json
