# Alibaba RFQ Scraper (`w3crawler/alibaba-rfq-scraper`) Actor

Extracts public buying requests from Alibaba's sourcing RFQ market using embedded page data and optional public detail pages.

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

## Pricing

from $2.99 / 1,000 rfqs

This Actor is paid per event and usage. You are charged both the fixed price for specific events and for Apify platform usage.

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

## Alibaba RFQ Scraper

This Actor collects rich, publicly visible buying requests from Alibaba’s sourcing RFQ pages. It parses the structured `window.PAGE_DATA` records delivered in the public HTML and can optionally fetch public RFQ detail pages for annex links.

It is read-only. It does not sign in, submit quotations, contact buyers, access private supplier portals, or bypass CAPTCHA/anti-automation controls. When Alibaba blocks or a public request fails, the Actor records an operational issue count in `OUTPUT_SUMMARY` and logs details; it does not write diagnostic or placeholder dataset rows.

### Dataset output

RFQ rows can contain:

- `rfqId`, `requestId`, `subject`, `description`, `sourceUrl`, and `detailUrl`
- buyer name, company, country, country code, location, member-since text, and verification flag
- quantity text, normalized numeric quantity, and unit
- requirement category, product type, target price, budget, destination, shipping terms, purchase frequency, and customization text when exposed
- opened-time text/epoch, quote counters, RFQ quality signals, tags, primary image, and image URLs
- optional public PDF/document `annexes` from detail pages

Instrumentation such as `fieldCoverage`, `dataQuality`, record IDs used only for deduplication, request counters, access metadata, and debug status fields is not emitted in dataset rows. Run metrics and operational issue counts are available under `OUTPUT_SUMMARY`; failure details stay in run logs.

### Basic input

```json
{
  "keywords": ["wireless earbuds"],
  "maxPages": 2,
  "maxItems": 50,
  "sortBy": "newest",
  "includeDetails": true,
  "includeAnnexes": true
}
```

Use `searchUrls` when you already have official public list URLs. Explicit URLs take precedence over generated keyword URLs, and each source is paginated independently.

```json
{
  "searchUrls": [
    "https://sourcing.alibaba.com/rfq/rfqSearchList.htm?keywords=wireless+earbuds",
    "https://sourcing.alibaba.com/rfq/rfqSearchList.htm?keywords=work+lights"
  ],
  "startPage": 1,
  "maxPages": 3,
  "maxItems": 100,
  "deduplicate": true
}
```

### Filters and sorting

```json
{
  "keywords": ["private label earbuds"],
  "buyerCountries": ["US", "United States"],
  "requiredTags": ["verified"],
  "minQuantity": 100,
  "maxQuantity": 10000,
  "textQuery": "charging case",
  "sortBy": "quantity_desc"
}
```

Supported `sortBy` values are `relevance`, `newest`, `quantity_desc`, `quotes_desc`, `quality_desc`, `buyer_country_asc`, and `subject_asc`.

### Developer options and proxy

The input schema groups operational controls under Developer options. Values are bounded and validated before requests are made:

- `requestTimeoutSecs` (5–300), with legacy alias `timeoutSecs`
- `requestDelayMs` (0–15,000)
- `maxConcurrency` (1–5)
- `maxRequestRetries` (0–10)
- `maxBytes` (1,000–15,000,000)
- optional transparent `userAgent`

For ordinary proxy routing, use Apify Proxy or public HTTP(S) proxy URLs. Private/local proxy addresses are rejected.

```json
{
  "keywords": ["work lights"],
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": ["RESIDENTIAL"],
    "apifyProxyCountry": "US"
  },
  "requestDelayMs": 1500,
  "maxConcurrency": 2,
  "maxRequestRetries": 3,
  "requestTimeoutSecs": 45
}
```

Proxy routing is not a stealth or access-control bypass. Challenges remain explicit operational diagnostics outside the dataset.

### Dataset example

```json
{
  "url": "https://sourcing.alibaba.com/rfq/rfqSearchList.htm?keywords=wireless+earbuds&page=1",
  "source": "https://sourcing.alibaba.com",
  "sourceUrl": "https://sourcing.alibaba.com/rfq/rfqSearchList.htm?keywords=wireless+earbuds&page=1",
  "detailUrl": "https://rfq.alibaba.com/rfq/1846209687",
  "rfqId": "1846209687",
  "requestId": "encoded-1846209687",
  "subject": "Bulk wireless earbuds for retail",
  "description": "Need private-label earbuds with charging cases.",
  "buyer": {
    "name": "Demo Buyer",
    "country": "United States",
    "countryCode": "US"
  },
  "quantity": {
    "value": "500",
    "valueNumber": 500,
    "unit": "pieces"
  },
  "opened": { "relative": "Opened today", "epochMs": 1760000000000 },
  "quotes": { "leftCount": 4, "hasQuoteEquity": true },
  "quality": { "starLevel": 4, "starMax": 5 },
  "imageUrl": "https://img.alibaba.com/photo/earbuds.jpg",
  "tags": ["urgent", "verified"],
  "hasAnnexes": true,
  "scrapedAt": "2026-09-07T07:27:49.678Z"
}
```

