# eBay to 1688 Sourcing Bridge: Suppliers & Margins (`crawleast/ebay-to-1688-sourcing-bridge`) Actor

Find 1688.com suppliers for eBay items by image search. Each row: wholesale and consign price in CNY, MOQ, factory signals, soldCount and an estimated per-unit margin. $0.005 per eBay lookup, $0.008 per supplier row, $0.00005 run base fee. Failed lookups are free.

- **URL**: https://apify.com/crawleast/ebay-to-1688-sourcing-bridge.md
- **Developed by:** [Kyle Wang](https://apify.com/crawleast) (community)
- **Categories:** E-commerce, Automation, Lead generation
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $5.60 / 1,000 1688 supplier matches

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

## eBay → 1688 Sourcing Bridge

Turn eBay item URLs, item IDs or product photos into a **1688 supplier shortlist** — one row per supplier, with wholesale and consign prices in CNY, MOQ, factory signals, sold counts, and an **estimated per-unit margin** in the listing's own price currency (USD, GBP, EUR, AUD or CAD).

Most eBay scrapers sell listings. Most 1688 image-search Actors accept a photo. This Actor joins the two: it reads a public eBay item page (`/itm/{id}`), takes the main image, runs a reverse image search on 1688.com, and returns sourcing rows you can act on.

### What you get

Three flat row types in one dataset (`type` tells them apart):

| `type` | Meaning |
| --- | --- |
| `source` | One eBay lookup: title, main image, price + currency, condition, sold count, available quantity, listing type (Buy It Now / auction), Best-Offer flag, seller store name and link, `fetchStatus` |
| `match` | One 1688 supplier offer: wholesale/consign price (CNY), MOQ, booked count, supplier city/province, years on 1688, factory flags, offer URL and image, `relevantMatch` flag, estimated unit margin |
| `summary` | Per-item rollup: match count, relevant match count, best margin, best supplier, factory count, top 3 margins |

The run report is stored as `RUN_SUMMARY` in the key-value store: requested/delivered counts, skipped items by reason, field-coverage metrics (including `relevantMatchRate` and `soldCountPresent`), eBay challenge statistics, request usage, FX rates and charged amount. The completeness identity `delivered.summaries + sum(skippedBy) = requested.dedupedItems` is verified before exit, so items are never silently dropped.

### Input

| Field | Type | Default | Notes |
| --- | --- | --- | --- |
| `ebayUrls` | string\[] | — | Up to 100 item URLs. `ebay.com/itm/{id}` and `ebay.com/p/{id}` are both normalized to the item ID. Any of the four supported marketplaces (`.com`, `.co.uk`, `.de`, `.com.au`) is accepted; each URL carries its own marketplace. `ebay.ca` URLs are rejected before any request and are not charged |
| `itemIds` | string\[] | — | Up to 100 numeric item IDs (6+ digits); merged and deduplicated with `ebayUrls`. Bare IDs use the `marketplace` setting |
| `imageUrls` | string\[] | — | Up to 100 direct product image URLs. Skips the eBay lookup (free); no eBay price, so margin is null |
| `marketplace` | enum | `com` | `com`, `co.uk`, `de`, `au` — used for bare item IDs. Each marketplace is fetched through a residential exit in its own country (US/GB/DE/AU). The price currency follows the listing (normally the marketplace currency: USD, GBP, EUR, AUD). `ebay.ca` is not supported — see the caveats |
| `maxItems` | int | 20 | 1–100, global hard cap across the three inputs (deduplicated union, first come first served). Each delivered item costs one $0.005 ebay-item-lookup plus its supplier-match rows. The default 1024 MB is not the binding limit at the top of the range — the run window is (see **Run window** below). Measured: a full `maxItems=100` run peaked at **457 MB** of its 1024 MB. Raise the memory to 2048 MB only if you also raise `maxSuppliersPerItem` well above its default |
| `maxSuppliersPerItem` | int | 10 | 1–20, in similarity order |
| `maxTotalChargeUsd` | number | 2 | Hard per-run charge cap, minimum 0.04. With a very small cap (under ~$0.10) send only 1–2 items: lookups are billed as they complete, and a micro cap can be consumed before many supplier rows are delivered |
| `maxRequestsPerRun` | int | 1200 | Request quota guard, 50–2000 |

You can mix all three inputs in one run. Duplicates are removed by item ID (URLs and bare IDs together) or by exact image URL. There is no keyword mode: every run is anchored to items you already know.

All inputs empty → the run exits with a validation error and **zero charge**.

### Output example

```json
{
  "type": "match",
  "itemKey": "ebay:186933747946",
  "itemId": "186933747946",
  "inputKind": "ebay",
  "offerId": "8899001",
  "similarityRank": 1,
  "title1688": "宠物玩具源头工厂批发 可定制",
  "priceYuan": 6.8,
  "consignPriceYuan": 9.5,
  "moq": 2,
  "bookedCount": 8200,
  "supplierName": "义乌市宠物用品厂",
  "supplierCity": "金华",
  "supplierProvince": "浙江",
  "yearsOnPlatform": 6,
  "compositeScore": 87,
  "isFactory": true,
  "isSuperFactory": false,
  "offerUrl": "https://detail.1688.com/offer/8899001.html",
  "offerImageUrl": "https://cbu01.alicdn.com/img/ibank/abc.jpg",
  "estMarginPerUnit": 23.66,
  "estMarginPct": 94.7,
  "marginBasis": "consign",
  "costBasis": "consign",
  "marginCurrency": "USD",
  "fxUsdCny": 7.14,
  "fxCnyToLocal": 0.14,
  "matchedBy": "image",
  "relevantMatch": true
}
```

`estMarginPerUnit` is negative when the 1688 cost exceeds the eBay price. Negative margins are reported as-is: a product to avoid is also an answer.

### About `relevantMatch`

Image search finds visually similar products, and visual similarity is not semantics: an exact-form hit and a loosely-similar category neighbor can come back in the same list. Every match row therefore carries a `relevantMatch` boolean: `true` **only** when the 1688 offer title shares meaningful vocabulary with the eBay title (a static EN→CN category word map, a German-stem bridge so `.de` listings are not left out, or latin tokens like `led`/`usb` that pass through untranslated). Vocabulary coverage is **English and German**; a title in another language can only match on latin pass-through tokens, so the flag can read `false` on an offer that really is the same product — it is a filter aid, never a verdict, and it has never affected billing. `similarityRank` orders the rows but never marks them relevant — a top-ranked visual neighbor from a different category (a scene photo matching the furniture around the product) is flagged `false` like any other miss. Offers that are services rather than products (processing, recycling, franchise/agency listings) are always `false`. Noisy rows are still delivered and billed — the engine cost has already happened — but `summary.relevantMatchCount` and `RUN_SUMMARY.fieldCoverage.relevantMatchRate` count only the relevant subset, so Store-facing numbers stay honest. Filter on the flag; judge with your own eyes before ordering.

### Pricing (pay-per-event)

All prices are **FREE-tier list prices**. Subscription tiers receive the discounts below.

| Event | FREE | BRONZE (−10%) | SILVER (−20%) | GOLD (−30%) | PLATINUM (−30%) | DIAMOND (−30%) | Charged when |
| --- | ---: | ---: | ---: | ---: | ---: | ---: | --- |
| `run-base-fee` | $0.00005 | $0.00005 | $0.00005 | $0.00005 | $0.00005 | $0.00005 | Once per run, only if at least one billable row is delivered |
| `ebay-item-lookup` | $0.005 | $0.0045 | $0.004 | $0.0035 | $0.0035 | $0.0035 | Per eBay item with `fetchStatus: "ok"` |
| `supplier-match` | $0.008 | $0.0072 | $0.0064 | $0.0056 | $0.0056 | $0.0056 | Per match row actually written to the dataset |

Discounts are applied by Apify according to the subscriber's plan. There is no platform start fee.

**Free:** blocked, not-found and errored eBay lookups, challenge-wall items, image-conversion failures, items stopped by budget/quota/deadline, zero-match image searches, and matches cut by your budget. You pay only for rows actually delivered.

Worked examples (FREE tier):

| Order | Calculation | Total |
| --- | --- | --- |
| 1 eBay item, 10 matches | $0.00005 + $0.005 + 10 × $0.008 | **$0.08505** |
| 10 eBay items, 10 matches each (default) | $0.00005 + 10 × $0.005 + 100 × $0.008 | **$0.85005** |
| 10 image URLs, 10 matches each | $0.00005 + 100 × $0.008 | **$0.80005** |
| 100 eBay items × up to 20 suppliers (top cap) | $0.00005 + 100 × $0.005 + up to 2,000 × $0.008 | **$16.50005** — far above the default $2 budget, so partial runs are expected; raise `maxTotalChargeUsd` for full batches. Note the run window also caps a single run at roughly 90 fully matched items (see **Run window**), so the top of the range is reached by splitting across runs |

The default `maxTotalChargeUsd` is $2, which covers roughly 23 fully matched items at 10 suppliers each. Raise it for larger batches; when the cap is reached the run stops gracefully and reports `trigger: "budget"` plus `skippedBy.budget`.

### Margin math

One FX snapshot is captured at the start of the run and reported in `RUN_SUMMARY.fx` (rates, source, timestamp). Every item is computed **in the currency its price is actually denominated in** — normally the marketplace currency (a `.co.uk` item yields GBP margins, a `.de` item EUR), except listings that price in a foreign currency (e.g. a GBP price served on ebay.com), which keep that currency — using a single CNY→local rate per run.

- Margin currency = the currency the listing's price is denominated in (`marginCurrency` records it; `fxCnyToLocal` records the rate used).
- Cost basis: `consignPriceYuan` when the 1688 offer exposes a dropship/consign price, otherwise `priceYuan` (`costBasis` and `marginBasis` record which).
- Revenue = the eBay price as served on the page. It is read from the page's own price block; the embedded JSON-LD price is a fallback only when its declared currency matches the marketplace — localized JSON-LD prices (a CN visitor seeing CNY on a US listing) are rejected this way. An explicit currency marker in the price block (a GBP price on ebay.com) wins over the marketplace default; a bare `$` price with no marker inherits the marketplace currency.
- `estMarginPerUnit = priceLocal − costCNY × fx(CNY→marginCurrency)`
- Direct `imageUrls` inputs have no eBay price, so their margin fields are null (`marginBasis: "none"`).

Margin estimates are **indicative, not guaranteed**. They exclude inbound shipping to you, duties, payment fees, eBay seller fees and returns. Always verify with a sample order.

### How it works

1. **eBay leg** — pure HTTP (no browser) with a fresh sticky residential session per item (hyphen-free session IDs), exiting in the marketplace's own country: US for .com, GB for .co.uk, DE for .de, AU for .com.au — a geographically matched exit lowers eBay's challenge rate. The session is warmed with a homepage request to seed cookies, then `/itm/{id}` is fetched with full navigation headers. eBay's splashui challenge is a passive proof-of-work page that usually auto-passes in a few seconds: on the first attempt the Actor polls the same session every 2 s for up to 15 s and continues when it clears. Interactive challenge forms (CAPTCHA, press-and-hold, sliders) are never waited for and never solved — the session rotates immediately, at most two rotations before the item is reported `blocked` (free). A 404/410 is `notFound`; datacenter-style 403/429/503 also rotate. All four supported marketplaces share the same leg.
2. **Image handling** — the main image URL is rewritten to eBay's `s-l1600.jpg` CDN name, which forces a full-size real JPEG. The download is verified by **magic bytes**, not extension; buffers under 10 KB are the CDN's placeholder trap, which triggers a downgrade to `s-l500.jpg` and one retry. WebP images from custom URLs are converted to real JPEG before upload.
3. **1688 leg** — the image is uploaded to 1688's image service and searched with the existing production image-search engine: per-identity quotas, residential China sessions, token cache, risk-control rotation. Card fields come straight from the search response (no extra 1688 detail calls), and each offer is immediately scored for margin and `relevantMatch`.

### Limits and honest caveats

- **Visual similarity ≠ same product or same factory.** Match rows are ordered by the platform's similarity rank. eBay product photos are typically clean object shots, so recall quality is high — but marketing composites, lifestyle shots and text-heavy designs still recall worse. Use `relevantMatch` + `similarityRank`, then verify with your own eyes.
- **`soldCount`, `availableQuantity`, `bestOffer`, `listingType`, `sellerName`/`storeSlug` are best-effort fields** read from public detail-page structures. They are parsed language-independently (structural selectors and the page's embedded view-model JSON, never localized button text), and each field is extracted independently — one missing block can no longer null out the others. They are null (never guessed) when the page genuinely lacks the information, and `RUN_SUMMARY.fieldCoverage.soldCountPresent` reports the measured coverage. `soldCount` and `availableQuantity` are both read from the quantity block's own facts, so recommended-item carousels can never leak another listing's sold count into a row. `availableQuantity` is what the page displays, and eBay caps that display at 10 ("More than 10 available"), so a `10` means "at least 10". `soldCount` is one of eBay buyers' core sourcing signals — treat the column as a signal, not a guarantee.
- **Variation listings show their cheapest variation.** Before a variation is selected, eBay's price block displays the minimum across variations (a £5–£200 listing shows £5.00). When that happens the source row carries `priceIsVariationMinimum: true` and `variationPriceRange {min, max, count, variationsTotal, currency, currencyMatchesListing}` — margin computed from the displayed minimum can understate the real deal price, so check the range before judging such an item. Both fields are only ever computed **inside one currency**: eBay sometimes serves the variation view-model converted (a .co.uk listing came back priced in USD), and comparing a displayed £5.00 with a converted $6.70 minimum once produced a flatly wrong `false`. When the page carries nothing but a converted currency the flag is `null` and `currencyMatchesListing` is `false` — unknown, not negative.
- **eBay anti-bot is real.** A challenge-blocked item is returned as a `source` row with `fetchStatus: "blocked"` and is **not billed**. The run report counts it under `skippedBy.blocked` and `challengeStats` records how often the passive challenge was seen, waited out, or blocked the item.
- **`skippedBy.blocked` covers both legs, and only the eBay one is free.** The same counter is incremented when 1688's image search returns risk control for an item. In that case the eBay fetch **had already succeeded**, so the item is billed one `ebay-item-lookup` and produces **no match rows and no summary row**. To tell the two apart, count the `source` rows with `fetchStatus: "blocked"`: `skippedBy.blocked` minus that number is the 1688-side count. Measured rate on the Etsy twin of this Actor: 19% of paid lookups on a 56-item run, 8% on a 39-item run — it depends on how hard 1688 is rate-limiting its image-search endpoint that day, and a smaller batch lowers it.
- **`ebay.ca` is not supported.** The Canadian site served an interactive human-verification challenge to every automated session measured — 6/6 through a US residential exit and 4/4 through a Canadian residential exit — while .com, .co.uk, .de and .com.au delivered normally inside the same runs. Rather than advertise five marketplaces and hand back free `blocked` rows on the fifth, `.ca` is not offered: an `ebay.ca` URL is rejected before any request, is never billed, and is reported in `RUN_SUMMARY.invalidInputs` with reason `unsupported_marketplace_ebay_ca`.
- **Prices vary by session and marketplace.** The price is read from the page as served to a residential session in the marketplace's country, in the currency the price block itself denotes (a foreign-currency listing keeps its own currency; `priceCurrency` and `marginCurrency` record which). Auction items carry the current bid, not a final price.
- **`imageUrls` input has no eBay price**, so margin fields are null. Match rows are still full price; the 1688 supplier data is the deliverable.
- **Supplier names stay in Chinese** as registered on 1688. `compositeScore` and `isFactory` are raw platform signals. This Actor does not claim a supplier is "verified"; the judgment is yours.
- **Run window.** Default timeout is 1,200 seconds, and the Actor caps its own working budget at **1,110 seconds** of that (the remainder is the finish reserve) — raising the platform timeout above 1,200 s does **not** buy you more items. The eBay phase holds back roughly the last 35% of the window for the 1688 leg — and never more than half of it, so a short timeout shrinks the reserve instead of cancelling the eBay phase outright. So for large batches the window, not memory or budget, is usually what stops you: a measured `maxItems=100` run delivered 100 source rows and 89 complete supplier shortlists, then cut the last 7 items with `trigger: "deadline"` (`skippedBy.deadline: 7`) at a 457 MB peak. The run always stops gracefully, reports `partial: true` with the exact reason, and never bills an item that did not reach a terminal state. When the reason is the clock, `RUN_SUMMARY.deadlineKind` says which of two budgets ran out, and the remedies are opposite: `platform-timeout` means Apify really ended the run, so send a smaller batch; `phase-window` means this Actor's own reserve closed the eBay phase while platform time was still on the clock, so raise the run timeout. Split very large jobs across runs if you need every item's shortlist.
- **Your report says whether its own money figures can be trusted.** `RUN_SUMMARY.billing.pricingSource` is `platform` when the prices in `billing.prices` are the ones this run was actually issued — the only case where `chargedUsd` can be reconciled line by line. If the Actor could not read its own pricing it says `platform-read-failed`, puts `PRICING_READ_FAILED` in `notes`, and reserves 3x per event at the budget gate so the run stops early instead of overspending: the platform charges its own price whatever the process believes, and an undelivered tail can be reported while an overcharge cannot be handed back.
- **Public data only.** No login, no cookies from you, no personal data, no buyer review text. Seller store names and links are public storefront attributes; individual persons' names and contact details are not collected.

### Disclosure

> Finds wholesale suppliers on 1688.com for eBay items using image search. Scrapes publicly available data without login. Not affiliated with eBay or Alibaba. Users are responsible for complying with the applicable terms of service.

> You are billed only for rows actually delivered. Failed, blocked and empty **eBay lookups** are never billed. A lookup whose eBay fetch succeeded is billed even if 1688's image search then returns risk control for it — see `skippedBy.blocked` above.

> Image matches include both exact and loosely-similar results; use the `relevantMatch` flag and `similarityRank` to filter. Margin figures are indicative estimates.

### Files

- `src/main.js` — orchestration, gates, billing, challenge statistics, completeness
- `src/ebay-client.js` — pure-HTTP eBay leg (sticky sessions, splashui wait + rotation, four marketplaces)
- `src/ebay-parse.js` — detail-page parser, challenge-shell detection, s-l1600 image URL handling
- `src/relevant-match.js` — EN↔CN title-overlap heuristic behind the `relevantMatch` flag, plus the German-stem bridge
- `src/margin.js`, `src/fx.js` — multi-currency unit economics and the CNY FX snapshot
- `src/image-convert.js` — magic-byte detection and JPEG normalization
- `src/image-search.js`, `src/image-search-probe.js` — 1688 reverse image search engine
- `src/token-cache.js` — cross-run mtop token cache
- `src/billing.js` — pay-per-event ledger

# Changelog

This Actor's version history is a separate document: https://apify.com/crawleast/ebay-to-1688-sourcing-bridge/changelog.md

# Actor input Schema

## `ebayUrls` (type: `array`):

Public eBay item URLs (ebay.com/itm/... or /p/...) from any of the four supported marketplaces (.com, .co.uk, .de, .com.au), up to 100 per run. Each successfully looked-up item costs one $0.005 ebay-item-lookup event; blocked and not-found lookups are free. Batch items in one run to pay the $0.00005 run base fee once. ebay.ca URLs are rejected before any request and are not charged (reason unsupported_marketplace_ebay_ca).

## `itemIds` (type: `array`):

Numeric eBay item IDs (the number in the item URL), up to 100 per run. Items are fetched from the selected marketplace. Billing is identical to ebayUrls: $0.005 per successfully looked-up item, failures are free. Mix with ebayUrls and imageUrls in one run.

## `imageUrls` (type: `array`):

Public product photo URLs (jpg/png/webp/avif, about 1 MB or less), up to 100 per run. These skip the eBay lookup entirely: no ebay-item-lookup fee and no eBay price, so margin fields are null. Mix with ebayUrls/itemIds to process both kinds in one run. Note: relevantMatch is false on every image-only row, because relevance is judged by comparing titles and an image-only item has no eBay title; mix in ebayUrls/itemIds if you need that flag.

## `marketplace` (type: `string`):

Which eBay site to fetch bare item IDs from. URL inputs always use their own host's marketplace; this setting only fills in bare item IDs. Each marketplace is fetched through a residential exit in its own country (US/GB/DE/AU). Prices and margins come back in the currency the listing's price is denominated in — normally the marketplace's currency (USD, GBP, EUR or AUD); a listing priced in a foreign currency keeps its own. ebay.ca is not supported: the site serves an interactive human-verification challenge to every automated session tested (10/10 across US and CA residential exits), so .ca URLs are rejected before any request, uncharged, with reason unsupported_marketplace_ebay_ca.

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

Global cap across all three inputs combined (URLs, IDs and images), first come first served. Each delivered item costs one $0.005 ebay-item-lookup plus its supplier-match rows. Memory is not the binding limit: runs default to 1024 MB and a measured maxItems=100 run peaked at 457 MB. The run window is — the Actor caps its own working budget at 1,110 seconds, and a measured 100-item run delivered 89 complete supplier shortlists before cutting the last 7 with trigger="deadline" (partial: true, nothing unbilled). Split very large jobs across runs, and raise the run memory to 2048 MB only if you also raise maxSuppliersPerItem well above its default.

## `maxSuppliersPerItem` (type: `integer`):

How many visually matching 1688 offers to return per item, in similarity order. Each delivered match row costs $0.008 (FREE tier); matches cut by your maxTotalChargeUsd are not charged. Image matches include both exact and loosely-similar results — filter on the relevantMatch flag: it is true only when the offer title shares meaningful product vocabulary with the item title (similarityRank orders rows but never marks them relevant), and service-type offers (processing/recycling/franchise) are always false.

## `maxTotalChargeUsd` (type: `number`):

Hard budget cap for this run including the run base fee. When the next item cannot be delivered within the cap, the run stops gracefully, marks the remaining items in skippedBy.budget and reports trigger="budget". Minimum 0.04 (Apify platform limit). Budgeting note: the eBay leg is billed per looked-up item and the 1688 leg per delivered supplier row — with a very small cap (under ~$0.10) send only 1-2 items, or the cap may be consumed by item lookups before many supplier rows can be delivered.

## `maxRequestsPerRun` (type: `integer`):

Request quota guard across both legs, metered as per-item reserved slots (each item reserves its eBay navigations, challenge retries, image download and 1688 upload/search calls). When the remaining slots cannot cover the next item, remaining items are marked skippedBy.quota and trigger="quota". The default 1,200 covers 100 items with retry headroom.

## Actor input object example

```json
{
  "ebayUrls": [
    "https://www.ebay.com/itm/186933747946",
    "https://www.ebay.co.uk/itm/126530835779"
  ],
  "itemIds": [
    "186933747946"
  ],
  "imageUrls": [
    "https://i.ebayimg.com/images/g/L5UAAOSwZENnBSxu/s-l1600.jpg",
    "https://i.ebayimg.com/images/g/eEcAAOSwBBJkwHb8/s-l1600.jpg"
  ],
  "marketplace": "com",
  "maxItems": 20,
  "maxSuppliersPerItem": 10,
  "maxTotalChargeUsd": 2,
  "maxRequestsPerRun": 1200
}
```

# Actor output Schema

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

Source rows (eBay lookups), match rows (1688 suppliers with unit economics) and per-item summaries, in delivery order.

## `sources` (type: `string`):

The dataset filtered to source rows: title, main image, price and currency, condition, sold count, seller and fetch status.

## `matches` (type: `string`):

The dataset filtered to match rows: 1688 offer, wholesale/consign price, MOQ, factory flags, relevantMatch flag and estimated unit margin.

## `summaries` (type: `string`):

The dataset filtered to summary rows: match count, relevant match count, best margin, factory count and top margins per item.

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

Machine-readable report: requested/delivered counts, skipped items by reason, coverage metrics, challenge statistics, request usage, FX rates and billed amount.

# 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 = {
    "ebayUrls": [
        "https://www.ebay.com/itm/186933747946",
        "https://www.ebay.com/itm/195907826307",
        "https://www.ebay.co.uk/itm/126530835779"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("crawleast/ebay-to-1688-sourcing-bridge").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 = { "ebayUrls": [
        "https://www.ebay.com/itm/186933747946",
        "https://www.ebay.com/itm/195907826307",
        "https://www.ebay.co.uk/itm/126530835779",
    ] }

# Run the Actor and wait for it to finish
run = client.actor("crawleast/ebay-to-1688-sourcing-bridge").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 '{
  "ebayUrls": [
    "https://www.ebay.com/itm/186933747946",
    "https://www.ebay.com/itm/195907826307",
    "https://www.ebay.co.uk/itm/126530835779"
  ]
}' |
apify call crawleast/ebay-to-1688-sourcing-bridge --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,crawleast/ebay-to-1688-sourcing-bridge"
        }
    }
}
```

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/V5eYgCWR3qqOPFG4u/builds/pgHFcqVRuHEwZrjZb/openapi.json
