# Cross-Retail Product Matcher (`lead.gen.labs/cross-retail-product-matcher`) Actor

Match Amazon and Walmart products using identifiers, models, specifications, titles, variants, brands, and images.

- **URL**: https://apify.com/lead.gen.labs/cross-retail-product-matcher.md
- **Developed by:** [LeadGen Labs](https://apify.com/lead.gen.labs) (community)
- **Stats:** 3 total users, 2 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $9.50 / 1,000 successful match decisions

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.

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

## Cross-Retail Product Matcher for Amazon and Walmart

Match product listings across Amazon and Walmart using GTIN, UPC, EAN, MPN, model numbers, brands, normalized titles, specifications, variants, pack quantities, conditions, and perceptual image fingerprints.

The Cross-Retail Product Matcher turns inconsistent retailer listings into explainable match decisions. Use it for product catalog matching, price-comparison data preparation, competitive retail intelligence, duplicate detection, ecommerce catalog enrichment, procurement, and marketplace analytics.

### What does the Cross-Retail Product Matcher do?

The Actor answers a practical question: **Do these retailer listings represent the same physical product?**

It accepts two product URLs or two product catalogs, extracts Amazon and Walmart product data when needed, normalizes the evidence, rejects critical contradictions, and returns one auditable decision per source product.

Possible decisions are:

- `EXACT_MATCH`: the same product and variant.
- `BASE_PRODUCT_MATCH`: the same product family, but a variant such as color, capacity, size, condition, or pack quantity differs.
- `POSSIBLE_MATCH`: evidence is promising but should be reviewed.
- `NOT_A_MATCH`: evidence is weak or a trusted field contradicts.

Every result includes confidence, component scores, matched signals, contradictions, and variant differences. A high title similarity cannot override a conflicting GTIN or model number.

### Why use product matching across retailers?

Amazon and Walmart can describe the same item differently. One listing might include a UPC, another a manufacturer part number, and another only a shortened title and image. Product matching connects those records so downstream systems can:

- Compare prices for the same item.
- Track retailer assortment and availability.
- Map competitor listings to internal SKUs.
- Merge supplier and marketplace catalogs.
- Detect duplicate products.
- Build shopping, affiliate, and comparison applications.
- Prepare normalized product data for analytics or AI agents.

### Quick start

1. Choose **Compare two products**.
2. Add one Amazon or Walmart product URL as the source.
3. Add the product you want to compare as the candidate.
4. Keep **Exact product and variant** selected.
5. Click **Start**.
6. Open the Dataset to inspect the decision and supporting evidence.

The Actor supports direct Amazon.com and Walmart.com product pages. Search pages and category pages are not product records and are not supported.

### Matching workflows

#### Compare two products

Use `PAIR` mode to compare two known products:

```json
{
  "mode": "PAIR",
  "products": [
    {"url": "https://www.amazon.com/dp/B0D1XD1ZV3", "externalId": "amazon-airpods"},
    {"url": "https://www.walmart.com/ip/17835006350", "externalId": "walmart-airpods"}
  ],
  "matchLevel": "EXACT_VARIANT"
}
```

#### Match two catalogs

Use `CATALOG` mode to select the best candidate for every source product:

```json
{
  "mode": "CATALOG",
  "sourceCatalog": [
    {
      "externalId": "SKU-1001",
      "title": "Apple AirPods Pro 3",
      "brand": "Apple",
      "identifiers": {"gtin": "0195950543799"},
      "attributes": {"color": "White", "packQuantity": 1}
    }
  ],
  "candidateProducts": [
    {"url": "https://www.walmart.com/ip/17835006350", "externalId": "WMT-17835006350"}
  ],
  "oneToOne": false,
  "maxCandidatesPerProduct": 25
}
```

Catalog entries can be product URLs, structured product objects, or a mixture of both. Structured input avoids retailer requests when you already have product data.

### How product matching works

The Actor uses a layered, deterministic process:

1. Validate the workflow, limits, IDs, and product records.
2. Reuse recent cached extractions when available.
3. Fetch uncached URLs with browser-impersonated `curl_cffi` requests.
4. Use Camoufox with sticky proxy sessions when a retailer challenges or incompletely renders a request.
5. Extract Amazon or Walmart identity, brand, title, identifiers, variants, specifications, and images.
6. Normalize punctuation, promotional words, units, colors, capacities, conditions, and pack quantities.
7. Rank catalog candidates using identifiers, brand, model, and title evidence.
8. Apply hard contradiction rules before fuzzy scoring.
9. Calculate an explainable weighted confidence score.
10. Save and charge only completed match decisions.

### Evidence used

| Evidence | Role |
| --- | --- |
| GTIN, UPC, EAN, ISBN | Strong global product identity |
| MPN and model number | Strong manufacturer identity |
| Brand | Prevents cross-brand false matches |
| Normalized title | Handles retailer naming differences |
| Specifications | Compares shared technical properties |
| Variant attributes | Distinguishes size, color, capacity, generation, and condition |
| Pack quantity | Prevents single-item and multipack mismatches |
| Image fingerprint | Supports visually similar listings without requiring equal image URLs |

Missing evidence is treated as unknown, not as a mismatch.

### Exact variant versus base product matching

Choose `EXACT_VARIANT` when each sellable variant must remain separate. A 128 GB phone and a 256 GB phone become `BASE_PRODUCT_MATCH`, not `EXACT_MATCH`.

Choose `BASE_PRODUCT` when variants should roll up into one product family. Variant differences are still included in the result, but a strong base-product match can be classified as exact for your selected workflow.

### Input reference

| Field | Type | Default | Description |
| --- | --- | --- | --- |
| `mode` | string | `PAIR` | `PAIR` or `CATALOG`. |
| `products` | array | Example pair | Exactly two entries for pair mode. |
| `sourceProducts` | array | `[]` | Source product URLs for catalog mode. |
| `candidateProducts` | array | `[]` | Candidate product URLs for catalog mode. |
| `sourceCatalog` | array | `[]` | Structured source records for API users. |
| `candidateCatalog` | array | `[]` | Structured candidate records for API users. |
| `matchLevel` | string | `EXACT_VARIANT` | Exact variant or base-product matching. |
| `exactMatchThreshold` | number | `0.9` | Automatic exact-match threshold. |
| `reviewThreshold` | number | `0.7` | Minimum possible-match threshold. |
| `oneToOne` | boolean | `false` | Prevent reuse of a selected candidate. |
| `maxCandidatesPerProduct` | integer | `25` | Highest-ranked candidates fully scored per source. |
| `maxProducts` | integer | `1000` | Safety limit across both catalogs. |
| `maxConcurrency` | integer | `3` | Concurrent retailer requests. |
| `maxRequestRetries` | integer | `2` | Retries before extraction failure. |
| `requestTimeoutSeconds` | integer | `40` | Direct request timeout. |
| `cacheMaxAgeHours` | integer | `24` | Maximum age of reusable product extraction. |
| `cacheStoreName` | string | `cross-retail-matcher-cache` | Named cache shared across runs. |
| `locale` | string | `en-US` | Browser and request locale. |

For API users, each product can include an `externalId`. IDs must be unique within the run and are returned in product references.

### Structured product format

A structured record requires a `title` plus at least one of `brand`, `modelNumber`, or `identifiers`.

```json
{
  "externalId": "MY-SKU-42",
  "title": "Example Noise Cancelling Headphones 256GB",
  "brand": "Example",
  "modelNumber": "EX-200-BLK",
  "identifiers": {
    "upc": "012345678905",
    "gtin": "0012345678905",
    "mpn": "EX-200-BLK"
  },
  "attributes": {
    "color": "Black",
    "packQuantity": 1,
    "condition": "New"
  },
  "specifications": {
    "Bluetooth": "5.3",
    "Battery life": "30 hours"
  },
  "imageHashes": ["ff00ff00ff00ff00"]
}
```

`imageHashes` accepts 64-bit hexadecimal perceptual hashes. URL extraction creates a hash from the primary image automatically when the image is accessible.

### Output example

```json
{
  "recordType": "MATCH_DECISION",
  "mode": "PAIR",
  "sourceId": "amazon-airpods",
  "candidateId": "walmart-airpods",
  "classification": "EXACT_MATCH",
  "confidence": 0.9482,
  "scores": {
    "identifier": 1,
    "brand": 1,
    "title": 0.89,
    "variants": 1,
    "images": 0.92
  },
  "matchedSignals": ["identifier", "brand", "title", "variants", "images"],
  "contradictions": [],
  "variantDifferences": [],
  "reviewRequired": false,
  "matchedAt": "2026-10-06T12:00:00Z"
}
```

Extraction failures are saved separately with `recordType: FAILURE`. Failed products do not produce or incur a match-decision charge.

### Run with the Apify API

```bash
curl -X POST \
  "https://api.apify.com/v2/acts/lead.gen.labs~cross-retail-product-matcher/runs?token=YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "mode": "PAIR",
    "products": [
      {"url": "https://www.amazon.com/dp/B0D1XD1ZV3"},
      {"url": "https://www.walmart.com/ip/17835006350"}
    ],
    "matchLevel": "EXACT_VARIANT"
  }'
```

Do not expose an Apify API token in client-side code or a public repository.

### Frequently asked questions

#### What is cross-retail product matching?

Cross-retail product matching is the process of deciding whether listings from different stores represent the same real-world product and variant. It combines exact identifiers with normalized text, attributes, specifications, and images.

#### Can it match Amazon products to Walmart products?

Yes. The first release includes Amazon.com and Walmart.com product extraction and also accepts structured records from other data sources.

#### Does an identical title guarantee a match?

No. Titles are supporting evidence. A conflicting GTIN, model number, capacity, pack quantity, or other critical attribute prevents an exact match.

#### What happens when identifiers are missing?

The Actor redistributes confidence across available evidence. Missing data is unknown. It does not automatically count as a mismatch.

#### Can I match my own ecommerce catalog?

Yes. Send structured source records with your SKU in `externalId`, then provide retailer URLs or structured products as candidates.

#### Does it support one-to-one catalog assignment?

Yes. Enable `oneToOne` to prevent one candidate from being selected for multiple successful source matches.

#### Is AI required?

No. Decisions are deterministic and explainable. This avoids hallucinated matches and keeps costs predictable. `POSSIBLE_MATCH` records identify cases that benefit from human or downstream AI review.

#### Why did a product extraction fail?

The URL may be unsupported, removed, incomplete, or blocked after all direct and browser retries. The failure is saved without charging a successful match decision.

# Actor input Schema

## `mode` (type: `string`):

Compare one source with one candidate, or match each source-catalog product against a candidate catalog.

## `products` (type: `array`):

The first entry is the source and the second is the candidate.

## `sourceProducts` (type: `array`):

Products that need a best-match decision in catalog mode.

## `sourceCatalog` (type: `array`):

Optional structured product objects for API and advanced users who already have catalog data.

## `candidateProducts` (type: `array`):

Products that each source product should be matched against.

## `candidateCatalog` (type: `array`):

Optional structured candidate objects for API and advanced catalog workflows.

## `matchLevel` (type: `string`):

Choose whether variant differences remain separate or roll up to the same base product.

## `exactMatchThreshold` (type: `number`):

Minimum confidence for an automatic exact match.

## `reviewThreshold` (type: `number`):

Scores from this value up to the exact threshold are marked for review.

## `oneToOne` (type: `boolean`):

Recommended when both catalogs contain one row per physical product.

## `maxCandidatesPerProduct` (type: `integer`):

Fully score only the highest-ranked candidates for each source product.

## `maxProducts` (type: `integer`):

Reject inputs whose combined source and candidate count exceeds this safety limit.

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

Higher values can finish faster but may increase retailer blocking.

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

Retry direct or browser requests before recording an extraction failure.

## `requestTimeoutSeconds` (type: `integer`):

Maximum duration of one direct retailer request.

## `cacheMaxAgeHours` (type: `integer`):

Reuse recent extractions to reduce runtime and retailer requests.

## `cacheStoreName` (type: `string`):

Stable private store used to reuse product extraction across runs.

## `locale` (type: `string`):

Locale used for retailer request and browser language settings.

## Actor input object example

```json
{
  "mode": "PAIR",
  "products": [
    {
      "url": "https://www.amazon.com/dp/B09B8V1LZ3"
    },
    {
      "url": "https://www.walmart.com/ip/17835006350"
    }
  ],
  "sourceProducts": [],
  "sourceCatalog": [],
  "candidateProducts": [],
  "candidateCatalog": [],
  "matchLevel": "EXACT_VARIANT",
  "exactMatchThreshold": 0.9,
  "reviewThreshold": 0.7,
  "oneToOne": false,
  "maxCandidatesPerProduct": 25,
  "maxProducts": 1000,
  "maxConcurrency": 3,
  "maxRequestRetries": 2,
  "requestTimeoutSeconds": 40,
  "cacheMaxAgeHours": 24,
  "cacheStoreName": "cross-retail-matcher-cache",
  "locale": "en-US"
}
```

# Actor output Schema

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

No description

## `summary` (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 = {
    "products": [
        {
            "url": "https://www.amazon.com/dp/B09B8V1LZ3"
        },
        {
            "url": "https://www.walmart.com/ip/17835006350"
        }
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("lead.gen.labs/cross-retail-product-matcher").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 = { "products": [
        { "url": "https://www.amazon.com/dp/B09B8V1LZ3" },
        { "url": "https://www.walmart.com/ip/17835006350" },
    ] }

# Run the Actor and wait for it to finish
run = client.actor("lead.gen.labs/cross-retail-product-matcher").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 '{
  "products": [
    {
      "url": "https://www.amazon.com/dp/B09B8V1LZ3"
    },
    {
      "url": "https://www.walmart.com/ip/17835006350"
    }
  ]
}' |
apify call lead.gen.labs/cross-retail-product-matcher --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,lead.gen.labs/cross-retail-product-matcher"
        }
    }
}
```

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/fhUK9EJraSx7xaa0X/builds/ssPOSryuaVRAcR9qf/openapi.json
