# Amazon Price History Without Keepa: Build Your Own

> See Amazon price history with Keepa or camelcamelcamel, or build your own: scheduled scrapes appended to SQLite, daily min and median, and a chart. Tested code.

- URL: https://datatooly.xyz/guides/amazon-price-history-without-keepa/
- Author: Omar Eldeeb (https://datatooly.xyz/about/)
- Published: 2026-10-20
- Updated: 2026-10-20
- Free tool: [E-commerce price tracker — scrape product prices, stock and variants](https://datatooly.xyz/ecommerce-price-tracker/)
- Tags: amazon, python, sqlite, webscraping

To see an Amazon product's past prices today, use Keepa or camelcamelcamel: both chart price history on the product page and send price-drop alerts. To own the data instead (any Amazon storefront or other retailer, your own schedule, raw rows in your database), schedule a scraper and append every reading to SQLite. Your own history starts the day you start, so begin before you need it.

This guide covers what the two trackers offer, when your own history is the better choice, and a tested Python pipeline: [E-commerce Product Scraper Pro](https://apify.com/constructive_calm/ecommerce-product-scraper-pro?fpr=v77kxu) runs on Apify, rows go into SQLite, and a report prints the daily minimum and median with a step chart.

## What Keepa and camelcamelcamel offer

From their own browser-extension listings, checked on 2026-10-20:

| | Keepa | camelcamelcamel (The Camelizer) |
|---|---|---|
| History chart on the Amazon page | Yes: "View interactive price history charts directly on the Amazon product page" | Yes: "price history charts inside Firefox when browsing products at Amazon" |
| Alerts | Price drops below your target and back-in-stock | Email when the price drops to your target |
| Amazon storefronts | .com, UK, Germany, France, Japan, Canada, Italy, Spain, India, Mexico, Brazil | "United States, Canada, (most of) Europe, Australia" |
| Cost | "No account required to start"; see `keepa.com` for plans and prices | Described as a "free service" |
| Raw data | API with a Keepa API key | Not offered in the listing |

When they've been tracking a product for a while, their charts show prices from before you heard of it. Nothing you build today can match that. For a quick "is this a good price?" check on a single Amazon product, use them.

## When your own history is better

- **Other stores in the same table.** Keepa and camelcamelcamel are Amazon trackers. If you compare Amazon with Walmart, Target, IKEA or Shopify stores, one pipeline that writes every store's rows to one table is simpler.
- **Your own cadence.** Hourly readings in Prime Day week, daily otherwise, on exactly the products you choose.
- **Raw rows you keep.** Timestamps, stock state, currency and the run that produced each reading, ready to join with your own sales or to feed a model.
- **Fields a price chart doesn't show.** Each reading also carries `inStock`, `availability`, `stockQty` ("only 1 left"), `rating` and `reviewCount`.

The trade-off: no backfill. If you need last year's Prime Day prices, you need a tracker that was watching last year.

## Step 1: record Amazon prices on a schedule

The input, with product URLs from two storefronts (the actor supports `amazon.com` and `amazon.de`; the other 22 Amazon storefronts are best-effort and often need residential traffic):

```json
{
  "detailsUrls": [
    { "url": "https://www.amazon.com/dp/B088NRLMPV" },
    { "url": "https://www.amazon.com/dp/B09XS7JWHH" },
    { "url": "https://www.amazon.de/dp/B0CHYDJW36" }
  ],
  "includeVariants": false,
  "maxProductResults": 3
}
```

Run on 2026-10-20, it returned the Anker USB-C cable at $9.99 (`InStock`), the Sony WH-1000XM5 at $199.99 (`LimitedAvailability`) and a LISEN USB-C cable on `amazon.de` at €6.28. Each Amazon storefront is pinned to its own currency.

Save it as a task and add an [Apify schedule](https://docs.apify.com/platform/schedules?fpr=v77kxu) such as `30 7 * * *` (07:30 every day in the schedule's timezone; 30 runs in 30 days). Don't turn on `onlyChanges` for a history: you want a reading every day, including the days nothing changed.

## Step 2: append every run to SQLite

This pulls the rows of your latest successful runs from the Apify API and inserts them. The primary key (ASIN, storefront, timestamp) makes it safe to run as often as you like: rows already stored are skipped.

```python
"""Append every scheduled run's Amazon rows to SQLite. pip install apify-client"""
import os
import re
import sqlite3

from apify_client import ApifyClient

ACTOR = "constructive_calm/ecommerce-product-scraper-pro"   # or your saved task's ID (see below)
DB = sqlite3.connect("price_history.db")
DB.execute("""CREATE TABLE IF NOT EXISTS prices (
    asin TEXT, site TEXT, scraped_at TEXT, price REAL, currency TEXT,
    in_stock INTEGER, availability TEXT, name TEXT, run_id TEXT,
    PRIMARY KEY (asin, site, scraped_at))""")

client = ApifyClient(os.environ["APIFY_TOKEN"])
runs = client.actor(ACTOR).runs().list(desc=True, limit=200, status="SUCCEEDED").items
# For a saved task: client.task("<your-task-id>").runs().list(...)

added = 0
for run in runs:
    for row in client.dataset(run["defaultDatasetId"]).iterate_items():
        m = re.search(r"/dp/([A-Z0-9]{10})", row.get("url") or "")
        if row.get("recordType") != "product" or not m:
            continue
        cur = DB.execute(
            "INSERT OR IGNORE INTO prices VALUES (?,?,?,?,?,?,?,?,?)",
            (m.group(1), row["site"], row["scrapedAt"], row.get("price"), row.get("currency"),
             None if row.get("inStock") is None else int(row["inStock"]),
             row.get("availability"), row.get("name"), run["id"]))
        added += cur.rowcount
DB.commit()
total = DB.execute("SELECT COUNT(*), COUNT(DISTINCT asin || site) FROM prices").fetchone()
print(f"{len(runs)} runs read, {added} new rows, {total[0]} rows for {total[1]} products in the DB")
```

Run against my account's runs on 2026-10-20 (which include our October test runs of the actor):

```text
200 runs read, 10 new rows, 226 rows for 94 products in the DB
```

A second run straight after adds 0 rows unless a new run finished in between. For a scheduled task, read `client.task(...)` runs instead of the actor's, so other jobs don't mix in.

Keep `NULL` prices. One `amazon.de` reading came back with no price and the availability text "Dieser Artikel kann nicht an den von dir ausgewählten Zustellungsort versendet werden" (Amazon saying it can't deliver to the selected location). That isn't a price of zero and it isn't out of stock; the report below skips it.

## Step 3: daily min, median and a chart

Repeated checks on one day would outweigh quiet days in a median, so the report first keeps the last reading per UTC day:

```python
"""Min / median / max per product from price_history.db, plus a step chart. pip install matplotlib"""
import sqlite3
import statistics
from datetime import datetime

import matplotlib
matplotlib.use("Agg")
import matplotlib.pyplot as plt

DB = sqlite3.connect("price_history.db")
WINDOW = "-30 days"

print(f"{'product':<22}{'days':>5}{'min':>9}{'median':>9}{'max':>9}{'latest':>9}  vs median")
for asin, site in DB.execute("""SELECT asin, site FROM prices WHERE price IS NOT NULL
                                GROUP BY asin, site ORDER BY COUNT(*) DESC LIMIT 6"""):
    rows = DB.execute("""SELECT scraped_at, price FROM prices
                         WHERE asin=? AND site=? AND price IS NOT NULL
                           AND scraped_at >= strftime('%Y-%m-%dT%H:%M:%fZ', 'now', ?)
                         ORDER BY scraped_at""", (asin, site, WINDOW)).fetchall()
    daily = {t[:10]: p for t, p in rows}                  # last reading per UTC day
    prices = list(daily.values())
    if not prices:
        continue
    med, latest = statistics.median(prices), prices[-1]
    print(f"{site + ':' + asin:<22}{len(prices):>5}{min(prices):>9.2f}{med:>9.2f}{max(prices):>9.2f}"
          f"{latest:>9.2f}  {100 * (latest - med) / med:+.1f}%")

asin, site = "B0CHYDJW36", "amazon-de"                    # chart one product
rows = DB.execute("SELECT scraped_at, price FROM prices WHERE asin=? AND site=? AND price IS NOT NULL "
                  "ORDER BY scraped_at", (asin, site)).fetchall()
x = [datetime.fromisoformat(t.replace("Z", "+00:00")) for t, _ in rows]
y = [p for _, p in rows]
fig, ax = plt.subplots(figsize=(8, 3))
ax.step(x, y, where="post", label="price")
daily = list({t[:10]: p for t, p in rows}.values())
ax.axhline(statistics.median(daily), linestyle="--", label="median of daily prices")
ax.axhline(min(daily), linestyle=":", label="min")
ax.set_title(f"{site} {asin}")
ax.legend()
fig.autofmt_xdate()
fig.savefig("history.png", dpi=120, bbox_inches="tight")
print("chart saved: history.png")
```

Output on 2026-10-20:

```text
product                days      min   median      max   latest  vs median
amazon-us:B088NRLMPV      4     9.99     9.99     9.99     9.99  +0.0%
amazon-de:B0DXDTX47W      3    10.95    12.99    12.99    10.95  -15.7%
amazon-de:B0CHYDJW36      3     6.28     7.99     7.99     6.28  -21.4%
amazon-de:B0GQSYJDHM      2     5.09     5.09     5.09     5.09  +0.0%
amazon-gb:B088NRLMPV      3     7.49     9.49     9.49     7.49  -21.1%
amazon-de:B08D6NCQ1Z      2     9.38     9.38     9.38     9.38  +0.0%
```

These are three or four days of test data, so treat it as a demonstration of the mechanics, not of Amazon pricing. It already shows something real: the `amazon.de` cable read €6.28 on 17 October, €7.99 from late that evening through 18 October, and €6.28 again on 20 October. A 30-day median built on daily readings is what tells you whether a "deal" is below the usual price or just back to it.

Two cautions for the chart. A step line assumes the price held until the next reading, so a two-day gap draws a flat line you never actually saw. And `vs median` is only meaningful once you have a few weeks of days.

## What it costs

The actor is pay per event. From the README's pricing table, a product row costs $0.0048 on FREE and $0.0008 on GOLD, plus $0.005 per run start. `amazon.com` is reached through datacenter connections with no traffic fee:

- **100 `amazon.com` ASINs, daily:** FREE 100 × $0.0048 + $0.005 = $0.485 per day, **$14.55** for 30 days. GOLD 100 × $0.0008 + $0.005 = $0.085 per day, **$2.55** for 30 days.
- **`amazon.co.uk`** mostly needs residential traffic, 0.3-0.5 MB per product in the README's measurements, so the README's own example for 100 `amazon.co.uk` products is $0.46-$0.71 per run on GOLD.

Apify's free plan includes $5 of usage a month with no credit card required; the actor has no in-actor free trial.

## Amazon-specific limits

From the README and our tests: storefronts outside the US often need residential traffic; `amazon.de` delivered 84% and `amazon.co.uk` 95% of deliverable products in the latest cloud tests; a page that says the product can't ship to the proxy's location is retried on another route and otherwise delivered without a price and with a note (the `NULL` row above). Expect some run-to-run variation, and read the Status dataset when a product is missing.

## During Black Friday and Prime events

Amazon's big events are where a history pays off: Black Friday 2026 is Friday 27 November and Cyber Monday 30 November. In 2025 Amazon's US "Black Friday Week" deals started on 20 November and ran through Cyber Monday, 1 December ([About Amazon](https://www.aboutamazon.com/news/retail/when-is-black-friday-2025)); the 2026 dates are usually announced in the weeks before. Start daily readings now so you have about a month of baseline, then add a second, temporary schedule on the same task for the event week (for example every three hours) and delete it afterwards. Summarising to one price per day keeps that burst from skewing your medians. The [Prime Day price tracker](https://datatooly.xyz/guides/prime-day-price-tracker/) covers Prime Day and Prime Big Deal Days, and the [holiday sales calendar](https://datatooly.xyz/guides/holiday-sales-calendar-price-tracking/) lists every event's dates and baseline start.

Related: [the Amazon price scraper guide](https://datatooly.xyz/guides/amazon-price-scraper-asin/) (ASIN inputs across marketplaces), [price drop alerts in Slack](https://datatooly.xyz/guides/price-drop-alerts-email-slack/), and the [e-commerce price tracker query builder](https://datatooly.xyz/ecommerce-price-tracker/).

*Disclosure: I built the actor and this site. Keepa and camelcamelcamel aren't affiliated with either; the SQLite pipeline works with rows from any scraper.*

Sources: [Keepa extension listing (Firefox Add-ons)](https://addons.mozilla.org/en-US/firefox/addon/keepa/), [The Camelizer extension listing (Firefox Add-ons)](https://addons.mozilla.org/en-US/firefox/addon/the-camelizer-price-history-ch/), [Keepa API backend on GitHub](https://github.com/keepacom/api_backend), [About Amazon: When is Black Friday 2025](https://www.aboutamazon.com/news/retail/when-is-black-friday-2025), [E-commerce Product Scraper Pro README](https://apify.com/constructive_calm/ecommerce-product-scraper-pro?fpr=v77kxu) (pricing, Amazon limits), [Apify docs: Schedules](https://docs.apify.com/platform/schedules?fpr=v77kxu), and actor runs on 17-20 October 2026.

## Quick answers

### How can I see the price history of an Amazon product?

Install a price-history extension such as Keepa or The Camelizer (camelcamelcamel's), or look the product up on their websites; both draw a chart on the Amazon product page. For history you control, schedule a scraper to record the price regularly and store each reading, which only covers the time since you started.

### Is there a free alternative to Keepa?

camelcamelcamel is a free Amazon price tracker with price history charts and email price-drop alerts, according to its own browser-extension listing. Keepa itself says 'No account required to start' on its extension listing. If you need the raw data in your own database, a scheduled scraper is the other route.

### Can I download Amazon price history data?

Keepa offers an API that requires an API key from Keepa. Otherwise, the reliable way to get raw rows is to collect them yourself on a schedule. That gives you exact timestamps and stock state, but no history from before you started.

### How often should I record Amazon prices?

Once a day is enough for a history and a 30-day median. Record more often (every few hours) only around events such as Prime Day or Black Friday, and summarise to one price per day before you compute medians, so a burst of checks doesn't outweigh quiet days.

## Related guides

- [Retail Sales Calendar 2026-2027: When to Track Prices](https://datatooly.xyz/guides/holiday-sales-calendar-price-tracking/)
- [Amazon Price Scraper: Prices by ASIN on Any Marketplace](https://datatooly.xyz/guides/amazon-price-scraper-asin/)
- [Back in Stock Alert Scraper: Products, Sizes, Colours](https://datatooly.xyz/guides/back-in-stock-alert-scraper/)
- [Back to School Price Tracker: Tax Holidays & Laptops](https://datatooly.xyz/guides/back-to-school-price-tracker/)
- [Compare Black Friday Prices Across Stores (Google Shopping)](https://datatooly.xyz/guides/black-friday-google-shopping-price-comparison/)
- [Black Friday Price Tracker: Watch 2026 Prices Daily](https://datatooly.xyz/guides/black-friday-price-tracker/)
- [Boxing Day Sales Tracker UK 2026: Prices in GBP](https://datatooly.xyz/guides/boxing-day-sales-price-tracker-uk/)
- [Christmas Price Tracker: When to Buy Gifts Cheapest](https://datatooly.xyz/guides/christmas-price-tracker/)
- [Competitor Price Monitoring: Scraper Setup and Cost](https://datatooly.xyz/guides/competitor-price-monitoring-scraper/)
- [Cyber Monday Price Tracker: Hourly Checks for Nov 30](https://datatooly.xyz/guides/cyber-monday-deal-tracker/)
- [Grocery Price Tracker: Build a Basket Index in Python](https://datatooly.xyz/guides/grocery-price-tracker-scraper/)
- [MAP Monitoring: Find Minimum Advertised Price Violations](https://datatooly.xyz/guides/map-price-monitoring/)
- [Price Drop Alerts to Slack or Email with a Scraper](https://datatooly.xyz/guides/price-drop-alerts-email-slack/)
- [Prime Day Price Tracker: ASINs, Big Deal Days, 2027](https://datatooly.xyz/guides/prime-day-price-tracker/)
- [Ramadan Sale Price Tracker 2027: Noon & Amazon UAE](https://datatooly.xyz/guides/ramadan-eid-sales-price-tracking/)
- [Scrape Product Prices with Python from JSON-LD](https://datatooly.xyz/guides/scrape-product-prices-json-ld-python/)
- [Scrape Product Reviews from Walmart, Target and Amazon](https://datatooly.xyz/guides/scrape-product-reviews-walmart-target-amazon/)
- [Scrape Product Variants: Sizes, Colors and Price per SKU](https://datatooly.xyz/guides/scrape-product-variants-sizes-colors/)
- [Fake Black Friday Deals: Check the Real Discount](https://datatooly.xyz/guides/spot-fake-black-friday-discounts/)
- [Apify E-commerce Scraping Tool Alternative: Same Input](https://datatooly.xyz/guides/switch-from-apify-ecommerce-scraping-tool/)
- [When Do Black Friday Prices Drop? The 2026 Timeline](https://datatooly.xyz/guides/thanksgiving-black-friday-price-drop-timeline/)
- [Walmart and Target Price Scraper: Search and Product Pages](https://datatooly.xyz/guides/walmart-target-price-scraper/)
- [White Friday & 11.11 Tracker: Noon and Amazon UAE Prices](https://datatooly.xyz/guides/white-friday-11-11-noon-amazon-uae-prices/)
- [Shopify products.json Pagination: 250 Limit and the 25,000 Cap](https://datatooly.xyz/guides/shopify-products-json-pagination/)
- [AI Share of Voice: Measure It in ChatGPT and Perplexity](https://datatooly.xyz/guides/ai-share-of-voice-chatgpt-perplexity/)
