Use case

Amazon price tracker: price history, alerts, the API and your own tracker

Track Amazon prices with Amazon's own price history, a free tracker, the API or your own script. We sent amazon.com 102 requests to see what breaks.

HProxy Team··Updated September 16, 2026·14 min read
HProxy.Use case

Free proxies won't hold up here.

Shared datacenter IPs get flagged and dropped fast. When it has to hold, gaming, streaming, accounts, you need mobile and residential IPs that read as a real device, from $0.44/GB, pay as you go.

Proxies for Amazon→

To track an Amazon price, open the product page and click the price history link next to the price. Amazon shows this history to customers in the U.S., UK, Canada and India. Alexa for Shopping can also alert you when a price reaches a number you set. For many listings on a schedule, sellers can call the Selling Partner API, and affiliates with enough sales can use the Creators API. Everyone else builds a tracker. A tracker of your own fails more often by reading the wrong price than by being blocked.

We checked each part of this page on 16 September 2026. We read Amazon's own pages and API documents. We also sent amazon.com 102 requests from our own server in Germany, to see what a tracker really gets back.

Does Amazon have its own price tracker?

Yes, for shoppers in four countries. On a product page, click the price history link right next to the price. Amazon says price history is available to all customers in the U.S., UK, Canada and India. It covers 30 and 90 days, and 365-day history is rolling out in the U.S., UK and India.

Alexa for Shopping shows the same history. Amazon built it by bringing together Rufus and Alexa+. You can ask it for a price alert in plain words, such as "Set a price alert for when this face cream is $75".

The Was price on a deal is a different number. Amazon defines the Typical or Was price as the median price paid on Amazon in the last 90 days. Prices paid during limited-time deals are left out. So the Was price is a middle value, not the highest price in the history.

Which other price trackers can you use?

Two trackers outside Amazon are CamelCamelCamel and Keepa. CamelCamelCamel describes itself as an Amazon US price tracker with price history, price watch and price drop alerts. Keepa says its API delivers complete price histories for Amazon marketplaces to your own code, on a subscription plan.

One shopper asked on Quora for a good tracker "if camelcamelcamel is often incorrect". Before you buy, compare the tracker's number with the price on the product page.

Can you use Amazon's API to track prices?

Only if you are an affiliate with sales or a seller.

The Product Advertising API 5.0 is closed. Amazon's notice says that applications that still call it "receive an HTTP 403 Forbidden response". The old documentation pages now redirect to that notice. The successor is the Creators API.

To use the Creators API, you must be in the Amazon Associates program for the marketplace. You also need "at least 10 qualifying sales within the past 30 days". Its GetItems call returns the buying price of an offer, for up to 10 ASINs per request. Access tokens last one hour, and it answers too many requests with HTTP 429. The pages we read give no number of requests per second.

Sellers have their own route. The Selling Partner API call getItemOffers "returns the lowest priced offers for a single item based on ASIN". Its limit is 0.5 requests per second with a burst of 1, which allows at most 1,800 calls an hour.

Keepa's API is the way to a ready history. Each product in a request costs one token, and tokens left unused for 60 minutes expire.

Way to trackWho can use itWhat you getThe limit
Price history link on the product pageShoppers in the U.S., UK, Canada and India30- and 90-day history, 365 days rolling outOne product at a time, by hand
Alexa for ShoppingAmazon shoppersPrice history and a price alert at your targetSet per product
CamelCamelCamel, KeepaAnyone; Keepa's API on a planPrice history; CamelCamelCamel lists drop alertsYou rely on their data and their timing
Creators APIAssociates with 10 qualifying sales in 30 daysOffer prices for up to 10 ASINs per callEligibility, and HTTP 429 when you ask too fast
SP-API getItemOffersSellersThe lowest priced offers for one ASIN0.5 requests per second
Your own trackerAnyone who can run a scriptAny listing, on your scheduleAmazon's terms, the delivery location, robot checks

Is it allowed to scrape Amazon prices?

