# Retail Price Intelligence API - Competitor Prices, Matching (`nabeelbaghoor/retail-price-intelligence-api`) Actor

Read retail price intelligence: competitor price and discount statistics by site and category, keyword search statistics, SKU price history, similar product matches, competitor matches for uploaded SKUs, Google rank checks, MAP violation checks and attribute coverage. Bring your own key.

- **URL**: https://apify.com/nabeelbaghoor/retail-price-intelligence-api.md
- **Developed by:** [Nabeel Hassan](https://apify.com/nabeelbaghoor) (community)
- **Categories:** E-commerce, Business, Developer tools
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $5.00 / 1,000 match record returneds

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

## Retail Price Intelligence API - Competitor Prices, Matching

Turn a list of keywords, product names, identifiers or categories into a dataset: what competitors charge, how deep they discount, which products match yours, where you rank on Google, and which resellers are breaking your minimum advertised price.

### What it collects

- **Statistics**: aggregated price and discount averages, minimums, maximums and modes for one site and category, broken down by any attribute value such as fabric; and the same statistics per site for a keyword, with the matching product URLs.
- **MAP monitoring**: a MAP violation check of one item against every competitor listing, with each competitor's host, price, discount, URL and a violation flag.
- **Matching and history**: similar products close to a product name across the provider's tracked sites, with host, price and URL; day by day price, discount and availability history for one SKU; and, for SKUs already uploaded from your own provider account, every competitor match with its sellers, shipping prices and availability.
- **Search rank**: Google results for a query, restricted to one or more of your sites, each with its link, caption and rank.
- **Content analysis**: an attribute coverage review of a product name or URL that normalises it into standard attributes and scores the copy, and a built product description with the attributes it extracted.
- **Taxonomy**: the provider's full list of last level category paths, which is where the exact breadcrumbs the statistics service expects come from.

### Input

- **What to read**: one service per run. The default is the category list, which needs nothing else.
- **Identifiers**: one line per request. What a line means depends on the service, see the FAQ below.
- **Host, hosts, Google domain, country, MAP price, page and the other filters**: each is sent only to the services the provider documents it for. A filter set for a service that does not take it is left off and named in the log, never sent.
- **Extra query parameters**: any other parameter the provider documents, passed through exactly as written.
- **Maximum results**: a row cap for the whole run.
- **App key**: your own key, as a secret input. See "Do I need an API key?" below.

### FAQ

#### What is a retail price intelligence API used for?

Watching the market at a scale that a browser cannot. A brand runs its catalogue through the MAP violation check every morning to find which resellers advertised below the floor overnight. A retailer reads category statistics for its five biggest competitors to see where its own average price sits against theirs, and reads keyword statistics to see how deep each of them is discounting on the terms that matter. A marketplace seller runs its listings through the attribute coverage review to find the ones with copy too thin to rank. A pricing team pulls the price history of the competitor SKUs it is matched against to see how often they move. The common shape is a list in and a table of facts out, which is exactly what this actor is.

#### What goes in the identifiers input?

One line per request, and what the line means depends on the service. Keyword statistics reads a search keyword. The Google rank check reads a search query. Similar products reads a product name. Attribute coverage reads a product name or a product URL, and a line starting with `http` is sent as the URL while anything else is sent as the name, because the provider answers each differently. Category statistics reads a category breadcrumb. Price history reads the provider's `oid` of a SKU, which you get from a similar products row or a MAP check. The MAP violation check reads an item id. The description builder reads a line of product text. The category list and match results services read no identifiers at all: the first lists the whole taxonomy and the second pages through your own account.

#### What does a category breadcrumb look like?

A path through the provider's own taxonomy from the root to a last level category, joined with `>`, for example `Home > Fashion > Apparel > Dress > Dresses` or `Home > Fashion > Lingerie > Swimwear > Swimsuit`. It has to match exactly, spacing included, because the provider answers an unknown breadcrumb with `Category Not Found.` rather than a nearest guess. Run the category list service once and copy the paths from there. Category statistics can then be broken down by an attribute such as `fabric_tag` through the extra query parameters input, for example `{"fabric_tag": "[* TO *]"}`, which is the form the provider's own example uses.

#### What happens when the provider has nothing for a line?

It becomes its own row, marked `found: false`, with a note saying which of three things happened: the provider did not accept the line, quoting its reason; the provider does not know the category; or the request was fine and the answer was simply empty. A run over a hundred keywords therefore returns a hundred rows rather than eighty-eight, and you can see at a glance which twelve were empty. Miss rows are never charged.

#### Can this actor spend money or change anything in my provider account?

No. Every route it can reach is a read, sent as a GET. The provider also publishes POST routes that upload SKUs into your account for matching and start live scrapes, and none of them is wired up anywhere in this actor: the match results service only reads back matches for SKUs you uploaded yourself through the provider. That is a deliberate limit, because a scheduled run that could change what your account tracks is a scheduled run that eventually will.

#### Which routes were checked against the live host, and which documented ones are left out?

Every route in this actor was called on the production host on 2026-09-24 and again on 2026-09-25 with a deliberately invalid key of realistic length, and each one answered with the provider's own authentication refusal, which is how a served and guarded route looks from outside: the category list, similar products, attribute coverage, category statistics, keyword statistics, price history, match results, Google rank, MAP violation check and description builder.

The provider's collection also documents routes that are left out on purpose. The Amazon keyword, ISBN and UPC searches, the category attributes and attribute values lists, and the search transformation answered with real data without checking the key at all, so they cannot be offered in a bring-your-own-key actor. The full domain scraper, estimate pricing, pricing API and image based similar search answered with the web server's own 404 page. The product quality scores, image attribute extractor, ISBN and text reader, product reviews and categorization answered with a 502 from behind the web server. The new product launches route wanted a browser session rather than an app key. The live scraper, SKU upload, 360 degree pricing and visibility based pricing are POST routes and are not wired for that reason alone.

#### Do I need an API key?

Yes. This actor is bring-your-own-key and never ships a key of its own. Paste the app key from your provider account, or set it once as the `DATA_API_KEY` environment secret. Without one, the run ends cleanly with a message saying so before any request is sent, and a key the provider refuses ends the run the same way with the provider's own words quoted. The key travels as the `app_key` query parameter, because that is the only way the provider documents it. A key in a query string ends up in whatever logs sit between you and the provider, so nothing in this actor prints a request URL, and the key never appears in a row. Every route this actor calls checks the key, so a mistyped key is caught on the first call.

#### What does it cost?

Pay per result. An analysis record costs the most, because one row is a complete computed answer about one input: a statistics reading, a coverage review, a MAP check or a built description. A match record costs well under half that, because one request commonly returns dozens: similar products, Google results, price history days and uploaded SKU matches. A category path is priced near zero, because the taxonomy is a reference list read once and a single call returns well over a hundred entries. Platform usage is included, and requests the provider returns nothing for are never charged.

#### How fast will it run?

The provider allows 60 requests per minute per key, and the actor defaults to half of that so that anything else using the same key has room. A 429 is retried against the provider's `Retry-After` header when one is sent and with a backoff when not, and a run that keeps being limited stops with a message rather than hammering on. Raise the requests per minute input to 60 if nothing else is calling the key. The Google rank route fetches live results, so each of those calls takes a few seconds regardless of pacing.

### Example output

```json
{
  "service": "similarProducts",
  "serviceLabel": "Similar products",
  "endpoint": "/openapi/similars",
  "requested": "white recliner chair",
  "requestedAs": "name",
  "found": true,
  "recordType": "match",
  "retrievedAt": "2026-09-25T09:14:52.118Z",
  "name": "Portage Power Wall Hugger Recliner",
  "url": "https://www.thebay.com/product/palliser-portage-power-wall-hugger-recliner-0600091120997.html?dwvar_0600091120997_color=SUGAR_SHACK___PACIFIC",
  "price": 3999,
  "host": "www.thebay.com",
  "record": {
    "oid": "5f1c41508e705ab2b78a0de2_white",
    "host": "www.thebay.com",
    "name": "Portage Power Wall Hugger Recliner",
    "price": 3999,
    "url": "https://www.thebay.com/product/palliser-portage-power-wall-hugger-recliner-0600091120997.html?dwvar_0600091120997_color=SUGAR_SHACK___PACIFIC"
  },
  "note": null
}
```

### Keyword map

retail price intelligence API, competitor price monitoring API, price tracking API, product matching API, MAP monitoring API, minimum advertised price violation check, price history API, ecommerce competitive intelligence, digital shelf analytics, Google rank tracking API, share of shelf API, competitor discount analysis, category price statistics, keyword price statistics, similar product search API, product attribute coverage, product taxonomy API, retail category breadcrumbs, product description generator API, pricing intelligence data, ecommerce price comparison API.

# Actor input Schema

## `service` (type: `string`):

One service per run. Match services answer with many records for one input line: Google results, similar products, price history days and uploaded SKU matches. Analysis services answer with one computed record per line: statistics, attribute coverage, a MAP violation check or a built description. The category list is the provider's own taxonomy, which is where the exact category breadcrumbs the statistics service expects come from, and it needs no identifiers, so it is the default.

## `identifiers` (type: `array`):

One per line. What a line means depends on the service: a search keyword for keyword statistics; a search query for the Google rank check; a product name for similar products; a product name or a product URL for attribute coverage, where a line starting with http is sent as the URL and anything else as the name; a category breadcrumb such as "Home > Fashion > Apparel > Dress > Dresses" for category statistics; the provider's oid of a SKU for price history; an item id for the MAP violation check; and a line of product text for the description builder. The category list and match results services read no identifiers.

## `host` (type: `string`):

One site, as the provider names it, for example www.hm.com.us. Required by category statistics, and accepted by similar products, keyword statistics and the Google rank check to restrict the answer to that site.

## `hosts` (type: `array`):

Several sites for the Google rank check, one per line. Each is sent as its own host parameter, which is how the provider documents asking for more than one site's rank in a single call. Other services take a single host, so this is left off for them.

## `googleDomain` (type: `string`):

Which Google to rank against, for the Google rank check: google.com, google.co.uk, google.de and so on.

## `size` (type: `integer`):

How many similar products the provider should return for each product name.

## `stockStatus` (type: `string`):

Restrict keyword statistics to products with this stock status, as the provider names it.

## `perPage` (type: `integer`):

How many products the keyword statistics service should list per page.

## `page` (type: `integer`):

Which page to read. For keyword statistics this is the one page returned. For match results it is the page to start from, and the actor keeps reading later pages until the provider runs out or the maximum results is reached.

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

The country the SKUs were uploaded for, as the provider names it. Required by the match results service.

## `mapPrice` (type: `number`):

The minimum advertised price to check each item against. Required by the MAP violation check: every competitor below it is reported as a violation.

## `imageUrl` (type: `string`):

A product image for the description builder to read alongside each line of text.

## `breadcrumbPrefix` (type: `string`):

The breadcrumb parameter the provider documents for the category list, for example "Home > Fashion". Left blank, it is not sent and the whole list is returned.

## `extraParameters` (type: `object`):

Any other parameter the provider documents for the selected service that this actor does not have its own field for. Given as a JSON object and passed to the provider exactly as written. Category statistics takes attribute filters this way, for example {"fabric\_tag": "\[\* TO \*]"} to break the statistics down by fabric. Nothing here is invented, so check the name against the provider's own documentation first.

## `maxResults` (type: `integer`):

Stop after this many rows. The match services can return dozens of records for one line and the match results service pages through every uploaded SKU, so this is what bounds a long run.

## `requestsPerMinute` (type: `integer`):

Pacing ceiling. The provider allows 60 requests per minute per key, so the default sits at half that to leave room for anything else calling the same key. Raise it up to 60 if nothing else is.

## `apiKey` (type: `string`):

Your own app key from your provider account. This actor is bring-your-own-key and never ships a key of its own. Leave blank to use the DATA\_API\_KEY environment secret instead. The provider only documents the key as a query parameter, so that is how it travels.

## `baseUrl` (type: `string`):

Override the host the actor calls. Only useful for testing against a different environment.

## Actor input object example

```json
{
  "service": "categoryList",
  "googleDomain": "google.com",
  "maxResults": 500,
  "requestsPerMinute": 30
}
```

# Actor output Schema

## `records` (type: `string`):

One row per record, alongside the keyword, identifier or category that produced it.

# 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("nabeelbaghoor/retail-price-intelligence-api").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("nabeelbaghoor/retail-price-intelligence-api").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 nabeelbaghoor/retail-price-intelligence-api --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,nabeelbaghoor/retail-price-intelligence-api"
        }
    }
}
```

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/CTy5Xf2R08JnUG0Nd/builds/Tk0ElVGXPXI05gp1F/openapi.json
