# Amazon Competitor Offer Matrix (`practical_ai_lab_jp/pal-amazon-competitor-offer-matrix`) Actor

Turn vendor offer rows you already have into a per-product comparison matrix with one currency, your own fee estimates and clear missing-data warnings.

- **URL**: https://apify.com/practical_ai_lab_jp/pal-amazon-competitor-offer-matrix.md
- **Developed by:** [Practical AI Lab](https://apify.com/practical_ai_lab_jp) (community)
- **Categories:** E-commerce, Developer tools
- **Security:** Limited permissions
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $2.00 / 1,000 matrix rows

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

## Amazon Competitor Offer Matrix

Turn vendor offer rows you **already have** (exports from spreadsheets, other datasets or your own tools) into one comparison row per product: all prices in a single currency, your own fee estimates applied, the cheapest all-in vendor highlighted, and explicit warnings wherever data is missing.

This Actor does **not** visit Amazon or any other website. It makes no network requests and uses no proxies or paid APIs. It only processes the rows you pass in.

### What you get

One dataset item per product:

- `bestVendor`, `bestAllInCost`, `highestAllInCost`, `savingsVsHighest`, `spreadPercent`
- `cells`: one entry per vendor with converted `price`, `shipping`, `estimatedFee`, `allInCost`, `sourceCurrency`, `feeSource` and per-offer warnings
- `warnings`: human-readable list such as `MISSING_FX_RATE`, `MISSING_SHIPPING`, `AMBIGUOUS_SYMBOL`, `CURRENCY_ASSUMED`, `SHIPPING_ASSUMED`, `DUPLICATE_VENDOR_OFFER`, `UNAVAILABLE`, `SMALL_SAMPLE`, `NO_COMPARISON`
- `sampleSizeNote`

A `SUMMARY` record in the key-value store lists skipped rows and warning counts.

All-in cost = converted price + shipping + estimated fee.

### Sample input

```json
{
  "offers": [
    {"asin": "EXAMPLE-001", "title": "Example Widget 2-Pack", "vendor": "Vendor A", "price": "$24.99", "shipping": "free"},
    {"asin": "EXAMPLE-001", "title": "Example Widget 2-Pack", "vendor": "Vendor B", "price": "21,50 €", "shipping": "3,00 €"},
    {"asin": "EXAMPLE-001", "title": "Example Widget 2-Pack", "vendor": "Vendor C", "price": 26.4, "currency": "USD"}
  ],
  "targetCurrency": "USD",
  "fxRates": {"EUR": 1.08},
  "vendorFees": {"Vendor A": {"percent": 15, "fixed": 0.3}, "Vendor B": {"percent": 8}},
  "defaultShipping": 4.0
}
```

The values above are illustrative placeholders, not real market data. Vendor C has no shipping, so `defaultShipping` is used and flagged with `SHIPPING_ASSUMED`.

### Input notes

- **offers**: array of row objects. Recognized columns include `asin`/`productId`/`sku`/`gtin`, `vendor`/`seller`/`merchant`, `title`/`name`, `price`, `currency`, `shipping`, `url`, `availability`. Use `fieldMap` for other column names.
- **fxRates**: rates you provide (target-currency units per one unit of the listed currency). Nothing is downloaded, so you control the rate date.
- **Fees**: `defaultFeePercent`, `defaultFeeFixed` and `vendorFees` are your own estimates, applied to the item price. The fixed part is in the target currency. They are not official fee schedules.
- Rows are grouped by product id, or by a normalized title when no id is present.

### How offers are ranked

Only offers with a readable price, a known currency, an FX rate, a shipping value and an in-stock status are ranked. Everything else stays in `cells` with `comparable: false` and a warning. If a vendor appears several times for one product, the lowest comparable row is shown.

### Pricing

The Actor reports one `matrix-row` event per matrix row written to the dataset, after the rows are stored. Invalid input fails before anything is written or charged. No payable plan is configured in this repository.

### Limitations

- Results reflect only the rows you supply. A matrix built from two or three vendors is a small sample; the Actor warns when fewer than three vendors are comparable and cannot compare with fewer than two.
- Input prices may be stale; the Actor cannot verify them or check live availability.
- Price parsing is heuristic. `1,234` is read as one thousand two hundred thirty-four, `12,50` as 12.50. Ranges such as `10 - 20` are rejected. The symbols `$` and `¥` are ambiguous and are flagged; put an explicit `currency` on each row when it matters.
- Same-product matching relies on identical ids or identical normalized titles. Different titles for the same item will not merge.
- Fees are simple percentage plus fixed estimates; real marketplace fees can depend on category, size, tax and fulfillment, which are not modeled. Taxes and duties are not included.
- Output is not financial or pricing advice.

### Local tests

Tests are offline and use only the Python standard library:

```
python -m unittest discover -s tests
```

# Actor input Schema

## `offers` (type: `array`):

Rows you already exported from vendor or product datasets. Each row should have a product id (for example an ASIN) or a title, a vendor name, a price and ideally a currency and shipping cost. Nothing is fetched from any website.

## `targetCurrency` (type: `string`):

Three-letter code all prices are converted into.

## `fxRates` (type: `object`):

How many units of the target currency one unit of each listed currency is worth, for example {"EUR": 1.08}. No rates are downloaded. Offers in a currency without a rate are marked not comparable.

## `defaultCurrency` (type: `string`):

Optional. Used when a row has no currency; every use is flagged with a warning.

## `defaultShipping` (type: `number`):

Optional. Used when a row has no shipping cost; every use is flagged. If empty, offers without shipping are not ranked.

## `defaultFeePercent` (type: `number`):

Your own estimate of fees as a percentage of item price, applied to vendors not listed below. This is not an official fee schedule.

## `defaultFeeFixed` (type: `number`):

Your own estimate of a fixed fee per offer, in the target currency.

## `vendorFees` (type: `object`):

Optional per-vendor overrides, for example {"Vendor A": {"percent": 15, "fixed": 0.30}}. Vendor names are matched case-insensitively.

## `fieldMap` (type: `object`):

Optional. Map your column names to ours, for example {"price": "Buy Box Price"}. Supported keys: productId, vendor, title, price, currency, shipping, url, availability.

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

Upper limit of matrix rows produced. One row per product.

## Actor input object example

```json
{
  "offers": [
    {
      "asin": "EXAMPLE-001",
      "title": "Example Widget 2-Pack",
      "vendor": "Vendor A",
      "price": "$24.99",
      "shipping": "free"
    },
    {
      "asin": "EXAMPLE-001",
      "title": "Example Widget 2-Pack",
      "vendor": "Vendor B",
      "price": "21,50 €",
      "shipping": "3,00 €"
    },
    {
      "asin": "EXAMPLE-001",
      "title": "Example Widget 2-Pack",
      "vendor": "Vendor C",
      "price": 26.4,
      "currency": "USD"
    },
    {
      "asin": "EXAMPLE-002",
      "title": "Example Gadget Mini",
      "vendor": "Vendor A",
      "price": "$12.00",
      "shipping": 4.5,
      "availability": "In stock"
    },
    {
      "asin": "EXAMPLE-002",
      "title": "Example Gadget Mini",
      "vendor": "Vendor B",
      "price": "11,20 €",
      "shipping": "2,50 €",
      "availability": "Out of stock"
    }
  ],
  "targetCurrency": "USD",
  "fxRates": {
    "EUR": 1.08
  },
  "defaultFeePercent": 0,
  "defaultFeeFixed": 0,
  "vendorFees": {
    "Vendor A": {
      "percent": 15,
      "fixed": 0.3
    },
    "Vendor B": {
      "percent": 8
    }
  },
  "maxProducts": 200
}
```

# Actor output Schema

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

Download rows from the latest run in JSON/CSV.

# 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 = {
    "offers": [
        {
            "asin": "EXAMPLE-001",
            "title": "Example Widget 2-Pack",
            "vendor": "Vendor A",
            "price": "$24.99",
            "shipping": "free"
        },
        {
            "asin": "EXAMPLE-001",
            "title": "Example Widget 2-Pack",
            "vendor": "Vendor B",
            "price": "21,50 €",
            "shipping": "3,00 €"
        },
        {
            "asin": "EXAMPLE-001",
            "title": "Example Widget 2-Pack",
            "vendor": "Vendor C",
            "price": 26.4,
            "currency": "USD"
        },
        {
            "asin": "EXAMPLE-002",
            "title": "Example Gadget Mini",
            "vendor": "Vendor A",
            "price": "$12.00",
            "shipping": 4.5,
            "availability": "In stock"
        },
        {
            "asin": "EXAMPLE-002",
            "title": "Example Gadget Mini",
            "vendor": "Vendor B",
            "price": "11,20 €",
            "shipping": "2,50 €",
            "availability": "Out of stock"
        }
    ],
    "fxRates": {
        "EUR": 1.08
    },
    "vendorFees": {
        "Vendor A": {
            "percent": 15,
            "fixed": 0.3
        },
        "Vendor B": {
            "percent": 8
        }
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("practical_ai_lab_jp/pal-amazon-competitor-offer-matrix").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 = {
    "offers": [
        {
            "asin": "EXAMPLE-001",
            "title": "Example Widget 2-Pack",
            "vendor": "Vendor A",
            "price": "$24.99",
            "shipping": "free",
        },
        {
            "asin": "EXAMPLE-001",
            "title": "Example Widget 2-Pack",
            "vendor": "Vendor B",
            "price": "21,50 €",
            "shipping": "3,00 €",
        },
        {
            "asin": "EXAMPLE-001",
            "title": "Example Widget 2-Pack",
            "vendor": "Vendor C",
            "price": 26.4,
            "currency": "USD",
        },
        {
            "asin": "EXAMPLE-002",
            "title": "Example Gadget Mini",
            "vendor": "Vendor A",
            "price": "$12.00",
            "shipping": 4.5,
            "availability": "In stock",
        },
        {
            "asin": "EXAMPLE-002",
            "title": "Example Gadget Mini",
            "vendor": "Vendor B",
            "price": "11,20 €",
            "shipping": "2,50 €",
            "availability": "Out of stock",
        },
    ],
    "fxRates": { "EUR": 1.08 },
    "vendorFees": {
        "Vendor A": {
            "percent": 15,
            "fixed": 0.3,
        },
        "Vendor B": { "percent": 8 },
    },
}

# Run the Actor and wait for it to finish
run = client.actor("practical_ai_lab_jp/pal-amazon-competitor-offer-matrix").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 '{
  "offers": [
    {
      "asin": "EXAMPLE-001",
      "title": "Example Widget 2-Pack",
      "vendor": "Vendor A",
      "price": "$24.99",
      "shipping": "free"
    },
    {
      "asin": "EXAMPLE-001",
      "title": "Example Widget 2-Pack",
      "vendor": "Vendor B",
      "price": "21,50 €",
      "shipping": "3,00 €"
    },
    {
      "asin": "EXAMPLE-001",
      "title": "Example Widget 2-Pack",
      "vendor": "Vendor C",
      "price": 26.4,
      "currency": "USD"
    },
    {
      "asin": "EXAMPLE-002",
      "title": "Example Gadget Mini",
      "vendor": "Vendor A",
      "price": "$12.00",
      "shipping": 4.5,
      "availability": "In stock"
    },
    {
      "asin": "EXAMPLE-002",
      "title": "Example Gadget Mini",
      "vendor": "Vendor B",
      "price": "11,20 €",
      "shipping": "2,50 €",
      "availability": "Out of stock"
    }
  ],
  "fxRates": {
    "EUR": 1.08
  },
  "vendorFees": {
    "Vendor A": {
      "percent": 15,
      "fixed": 0.3
    },
    "Vendor B": {
      "percent": 8
    }
  }
}' |
apify call practical_ai_lab_jp/pal-amazon-competitor-offer-matrix --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,practical_ai_lab_jp/pal-amazon-competitor-offer-matrix"
        }
    }
}
```

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/aEVbZlll9CGE5PPNa/builds/ffaeqw7X7ahStk6hK/openapi.json