Amazon's terms say no. Its Conditions of Use were last updated on 14 August 2026. They grant a license "to access and make personal and non-commercial use of the Amazon Services". The license then leaves out "any collection and use of any product listings, descriptions, or prices". It also leaves out "any use of data mining, robots, or similar data gathering and extraction tools".

Amazon's robots.txt points the same way. On 16 September 2026, it barred 99 named crawlers from the whole site, among them GPTBot, ClaudeBot and Scrapy. A new Scrapy project calls itself Scrapy and obeys robots.txt by default. Robots rules match that name without regard to case, so such a project fetches no Amazon page until you change those settings.

What this means for you depends on where you are, what you collect and what you do with it. This page does not give legal advice. It explains how each route works and what it costs you.

What breaks when you build your own tracker

We sent amazon.com 102 requests on 16 September 2026, between 11:56 and 12:06 UTC. They came from our own server in Germany and used only Python's standard library. The script asked for six listings, with Python's default headers and with the headers of a desktop browser.

Bar chart of the 102 requests. Probe 1, three Kindle pages eight seconds apart: 26 returned. Probe 2, Stanley and Crocs listings and then a 60-request burst: 68 returned and 4 error pages. Probe 3, one cookie session: 1 returned, 1 challenge with status 202 and 2 robot checks sent with status 200.
What amazon.com sent our server on 16 September 2026. Every product page came back with an empty price box. Source: our own measurement.

Blocks were not the first problem

95 of the 102 requests came back with the page or file. At one request every eight seconds, nothing was challenged. A burst of 60 requests to one listing passed as well. The challenge came later, in a new session that kept cookies. The home page came back with status 202 and a page of 2,010 bytes. After that, two product pages came back as a robot check.

The robot check arrives with status 200

The robot check said "Click the button below to continue shopping". Its status code was 200, and the page was 3,781 bytes long. A tracker that checks only the status code saves this page as a normal answer. Check the page itself: the robot check is a form that posts to /errors_page/validateCaptcha.

The price box was empty for our location

We never chose a delivery location, yet the pages said "Deliver to France". For that location, no product page showed its own price. The Stanley listings said "No featured offers available". The Kindle page said "This item cannot be shipped to your selected delivery location". With browser headers, the other prices on those pages were in euros. With Python's default headers, they were in dollars.

The first price on the page belongs to another product

A product page carries many prices: carousels, other colors and "customers also bought". On the Stanley Quencher page (B0BD76BTSN), the first price in the HTML was $44.54. That price sits on the carousel card of another listing, B0CRMXG22M. A reader that falls back to the first span.a-offscreen on the page saves $44.54 as the Stanley price.

What the empty price box needs

Amazon's help pages say the delivery location decides the product availability a page shows. The Kindle page asked for the same thing: "Please choose a different delivery location." So a tracker for amazon.com needs a US delivery location. There are two ways to get one. You can set a US location in the session, which is what the "Deliver to" selector does. Or you can send the requests from a US address, such as a server in the US or a proxy with a US exit.

We could not confirm either way on the day. Our attempt to set the ZIP code 10001 was stopped by the challenge, and we did not test a US exit. Check the "Deliver to" line on every page you save: the tracker below prints it. Our residential proxies include country targeting on every plan and are billed by the gigabyte. Our Amazon proxies page compares rotating residential, static ISP and datacenter proxies for Amazon.

Pace decides when the challenge comes

From one address, the robot check came after about 100 requests in 11 minutes. It did not go away quickly. 44 minutes later, the home page still came back as a page of 3,781 bytes, the same size as the robot check. If a tracker needs more checks per hour than that, spread them over more time or over more addresses.

Build a tracker that reads the right price

The tracker below reads the price only from the product's own price box. When there is no price, it says why, and it prints the "Deliver to" line of each page. It saves every check in a SQLite file and prints a drop below the lowest price it has seen.

You need Python 3 and two packages. We ran it with Python 3.13, requests 2.34.2 and beautifulsoup4 4.15.0.

