# Keepa Scraper — Amazon Price History, BSR, Offers & Deals (`memo23/keepa-scraper`) Actor

Keepa Scraper for Amazon: paste ASINs, Amazon or Keepa links and get one row per product with Buy Box and lowest prices, sales rank, monthly sales, ratings and a price trend with a 0-100 deal score. Keepa deals links work too. Price history and seller offers are optional add-ons.

- **URL**: https://apify.com/memo23/keepa-scraper.md
- **Developed by:** [Muhamed Didovic](https://apify.com/memo23) (community)
- **Categories:** E-commerce
- **Stats:** 3 total users, 2 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $5.00 / 1,000 products

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

## Keepa Scraper — Amazon Price History, BSR, Offers & Deals

Paste a list of Amazon products, or a Keepa deals link, and get back one clean row per product or per deal. The rows come straight from Keepa's API, so you get Keepa's tracking data (price history, sales rank, offers) without a Keepa subscription or any code.

On top of Keepa's own numbers, every product row carries a `trend` block worked out by this actor: is the price rising, falling or flat, how far it moved in 30 and 90 days, and a 0-100 deal score that says how cheap today's price is compared with the product's own history.

### What one run gives you

- **Any mix of inputs in one list.** Bare ASINs, Amazon product links, Keepa product links and Keepa deals links. Each one is recognised on its own; repeats are fetched once.
- **11 Amazon sites.** amazon.com, .co.uk, .de, .co.jp, .ca, .fr, .it, .es, .in, .com.mx and .com.br. A link brings its own site; bare ASINs use the site you pick.
- **Prices now and over time.** Buy Box, lowest new, lowest used, Warehouse, FBA and FBM, list price, refurbished, collectible and more, plus dated history series when the history add-on is on.
- **Demand signals.** Best Sellers Rank, sales-rank drops over 30, 90, 180 and 365 days, Amazon's monthly-sold estimate, star rating and rating count.
- **Sellers and stock.** Up to 40 live offers per product with seller ID, condition, FBA and Prime flags, price, shipping and stock (add-on).
- **Deals.** Set your filters on keepa.com, copy the deals link, and the actor pages through the results, one row per deal with the drop against its average price.
- **Your column set.** Keep only the fields you need, or switch to Keepa's own field names if your pipeline already reads Keepa's API.

### What you can paste in

| You paste | You get |
| --- | --- |
| `B0BN72FYFG` | One product row, looked up on the Amazon site chosen in the settings |
| `https://www.amazon.com/dp/B0DZDBDCFH` | One product row; the site comes from the link |
| `https://keepa.com/#!product/3-B0FRYBZR91` | One product row; `3` is Keepa's code for amazon.de |
| `https://keepa.com/#!deals/...` | One row per deal on each page read, up to the page limit |

Amazon links are read in the `/dp/`, `/gp/product/` and `/product/` forms. Anything the actor cannot read (a search page, a store front, a typo) is skipped and named in the run log, and the rest of the list still runs.

Keepa uses these site codes in its own product links: 1 amazon.com, 2 amazon.co.uk, 3 amazon.de, 4 amazon.fr, 5 amazon.co.jp, 6 amazon.ca, 8 amazon.it, 9 amazon.es, 10 amazon.in, 11 amazon.com.mx, 12 amazon.com.br.

### How a run works

<img src="readme-stuff/how-it-works-keepa.png" alt="How the Keepa Scraper works" width="1200" />

The list is sorted into product lookups and deals links first. Products are grouped by Amazon site and requested from Keepa 100 at a time, with 180 days of statistics. Keepa packs its histories into flat number arrays with prices in cents and times in its own minute count; the actor unpacks them into `{ seen_at, price }` points with ISO dates and normal currency amounts. Deals links are read page by page and stop at the first empty page. Every row is written to the dataset the moment it is ready, so a long run shows results from the start.

### Settings

Shown in the order of the input form.

| Field | Default | What it does |
| --- | --- | --- |
| `identifiers` | (required) | The products and deals links to fetch, one per line |
| `marketplace` | `us` | Amazon site for bare ASINs; ignored for links |
| `maxItems` | `0` | Total row cap across products and deals; `0` means no cap |
| `max_pages` | `1` | Pages read from each Keepa deals link |
| `output_fields` | `[]` | Keep only these top-level fields; empty keeps all |
| `raw_output` | `false` | Use Keepa's own field names and packed arrays |
| `include_offers_sellers` | `true` | Add offers, seller lists and stock (paid add-on) |
| `include_price_history` | `true` | Add dated price series (paid add-on) |

#### Three ways to run it

Look up two products with every add-on:

```json
{
    "identifiers": ["B0BN72FYFG", "https://www.amazon.com/dp/B0DZDBDCFH"]
}
```

Cheapest product rows, only the columns a price sheet needs:

```json
{
    "identifiers": ["https://keepa.com/#!product/3-B0FRYBZR91"],
    "include_price_history": false,
    "include_offers_sellers": false,
    "output_fields": ["asin", "title", "buybox_price", "new_price", "bsr", "monthly_sold", "trend"]
}
```

Five pages of a Keepa deals search, capped at 300 rows:

```json
{
    "identifiers": ["https://keepa.com/#!deals/<the link you copied from keepa.com>"],
    "max_pages": 5,
    "maxItems": 300
}
```

### Rows you get back

Every row starts with `data_captured_at` (when the actor wrote it) and `data_type`: `product_search` for a product, `deal` for a deal. Filter on `data_type` when one run mixes both.

#### A product row

A real row from our smoke test, shortened:

```json
{
    "data_captured_at": "2026-10-21T12:00:00.000Z",
    "data_type": "product_search",
    "asin": "B0BN72FYFG",
    "title": "Apple iPhone 14, 128GB, Midnight - Unlocked (Renewed)",
    "marketplace": "us",
    "currency": "USD",
    "brand": "Apple",
    "model": "iPhone 14",
    "product_url": "https://www.amazon.com/dp/B0BN72FYFG",
    "buybox_price": 299.95,
    "new_price": 298.94,
    "used_price": 252.99,
    "list_price": 389.0,
    "monthly_sold": 6000,
    "bsr": 1,
    "sales_rank_drops30": 3,
    "sales_rank_drops90": 9,
    "rating": 4.0,
    "rating_count": 13859,
    "statistics": {
        "buybox_price": { "current": 299.95, "average_30_days": 304.45 }
    }
}
```

With the history add-on on, the same row also has series like this one:

```json
{
    "buybox_price_history": [
        { "seen_at": "2026-09-30T14:49:00Z", "price": 299.95, "shipping": 0.0 }
    ]
}
```

With the offers add-on on, it also has `offers`, one entry per live offer:

```json
{
    "offers": [
        { "seller_id": "A2ZA9A2J109LFF", "condition_name": "refurbished", "is_fba": false, "is_prime": false, "price": 299.95, "shipping": 0.0, "total_price": 299.95, "is_active": true }
    ],
    "total_offer_count": 41
}
```

#### A deal row

```json
{
    "data_captured_at": "2026-10-21T12:00:00.000Z",
    "data_type": "deal",
    "page": 0,
    "asin": "B0028OA2O0",
    "title": "Justice League: Justice on Trial (DVD) (AT)",
    "marketplace": "us",
    "metric": "buybox_price",
    "current_value": 5.97,
    "average_value": 47.56,
    "difference_from_average": 41.59,
    "difference_percent": 87,
    "deal_created_at": "2026-09-29T10:26:00Z"
}
```

Read it as: the Buy Box price is 5.97 against a 47.56 average, 87% under.

### The fields, by what you would use them for

**Identify the product:** `asin`, `parent_asin`, `title`, `brand`, `manufacturer`, `model`, `product_url`, `product_image_url`, `category_tree`, `product_group`, `binding`.

**Price right now:** `buybox_price`, `new_price`, `used_price`, `warehouse_price`, `new_fba_price`, `new_fbm_price`, `list_price`, `refurbished_price`, `collectible_price`, the four used-condition prices (`used_like_new_price` down to `used_acceptable_price`), `prime_exclusive_price`, `rent_price`, `trade_in_price`, `ebay_new_price`, `ebay_used_price`. Most have a matching `*_seen_at` with the time Keepa last saw that price.

**Price over time (history add-on):** a `*_history` series for most of the prices above, each a list of `{ seen_at, price }` points.

**Demand:** `bsr` with its category, `sales_ranks`, `sales_rank_drops30` / `90` / `180` / `365`, `monthly_sold`, `rating`, `rating_count`, `review_count`.

**Buy Box detail:** `buybox_seller_id`, `buybox_is_fba`, `buybox_is_amazon`, `buybox_is_prime_eligible`, `buybox_eligible_offer_counts`, `buybox_stats` and the used Buy Box equivalents.

**Sellers and stock (offers add-on):** `offers`, `total_offer_count`, `offer_count_fba`, `offer_count_fbm`, `stock_amazon`, `stock_buybox`, `seller_ids_lowest_fba`, `seller_ids_lowest_fbm`.

**Selling costs:** `fba_fees`, `referral_fee_percent`, `variable_closing_fee`, `suggested_lower_price`, `competitive_price_threshold`, `return_rate`.

The full list of top-level fields is in the "Keep only these columns" picker on the input form.

### The `trend` block

Worked out from the product's lowest-new-price history, which the actor always requests (so the block is there even with the history add-on off):

| Key | Meaning |
| --- | --- |
| `direction` | `rising` or `falling` when the 30-day move is over 2%, else `stable`; `unknown` when there is no price 30 days back |
| `current_price` | Latest lowest-new price |
| `change_30_days_pct`, `change_90_days_pct` | Move against the price 30 and 90 days earlier, in percent |
| `lowest_price`, `highest_price`, `average_price` | Across the whole history Keepa holds |
| `deal_score` | 0 = at its all-time high, 100 = at its all-time low; 50 when the price never changed |
| `data_points` | How many price points went into it |

`trend` is `null` when Keepa has fewer than two new-price points for the product.

### Billing

Pay per event. Each product row is one `product` event and each deal row one `deal` event. The two add-ons bill per product row on top: `price_history` for every product row while price history is on, and `offers_sellers` for each product row that came back with offers while the offers add-on is on. Deal rows never carry add-on events. Turning both add-ons off is the cheapest way to fetch products.

Runs on Apify's free plan look up the first 5 products and read 1 page of the first deals link.

### Questions

**Where does the data come from?**
From Keepa's API, called by the actor with its own key. You do not need a Keepa account.

**How fresh is it?**
As fresh as Keepa's tracking. `data_captured_at` is when the actor wrote the row; each price's `*_seen_at` is when Keepa last observed that price, which can be hours older for slow-moving products.

**Why is a price `null`?**
Keepa has no current value for it: nobody is selling in that condition, or Keepa does not track that price on that Amazon site.

**A line in my list was skipped. Why?**
It did not parse as an ASIN, a product link or a Keepa link. The run log names every skipped line. Amazon search pages and store fronts are not product links.

**Can I get the same product from two Amazon sites?**
Yes. Add it twice with the site in the link (`amazon.com/dp/...` and `amazon.de/dp/...`) or as two Keepa links with different site codes. The same ASIN on the same site is fetched once.

**Does the proxy setting matter?**
No. The actor only talks to Keepa's API and does not use a proxy.

### Support

Open an issue on the actor's Issues tab with the run ID and the line that misbehaved.

### 🤖 For AI Agents & LLM Apps

Purpose: Amazon product data and Keepa deals through Keepa's API, one row per product or deal.

Minimal input:

```json
{ "identifiers": ["B0BN72FYFG"], "include_price_history": false, "include_offers_sellers": false }
```

- `identifiers` takes ASINs, Amazon product links, Keepa product links and Keepa deals links, mixed.
- Rows carry `data_type` (`product_search` or `deal`); product rows include `trend.deal_score` (0-100, higher = cheaper vs own history).
- Use `output_fields` to cut rows down to the fields you need; history series make full rows large.
- Billing: one event per row, plus one per product row for each add-on left on. Both add-ons default to on.

### ⚠️ Disclaimer

This actor is an independent tool. It is not made, endorsed or supported by Keepa or by Amazon.com, Inc. or its affiliates; Keepa and Amazon are trademarks of their owners. It reads product and price data through Keepa's API and does not collect personal data. You are responsible for using the results in line with Keepa's and Amazon's terms and the law that applies to you, including data-protection law such as the GDPR.

### SEO Keywords

keepa scraper, keepa api alternative, keepa price history export, amazon price history, amazon price tracker, amazon buy box price, amazon bsr tracker, amazon sales rank history, amazon monthly sales estimate, amazon product research, asin lookup, asin to price, amazon offers and sellers, fba seller data, keepa deals export, amazon price drop finder, amazon deal score, amazon product data csv

# Actor input Schema

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

One entry per line. Accepted: a 10-character ASIN (B0BN72FYFG), an Amazon product link from any of the 11 supported Amazon sites (https://www.amazon.de/dp/B0FRYBZR91), a Keepa product link (https://keepa.com/#!product/3-B0FRYBZR91, where 3 is Keepa's code for amazon.de), or a Keepa deals link copied from keepa.com/#!deals after you set your filters there. Entries the actor cannot read are skipped and listed in the run log. The same product on the same Amazon site is fetched once, however many times it appears.

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

Which Amazon site a bare ASIN is looked up on, e.g. 'de' for amazon.de. Links carry their own site, so this setting is ignored for Amazon links, Keepa product links and Keepa deals links. Default: us (amazon.com).

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

Total cap on rows written to the dataset across products and deals together, e.g. 200. Leave at 0 to write every row the inputs produce.

## `max_pages` (type: `integer`):

How many result pages to read from each Keepa deals link, e.g. 5. Reading stops early when Keepa returns an empty page. Has no effect on product lookups. Free-plan runs read 1 page. Default: 1.

## `output_fields` (type: `array`):

Pick the top-level fields you want in each row, e.g. asin, title, new_price, bsr. data_captured_at and data_type are always kept. A field a row does not have is left out of that row. Empty (default) keeps every field. When 'Keepa field names' is on, pick from Keepa's own names; those are marked (raw).

## `raw_output` (type: `boolean`):

Off (default): readable field names, prices in currency units and history arrays turned into dated points. On: Keepa's own field names and packed arrays, for pipelines already built on Keepa's API. Add-ons that are switched off stay out either way. data_captured_at and data_type are added in both modes.

## `include_offers_sellers` (type: `boolean`):

On (default): each product row also gets the live offers (up to 40 per product) with seller IDs, condition, FBA/Prime flags, price, shipping and stock, plus offer counts and seller lists. Billed as one extra event per product that came back with offers. Buy Box price, Buy Box seller and offer statistics are in every row whether this is on or off.

## `include_price_history` (type: `boolean`):

On (default): each product row also gets dated price series for Amazon, New, Used, Buy Box, Warehouse, Refurbished, FBA, FBM, list price and more. Billed as one extra event per product row while this is on. Off: current prices, price statistics and the computed trend are still in every row.

## `proxy` (type: `object`):

Not needed and not used: the actor talks to Keepa's API directly. The field stays so saved inputs and tasks that include it keep working.

## Actor input object example

```json
{
  "identifiers": [
    "B0BN72FYFG",
    "https://www.amazon.com/dp/B0DZDBDCFH"
  ],
  "marketplace": "us",
  "maxItems": 0,
  "max_pages": 1,
  "output_fields": [],
  "raw_output": false,
  "include_offers_sellers": true,
  "include_price_history": true,
  "proxy": {
    "useApifyProxy": false
  }
}
```

# Actor output Schema

## `results` (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 = {
    "identifiers": [
        "B0BN72FYFG",
        "https://www.amazon.com/dp/B0DZDBDCFH"
    ],
    "output_fields": [],
    "proxy": {
        "useApifyProxy": false
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("memo23/keepa-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 = {
    "identifiers": [
        "B0BN72FYFG",
        "https://www.amazon.com/dp/B0DZDBDCFH",
    ],
    "output_fields": [],
    "proxy": { "useApifyProxy": False },
}

# Run the Actor and wait for it to finish
run = client.actor("memo23/keepa-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 '{
  "identifiers": [
    "B0BN72FYFG",
    "https://www.amazon.com/dp/B0DZDBDCFH"
  ],
  "output_fields": [],
  "proxy": {
    "useApifyProxy": false
  }
}' |
apify call memo23/keepa-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,memo23/keepa-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/vap25Og4PgGY0JA0A/builds/Y5TipOfQw6BUVKUrk/openapi.json