Operational issue details stay in the run logs, and numeric issue counts are kept in `OUTPUT_SUMMARY`; diagnostic records are never added to the dataset.

Runs with saved RFQ records and later source/detail/output issues finish as `SUCCEEDED` with `dataStatus: PARTIAL`. A readable source with no RFQs or filters that remove all observed RFQs completes with `status: SUCCEEDED` and `dataStatus: NO_RESULTS`. A blocked or genuine request/parse failure without saved records is `FAILED` and exits nonzero.

### Offline QA and deployment

The checked-in `.actor/input.json` uses `fixtures/sample.html`, so it is reproducible without a network request:

```bash
npm install
npm test
npm run validate
apify run --input-file .actor/input.json --purge
```

The offline test suite uses ordered page fixtures or one fixture per source. Live Actor runs use public Alibaba pages through `startUrls` and do not expose local fixture overrides.

Run summaries include the required `status`, `runStatus`, and `dataStatus` fields. `status` and `runStatus` are `SUCCEEDED` when source-backed rows were saved or a run completed cleanly with no matches; they are `FAILED` only when a real failure occurs before any rows are saved. `dataStatus` is `COMPLETE`, `PARTIAL`, `NO_RESULTS`, or `FAILED`. Failure details stay in the run logs; key-value summaries retain counts only.

# Actor input Schema

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

One public sourcing keyword. Use keywords for multiple independent query sources.

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

Multiple public RFQ search keywords. Each keyword becomes a source and is paginated independently.

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

Optional country query parameter passed to Alibaba's public RFQ list.

## `countries` (type: `array`):

Compatibility input retained for integrations; the first source country is used for URL generation.

## `searchUrl` (type: `string`):

One HTTPS sourcing.alibaba.com/rfq/rfqSearchList.htm URL copied from the public site. It takes precedence over generated keyword sources.

## `searchUrls` (type: `array`):

Multiple official public RFQ list URLs. Each URL is paginated independently.

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

Compatibility alias for searchUrls.

## `startPage` (type: `integer`):

One-based page number at which every source starts.

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

Bound the number of public RFQ pages fetched for each source.

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

Maximum RFQ rows emitted after deduplication, filtering, and sorting.

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

Stable ordering applied after all sources and pages are aggregated.

## `textQuery` (type: `string`):

Keep RFQs whose subject, description, buyer, requirement, country, or tags contain this text.

## `buyerCountries` (type: `array`):

Keep RFQs whose visible buyer country or country code contains one of these values.

## `requiredTags` (type: `array`):

Keep RFQs containing every requested visible tag.

## `minQuantity` (type: `number`):

Keep RFQs whose parsed quantity is at least this value.

## `maxQuantity` (type: `number`):

Keep RFQs whose parsed quantity is at most this value.

## `deduplicate` (type: `boolean`):

Merge repeated public observations by RFQ identifier.

## `includeDetails` (type: `boolean`):

Fetch visible public RFQ detail pages when a detail URL is present.

## `includeAnnexes` (type: `boolean`):

Retain visible public PDF/document links discovered on detail pages.

## `includeImages` (type: `boolean`):

Include public Alibaba RFQ image URLs when exposed by the page.

## `requestTimeoutSecs` (type: `integer`):

Maximum time for each public Alibaba request.

## `timeoutSecs` (type: `integer`):

Compatibility alias for requestTimeoutSecs.

## `requestDelayMs` (type: `integer`):

Delay between public requests per source worker.

## `maxConcurrency` (type: `integer`):

Maximum simultaneous source/detail workers.

## `maxRequestRetries` (type: `integer`):

Retries for timeouts, rate limits, access challenges, and transient HTTP failures.

## `maxBytes` (type: `integer`):

Reject unusually large public responses before parsing.

## `userAgent` (type: `string`):

Optional transparent HTTP user-agent string.

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

Optional ordinary proxy routing. Use Apify Proxy or a supplied public HTTP(S) proxy; no stealth or access-control bypass is performed.

## Actor input object example

```json
{
  "keywords": [
    "wireless earbuds"
  ],
  "startPage": 1,
  "maxPages": 1,
  "maxItems": 20,
  "sortBy": "relevance",
  "deduplicate": true,
  "includeDetails": false,
  "includeAnnexes": true,
  "includeImages": true,
  "requestTimeoutSecs": 30,
  "requestDelayMs": 1000,
  "maxConcurrency": 1,
  "maxRequestRetries": 3,
  "maxBytes": 5000000
}
```

# Actor output Schema

## `dataset` (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 = {};

// Run the Actor and wait for it to finish
const run = await client.actor("w3crawler/alibaba-rfq-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 = {}

# Run the Actor and wait for it to finish
run = client.actor("w3crawler/alibaba-rfq-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 '{}' |
apify call w3crawler/alibaba-rfq-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,w3crawler/alibaba-rfq-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/f5UFv9MCLCag5kU3I/builds/ZoRTGJYLRsdG8vyl8/openapi.json