python -m pip install requests beautifulsoup4
#!/usr/bin/env python3
"""A small Amazon price tracker: one listing at a time, history in SQLite.

It reads the price only from the product's own price box, and it names the
answer when there is no price:

  ok           a price was read from the price box
  robot_check  Amazon's "continue shopping" check, which arrives with status 200
  error_page   Amazon's error page, which points automated traffic to its API team
  cannot_ship  the listing does not ship to the delivery location Amazon picked
  no_offer     "No featured offers available": Amazon shows no main offer here
  no_price     none of the above, and no price in the price box

Every check also prints Amazon's "Deliver to" line, so you can see which
delivery location a price belongs to.

Usage:
  python amazon_price_tracker.py B0BD76BTSN B0CTS9WK49
  python amazon_price_tracker.py --proxy http://USER:PASS@HOST:PORT B0BD76BTSN
"""

import argparse
import random
import sqlite3
import time
from datetime import datetime, timezone

import requests
from bs4 import BeautifulSoup

HEADERS = {
    "User-Agent": (
        "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 "
        "(KHTML, like Gecko) Chrome/140.0.0.0 Safari/537.36"
    ),
    "Accept-Language": "en-US,en;q=0.9",
}

# The product's own price lives in one of these boxes. Any other price on the
# page belongs to a different product (carousels, "customers also bought").
PRICE_BOXES = [
    "#corePriceDisplay_desktop_feature_div",
    "#corePrice_feature_div",
    "#apex_desktop",
]


def read_page(html):
    """Return (price_text, state, deliver_to) for one product page."""
    if "/errors_page/validateCaptcha" in html:
        return None, "robot_check", ""
    if "automated access to Amazon data" in html:
        return None, "error_page", ""
    soup = BeautifulSoup(html, "html.parser")
    line = soup.select_one("#glow-ingress-line2")
    deliver_to = line.get_text(" ", strip=True) if line else ""
    if "cannot be shipped to your selected delivery location" in html:
        return None, "cannot_ship", deliver_to
    for box in PRICE_BOXES:
        tag = soup.select_one(f"{box} span.a-offscreen")
        if tag and tag.get_text(strip=True):
            return tag.get_text(strip=True), "ok", deliver_to
    if "No featured offers available" in html:
        return None, "no_offer", deliver_to
    return None, "no_price", deliver_to


def to_number(price_text):
    """'$1,249.99' -> 1249.99 (US listings only)."""
    return float(price_text.replace("$", "").replace(",", ""))


def check(session, asin, proxies=None):
    response = session.get(
        f"https://www.amazon.com/dp/{asin}",
        headers=HEADERS,
        proxies=proxies,
        timeout=30,
    )
    price, state, deliver_to = read_page(response.text)
    if state == "no_price" and response.status_code != 200:
        state = f"http_{response.status_code}"
    return price, state, deliver_to, response.status_code


def open_db(path):
    db = sqlite3.connect(path)
    db.execute(
        "CREATE TABLE IF NOT EXISTS prices "
        "(asin TEXT, checked_at TEXT, price REAL, state TEXT, http INTEGER)"
    )
    return db


def save(db, asin, price, state, status):
    db.execute(
        "INSERT INTO prices VALUES (?, ?, ?, ?, ?)",
        (
            asin,
            datetime.now(timezone.utc).isoformat(timespec="seconds"),
            to_number(price) if price else None,
            state,
            status,
        ),
    )
    db.commit()


def lowest_before(db, asin):
    row = db.execute(
        "SELECT MIN(price) FROM prices WHERE asin = ? AND state = 'ok'", (asin,)
    ).fetchone()
    return row[0] if row else None


def main():
    parser = argparse.ArgumentParser()
    parser.add_argument("asins", nargs="+")
    parser.add_argument("--proxy", help="http://USER:PASS@HOST:PORT, with an exit in the marketplace's country")
    parser.add_argument("--db", default="prices.db")
    args = parser.parse_args()

    proxies = {"http": args.proxy, "https": args.proxy} if args.proxy else None
    db = open_db(args.db)
    try:
        track(db, args.asins, proxies)
    finally:
        db.close()


