20 % off all paid plans with code

Python · Amazon

Track Amazon prices with Python

Prices on Amazon change all the time, and nobody wants to open the same product page every morning. In this guide you build a small script that does it for you: it looks up the current price of the products you care about, writes every check into a spreadsheet-friendly file, and tells you when a price falls below the amount you are willing to pay.

You do not have to deal with the Amazon page itself. everydata.io loads the live product page for you and sends back clean data – title, price, currency, availability – so the script stays short and does not break when Amazon changes its layout.

Updated · by everydata.io

What you will have at the end

  • A script that checks a list of Amazon products and appends each price to prices.csv.
  • A message when a price drops or reaches your target.
  • A daily schedule, so it runs without you.

What you need

  • Python 3.10 or newer and the requests package (pip install requests).
  • A free everydata.io API key – create one here.
  • The ASIN of each product: the 10-character code after /dp/ in the Amazon address.

01Keep your API key out of the code

Sign in, copy your key from the dashboard and put it into an environment variable. The script reads it from there, so the key never ends up in a file you might share.

terminal
export EVERYDATA_API_KEY="ds_your_key"

02Look up one product

Start with a single request so you can see what comes back. The product-by-ASIN endpoint takes the ASIN and the marketplace (com for amazon.com, de for amazon.de and so on).

first_request.py
import os

import requests

API = "https://api.everydata.io"
KEY = os.environ["EVERYDATA_API_KEY"]

resp = requests.get(
    f"{API}/amz/amazon-lookup-product-by-asin",
    params={"asin": "B07ZPKN6YR", "domainCode": "com"},
    headers={"x-api-key": KEY},
    timeout=60,
)
resp.raise_for_status()
product = resp.json()

print(product["productTitle"])
print(product["price"], product["currency"], "-", product["warehouseAvailability"])

The answer is JSON. Every response starts with responseStatus and responseMessage, followed by the product. This is a shortened version of the documented example:

Example response · GET /amz/amazon-lookup-product-by-asin
{
  "responseStatus": "PRODUCT_FOUND_RESPONSE",
  "responseMessage": "Product successfully found!",
  "productTitle": "Apple iPhone 11, 64GB, Black - Unlocked (Renewed)",
  "productRating": "4.2 out of 5 stars",
  "countReview": 60024,
  "asin": "B07ZPKN6YR",
  "warehouseAvailability": "This item cannot be shipped to your selected delivery location. Please choose a different delivery location.",
  "retailPrice": 0,
  "price": 0,
  "currency": "USD"
}

Look at price in this example: it is 0. That happens when the page shows no buyable price – here because the item could not be shipped to the delivery location. Your script should treat 0 as “no price right now”, not as a free product.

03Check a list of products and save every result

Now turn the single request into a small tracker. WATCHLIST holds the products and the price you are waiting for. Each run looks up every product once, appends a row to prices.csv and compares the new price with the last one it saved.

track.py
"""Check the price of a few Amazon products and append each check to prices.csv."""
import csv
import os
import re
import time
from datetime import datetime, timezone
from pathlib import Path

import requests

API = "https://api.everydata.io"
KEY = os.environ["EVERYDATA_API_KEY"]
CSV_FILE = Path("prices.csv")
DOMAIN = "com"  # amazon.com – use "de", "co.uk", "fr" … for other marketplaces

# ASIN -> the price at which you want to hear about it
WATCHLIST = {
    "B07ZPKN6YR": 30.00,
}


def api_get(path: str, params: dict) -> dict:
    """GET an everydata.io endpoint. Waits when rate limited (429) and retries a failed page load (5xx) once."""
    retried = False
    for _ in range(4):
        resp = requests.get(f"{API}{path}", params=params, headers={"x-api-key": KEY}, timeout=60)
        if resp.status_code == 429:
            # Too many requests at once. The answer says how long to wait: "... Try again in 12 seconds."
            wait = re.search(r"in (\d+) seconds", resp.text)
            time.sleep(int(wait.group(1)) + 1 if wait else 60)
            continue
        if resp.status_code >= 500 and not retried:
            retried = True  # 5xx answers are not counted against your quota
            time.sleep(10)
            continue
        resp.raise_for_status()  # 4xx (wrong key, quota used up, bad parameter) or a second 5xx
        return resp.json()
    raise RuntimeError("still rate limited after several waits – try again later")


def last_price(asin: str) -> float | None:
    """The most recent price we saved for this ASIN, if any."""
    if not CSV_FILE.exists():
        return None
    with CSV_FILE.open(newline="") as f:
        rows = [r for r in csv.DictReader(f) if r["asin"] == asin and r["price"]]
    return float(rows[-1]["price"]) if rows else None