def track(db, asins, proxies):
    with requests.Session() as session:
        for n, asin in enumerate(asins):
            if n:
                time.sleep(random.uniform(20, 40))
            before = lowest_before(db, asin)
            price, state, deliver_to, status = check(session, asin, proxies)
            save(db, asin, price, state, status)
            print(f"{asin}  {state:12} {price or '-':10} deliver to: {deliver_to or '?'}")
            if state == "ok" and before is not None and to_number(price) < before:
                print(f"  price drop: {price}, lowest before was ${before:,.2f}")
            if state == "robot_check":
                print("  robot check: stop here and try again later, more slowly")
                break
            if state in ("cannot_ship", "no_offer"):
                print(f"  no price for this delivery location ({deliver_to or 'unknown'}): amazon.com prices need a US one")


if __name__ == "__main__":
    main()

Save it as amazon_price_tracker.py and pass the ASINs you want to watch. The ASIN is the ten-character code that appears after /dp/ in a product link. To send the checks through a US exit, add --proxy:

python amazon_price_tracker.py B0BD76BTSN B0CTS9WK49
python amazon_price_tracker.py --proxy http://USER:PASS@HOST:PORT B0BD76BTSN

The tracker waits a random 20 to 40 seconds between listings. That is about 90 to 180 listings an hour from one address. Run it on a schedule, such as a cron job every few hours, and the SQLite file becomes your price history.

What each state means

StateWhat Amazon sentWhat to do
okA price inside the price boxSave it
robot_checkThe continue-shopping page, with status 200Stop and try again later, more slowly
error_pageAn error page that names automated accessCheck the ASIN and your pace
cannot_ship"This item cannot be shipped to your selected delivery location"Get a US delivery location first
no_offer"No featured offers available"Amazon shows no main offer for this location: get a US delivery location first
no_priceNo price and none of the aboveOpen the saved page and look

We tested the tracker on Amazon's real answers

We ran read_page on the 11 product answers we saved from our test, without network access. It returned a named state for all 11 and a price for none, because none of them had a filled price box. Where the page carried a "Deliver to" line, it said France. The first span.a-offscreen on those pages gave a number 7 times, and none of the 7 was the listing's own price. On a test file that copies Amazon's price markup, the reader returned $44.99 from the price box. The first price on that file was $12.49, from a carousel card.

We also ran the whole script offline, with saved answers in place of the network. It saved each answer to SQLite with its state, and it stopped at the robot check before the next listing. When the test file came back at $39.99, it printed a price drop from $44.99.

Output of our offline check. For each of the 11 saved amazon.com answers, the state our reader returned (error_page, no_offer, cannot_ship, robot_check, no_price), the Deliver to line (France where the page showed one) and the first price on the page, such as $44.54, $48.76, EUR34.64. On the markup fixture the reader returned $44.99 and the first price on the page was $12.49.
Our reader on the answers amazon.com sent our server, 16 September 2026, run offline. The right column is what a reader that takes the first price would have saved.

Where to go from here

For the choice of proxy type on Amazon, read proxies for Amazon. For price checks across many shops, see proxies for price monitoring. The Python requests guide covers the proxies setting, timeouts and errors in depth. For a larger crawler, how to avoid IP bans is the checklist.

What this page could not check

Every product page in our test came from a server in Germany, and Amazon hid the product's own price for the location it showed us, France. So the success path of the reader is tested on a file that copies Amazon's price markup, not on a live US page. Our attempt to set a US ZIP code was stopped by the challenge, and we did not test a residential or ISP exit. Our test covers one day, one address, 102 requests and six listings. Where Amazon starts to challenge depends on the address, cookies, headers and pace, so treat our numbers as one sample. The Creators API pages we read publish no request rate, and Amazon's articles on price history carry no date. Amazon's page markup, its terms and its API rules change, so we will check this page again by 15 December 2026.

Sources

  • Amazon, Conditions of Use, last updated 14 August 2026, read 16 September 2026: amazon.com Conditions of Use.
  • Amazon, robots.txt, as served on 16 September 2026: amazon.com/robots.txt.
  • Amazon Associates, PA-API 5 deprecation notice, read 16 September 2026: Creators API documentation.
  • Amazon Associates, Creators API introduction, read 16 September 2026: Creators API documentation.
  • Amazon Associates, Creators API GetItems reference, read 16 September 2026: Creators API documentation.
  • Amazon Associates, Creators API error codes and messages, read 16 September 2026: Creators API documentation.
  • Amazon Selling Partner API, getItemOffers reference, updated 10 September 2026: SP-API documentation.
  • Amazon, "What is Amazon's price history feature and how does it work?", read 16 September 2026: aboutamazon.com.
  • Amazon, how to use Alexa for Shopping, read 16 September 2026: aboutamazon.com.
  • Amazon, Meet Alexa for Shopping, read 16 September 2026: aboutamazon.com.
  • Amazon, how Amazon offers genuine deals to customers, read 16 September 2026: aboutamazon.com.
  • Keepa, API overview, and plans and tokens, read 16 September 2026: Keepa API documentation.
  • Scrapy documentation, settings ROBOTSTXT_OBEY and USER_AGENT, read 16 September 2026: docs.scrapy.org.
  • RFC 9110, HTTP Semantics, June 2022: rfc-editor.org/rfc/rfc9110.
  • RFC 9309, Robots Exclusion Protocol, September 2022: rfc-editor.org/rfc/rfc9309.
  • Amazon, "Understanding ASIN numbers", 4 June 2025: sell.amazon.com.
  • CamelCamelCamel, home page title, read 16 September 2026 (camelcamelcamel.com).
  • Our own measurement: 102 requests to amazon.com from our server on 16 September 2026, and an offline test of the reader on the saved answers, logged in the research folder for this page.

Frequently asked questions

Does Amazon show a product's price history?
Yes, in four countries. Amazon says price history is available to all customers in the U.S., UK, Canada and India. Click the price history link next to the price on the product page. It shows 30 and 90 days, and 365-day history is rolling out in the U.S., UK and India.
Can Amazon alert me when a price drops?
Yes, through Alexa for Shopping, which Amazon built from Rufus and Alexa+. You can ask it to set a price alert at a target price, such as when an item reaches $75. It also shows 30-, 90- and 365-day price history.
Can I still use the Product Advertising API to get Amazon prices?
No. Amazon deprecated PA-API 5, and its notice says calls to it now receive HTTP 403 Forbidden. The successor is the Creators API. It needs an Amazon Associates account with at least 10 qualifying sales in the past 30 days.
Why does my Amazon scraper return the wrong price?
Usually it reads the first price on the page. A product page carries many prices from carousels and other listings. In our test from a server in Germany, the product's own price box was empty on every product page, and the first price in the HTML belonged to another product every time. Read the price only from the price box.
Why does my scraper get a page with status 200 but no product?
That can be Amazon's robot check. In our test it came back with status 200, was 3,781 bytes long and said Click the button below to continue shopping. Check the page itself for the form that posts to /errors_page/validateCaptcha, not only the status code.
What is the Was price on Amazon?
Amazon defines the Typical or Was price as the median price paid on Amazon in the last 90 days. It leaves out prices paid during limited-time deals. So it is a middle value, not the highest price the product ever had.

Proxies that don't die mid-job

Residential, ISP, datacenter and mobile, verified by the same engine that runs tens of millions of checks. They read as a real device and hold up under load. Pay as you go, and your balance never expires. $0.44/GB is the 2,000 GB+ rate; a single gigabyte is $0.50/GB, with no minimum order.

129M+ proxy checks run · 100+ countries · HTTP / HTTPS / SOCKS · re-checked every few minutes · no signup

HProxy.

Honest guides and comparisons on proxies, scraping and staying unblocked, from the team that runs the network.

RSS feed