def main() -> None:
    previous = {asin: last_price(asin) for asin in WATCHLIST}
    now = datetime.now(timezone.utc).isoformat(timespec="seconds")
    new_file = not CSV_FILE.exists()

    with CSV_FILE.open("a", newline="") as f:
        writer = csv.writer(f)
        if new_file:
            writer.writerow(["checked_at", "asin", "title", "price", "retail_price", "currency", "availability"])

        for asin, target in WATCHLIST.items():
            try:
                p = api_get("/amz/amazon-lookup-product-by-asin", {"asin": asin, "domainCode": DOMAIN})
            except (requests.RequestException, RuntimeError) as exc:
                print(f"{asin}: could not check this time ({exc}) – the next run tries again")
                continue  # one failed product does not stop the others
            price = p.get("price") or None  # 0 means the page showed no price
            writer.writerow([
                now, asin, p.get("productTitle"), price or "", p.get("retailPrice") or "",
                p.get("currency") or "", p.get("warehouseAvailability") or "",
            ])

            if price is None:
                print(f"{asin}: no price on the page ({p.get('warehouseAvailability')})")
            elif price <= target:
                print(f"{asin}: {price} {p.get('currency')} – at or below your target of {target}")
            elif previous[asin] is not None and price < previous[asin]:
                print(f"{asin}: dropped from {previous[asin]} to {price} {p.get('currency')}")


if __name__ == "__main__":
    main()

Run it with python3 track.py. The first run creates the file; later runs add one row per product. You can open prices.csv in any spreadsheet program to draw a price chart.

api_get handles the two answers you will meet sooner or later. If a page cannot be loaded, the API answers with a 5xx status such as 502 or 503; those answers are not counted against your quota, so the script waits a few seconds and tries once more. If you send too many requests at once, the answer is 429 and says how many seconds to wait – the script waits that long and continues. A product that still fails is reported and skipped, and the next run picks it up again.

04Decide how you want to be told

The script prints a line in three cases: the page shows no price, the price is at or below your target, or it dropped since the last check. Printing is enough when you read the log. If you would rather get a message, replace the three print calls with whatever you already use – an email via Python's smtplib, a chat webhook or a push service.

05Run it every day

On a Mac or Linux machine that is switched on, cron is the simplest scheduler. This line runs the tracker every morning at 7:00 and keeps a log:

crontab
# Every day at 07:00 – edit with: crontab -e
0 7 * * * cd /home/you/price-tracker && EVERYDATA_API_KEY=ds_your_key /usr/bin/python3 track.py >> track.log 2>&1

On Windows, the Task Scheduler does the same job. Any small server or a scheduled CI job works too.

Every product check is one request. As an example: three products checked once a day are about 90 requests a month, which fits into the free plan of 100 requests. For more products or more checks per day, see pricing.

06Optional: read the price history everydata.io already has

Every product lookup made through everydata.io also stores the price it saw. The price-history endpoint returns those observations for the last days days. It only knows about products someone has looked up before, so for a product nobody has checked yet the list is empty – your own CSV file stays the reliable record.

history.py
resp = requests.get(
    f"{API}/amz/amazon-price-history",
    params={"asin": "B07ZPKN6YR", "domainCode": "com", "days": 90},
    headers={"x-api-key": KEY},
    timeout=60,
)
data = resp.json()
print(data["count"], "observations")
for point in data["history"]:
    print(point["observedAt"], point["price"], point["inStock"])
Example response · GET /amz/amazon-price-history
{
  "responseStatus": "PRODUCT_FOUND_RESPONSE",
  "responseMessage": "Product successfully found!",
  "asin": "B07ZPKN6YR",
  "domainCode": "com",
  "days": 90,
  "currency": "USD",
  "count": 2,
  "history": [
    {
      "observedAt": "2026-09-18T10:12:00Z",
      "price": 39.99,
      "retailPrice": 49.99,
      "inStock": true
    },
    {
      "observedAt": "2026-09-19T09:40:00Z",
      "price": 34.99,
      "retailPrice": 49.99,
      "inStock": true
    }
  ]
}

Endpoints used in this guide

GET/amz/amazon-lookup-product-by-asin

Product Details by ASIN · Amazon API

GET/amz/amazon-price-history

Price History (observations collected by this API) · Amazon API

All parameters in the API reference

Try it with your own data

The free plan includes 100 requests a month, usable on every platform – enough to follow this guide end to end.