Christmas Price Tracker: When to Buy Gifts Cheapest
Track Christmas gift prices from November, compare each price with its own season low, and buy before the 2026 shipping cut-offs. Scripts and schedule included.
Published · Updated · By Omar Eldeeb
To buy Christmas gifts at the lowest price, track each gift's price from early November, compare every new price with that gift's own season low and 30-day median, and buy when it's within about 5% of the low or the shipping cut-off is a few days away. In 2026, USPS recommends shipping by 17-19 December for delivery by Christmas.
The rest of this page covers the December-specific part of holiday price tracking: the dates that matter in the US and UK, why a gift watchlist works better than deal pages, a script that turns your snapshots into "buy now" or "wait", and a schedule. For the full season calendar, see the retail sales calendar.
The Christmas timeline for 2026
| Date | What happens | What it means for tracking |
|---|---|---|
| 1 November | Start of the useful baseline | Weekly snapshots from here give you 3-4 pre-Black Friday prices per gift. |
| 20-30 November (typically) | Black Friday week through Cyber Monday (27 and 30 November 2026) | Often a season low. Record it; it's the price later deals have to beat. See the Black Friday price tracker. |
| Mon 14 December | Green Monday (second Monday of December) | A US retail term (from eBay) for one of the last strong online shopping days before shipping cut-offs. |
| Thu 17 - Sat 19 December | USPS recommended send-by dates (contiguous US, 2026): Ground Advantage and First-Class Mail 17 Dec, Priority Mail 18 Dec, Priority Mail Express 19 Dec | After these dates, an online "deal" may not arrive in time. Your buy-or-wait logic should switch to "buy" before them. |
| Sat 19 December | Super Saturday (last Saturday before Christmas) | The last weekend before Christmas; online prices still worth a check. |
| Fri 25 December | Christmas Day | Stop daily checks for gifts. Keep tracking anything you plan to buy in the sales. |
| Sat 26 December | Boxing Day (UK bank holiday moves to Mon 28 Dec) | The comparison point for post-Christmas prices. See the Boxing Day tracker. |
Retailers publish their own Christmas delivery cut-offs, which can differ from the postal service's; use the earliest one that applies to your order. In the UK, Royal Mail publishes its last posting dates on its website each autumn; check the current year's table rather than last year's, because the weekdays change.
Why a watchlist beats deal pages
Deal pages and "top 50 gifts" lists change every day, so you can't compare them over time. A watchlist is a fixed set of product URLs you check on a schedule. It gives you three things a deal page can't:
- A price history per gift, so you know whether today's "20% off" is lower than what it cost two weeks ago.
- Stock-outs. Popular toys and gadgets can sell out in December. If a gift flips to out-of-stock at one store, you see it the same day and can buy elsewhere.
- A comparison across stores. The same LEGO set at Target and Walmart is two URLs in the same list.
Keep the watchlist to the products you'd actually buy, plus one or two alternatives for each gift in case the first choice sells out.
Capture snapshots of the watchlist
Any scraper that returns price, stock and a timestamp per URL works. The example below uses the E-commerce Product Scraper Pro actor through Apify's run-sync API with plain fetch (Node 18+). Monitor mode compares each run with the previous run of the same input and labels each row.
// Node 18+ (built-in fetch). Run: APIFY_TOKEN=... node gift_watch.mjs
const input = {
listingUrls: [
{ url: "https://www.target.com/s?searchTerm=lego" },
{ url: "https://www.walmart.com/search?q=lego" },
],
monitorMode: true, // compare with the previous run of this exact input
onlyChanges: false, // keep every row, so each run is a full snapshot
maxItemsPerStartUrl: 2,
maxProductResults: 4,
};
const url = "https://api.apify.com/v2/acts/constructive_calm~ecommerce-product-scraper-pro/run-sync-get-dataset-items"
+ "?maxTotalChargeUsd=0.10&fields=site,name,price,currency,inStock,changeType,previousPrice";
const res = await fetch(url, {
method: "POST",
headers: { "Content-Type": "application/json", Authorization: `Bearer ${process.env.APIFY_TOKEN}` },
body: JSON.stringify(input),
});
const rows = await res.json();
console.table(rows.map((r) => ({ ...r, name: r.name.slice(0, 40) })));Real output from 2026-10-20. The same input had run two days earlier, so every row is compared with that run:
┌─────────┬──────────────┬────────────────────────────────────────────┬───────┬──────────┬─────────┬─────────────┬───────────────┐
│ (index) │ site │ name │ price │ currency │ inStock │ changeType │ previousPrice │
├─────────┼──────────────┼────────────────────────────────────────────┼───────┼──────────┼─────────┼─────────────┼───────────────┤
│ 0 │ 'target' │ 'LEGO Minifigures Shrek Series Mystery Bo' │ 4.99 │ 'USD' │ true │ 'unchanged' │ 4.99 │
│ 1 │ 'target' │ 'LEGO Marvel Spider-Man vs. Anti-Venom He' │ 4.99 │ 'USD' │ true │ 'unchanged' │ 4.99 │
│ 2 │ 'walmart-us' │ 'LEGO Creator 3 in 1 Cute Hamster with a ' │ 9.97 │ 'USD' │ true │ 'unchanged' │ 9.97 │
│ 3 │ 'walmart-us' │ 'LEGO Creator 3 in 1 Turtle with a Water ' │ 7.99 │ 'USD' │ true │ 'unchanged' │ 7.99 │
└─────────┴──────────────┴────────────────────────────────────────────┴───────┴──────────┴─────────┴─────────────┴───────────────┘Search pages are handy to discover candidates. For the real watchlist, paste the chosen product URLs into detailsUrls instead, because search results reorder from day to day. Two practical notes from the actor's README:
- Monitor state is keyed by the exact set of input URLs. Add or remove one URL and the next run starts a fresh comparison, so settle the list before 1 November, or run late additions as a second list.
changeTypeis one ofnew,price-up,price-down,back-in-stock,out-of-stock,changed,unchangedorremoved. For gifts,out-of-stockis the one to alert on.
Turn snapshots into "buy now" or "wait"
Export each run's rows (date, gift, price) into one CSV. This script compares the latest price with the season low and the 30-day median, and switches to "buy" once the shipping deadline is close. The CSV values below are made-up examples to show the logic, not real prices.
date,gift,price
2026-11-02,lego-set,49.99
2026-11-09,lego-set,49.99
2026-11-16,lego-set,44.99
2026-11-27,lego-set,34.99
2026-12-04,lego-set,39.99
2026-12-09,lego-set,39.99
2026-11-02,headphones,199.00
2026-11-09,headphones,199.00
2026-11-16,headphones,179.00
2026-11-27,headphones,149.00
2026-12-04,headphones,169.00
2026-12-09,headphones,159.00import csv
from collections import defaultdict
from datetime import date
from statistics import median
TODAY = date(2026, 12, 9)
ORDER_BY = date(2026, 12, 17) # your carrier's/retailer's last safe order date
history = defaultdict(list)
for row in csv.DictReader(open("gift_history.csv")):
history[row["gift"]].append((date.fromisoformat(row["date"]), float(row["price"])))
days_left = (ORDER_BY - TODAY).days
for gift, points in history.items():
points.sort()
now = points[-1][1]
low = min(p for _, p in points)
base = median(p for d, p in points if (TODAY - d).days <= 30)
gap = (now - low) / low * 100
if days_left <= 3:
verdict = "buy now: shipping window closing"
elif gap <= 5:
verdict = "buy: within 5% of the season low"
else:
verdict = f"wait: {gap:.0f}% above the season low, {days_left} days left"
print(f"{gift:<11} now {now:>7.2f} low {low:>7.2f} 30-day median {base:>7.2f} -> {verdict}")lego-set now 39.99 low 34.99 30-day median 39.99 -> wait: 14% above the season low, 8 days left
headphones now 159.00 low 149.00 30-day median 169.00 -> wait: 7% above the season low, 8 days leftTune the two thresholds to your own patience: 5% and 3 days are starting points. If a gift goes out-of-stock at your preferred store while it's in stock elsewhere, that overrides "wait".
A Christmas schedule
| Window | Cadence | Cron (UTC) | Mode |
|---|---|---|---|
| 1 Nov - 19 Nov | weekly | 0 7 * * 1 |
full snapshot (onlyChanges: false) |
| 20 Nov - 30 Nov | daily (hourly if you're also hunting Black Friday drops) | 0 7 * * * |
full snapshot |
| 1 Dec - 19 Dec | daily | 0 7 * * * |
onlyChanges: true, alert on price-down and out-of-stock |
| 20 Dec - 24 Dec | daily, local delivery and click-and-collect only | 0 7 * * * |
as above |
| 26 Dec onward | daily for anything you'll buy in the sales | 0 7 * * * |
see the Boxing Day guide |
To get the price-down and out-of-stock rows to your phone, connect the run to email or Slack; the price drop alerts guide shows how.
Which stores this covers
The actor's supported-sites matrix decides where the watchlist works. For Christmas shopping in the US, Amazon, Walmart and Target are supported, and so is any Shopify or WooCommerce gift shop. Best Buy, Costco, Kohl's, Barnes & Noble and eBay are listed as unsupported, so track those by hand or with another tool. In the UK, John Lewis and Marks & Spencer are supported and amazon.co.uk is best-effort; Argos, Smyths Toys, Boots and Next are unsupported.
What it costs
Pricing is pay per event; the README lists every event per tier. On the FREE tier: $0.0048 per product row, $0.0012 per unchanged product with onlyChanges, $0.005 per run start. Target, Walmart and amazon.com pages go through datacenter connections, so there's no residential traffic fee on them.
Worked example, 60 gift URLs on Target and Walmart, FREE tier:
- 4 weekly full snapshots in November: 4 × (60 × $0.0048 + $0.005) = 4 × $0.293 = $1.172
- 19 daily checks in December with
onlyChanges, if nothing changes: 19 × (60 × $0.0012 + $0.005) = 19 × $0.077 = $1.463 - Each product that does change in a December run costs $0.0048 instead of $0.0012.
So about $2.64 for the season before changes, inside Apify's free plan ($5 of usage every month, no credit card required). The actor has no free trial of its own. On GOLD, the same rows are $0.0008 and $0.0002.
If you'd rather not write code, the free e-commerce price tracker query builder builds the input for you and shows a fixed example of the output shape.
Related guides
- Retail sales calendar 2026-2027
- Black Friday price tracker
- Back in stock alerts
- Boxing Day sales tracker (UK)
Disclosure: I built the Apify actor and this site. The buy-or-wait script works on prices from any source.
Sources: USPS: 2026 holiday shipping dates, GOV.UK bank holidays, Wikipedia: Green Monday, the actor's README (supported sites, monitor mode, pricing table), and a live actor run on 2026-10-20.
Quick answers
When is the cheapest time to buy Christmas gifts?
There is no single day for every product. Black Friday week is the obvious candidate, but some items drop again in December and others sell out. Record each gift's price weekly from November, then buy when it is within a few percent of its own season low or when the shipping cut-off is close.
What is the last day to ship Christmas presents in the US in 2026?
USPS recommends sending by Thursday 17 December 2026 for Ground Advantage and First-Class Mail, Friday 18 December for Priority Mail and Saturday 19 December for Priority Mail Express, for delivery in the contiguous US before 25 December. These are recommendations, not guarantees.
Do prices go down right before Christmas?
Some do, especially on items a retailer is overstocked on, but popular toys and electronics can also go out of stock or back to full price. Because it varies by product and store, the reliable answer comes from tracking the specific items you want rather than from a general rule.
Should I wait for Boxing Day or after-Christmas sales?
Only for things you don't need by 25 December. After-Christmas and Boxing Day sales can be deeper on some lines, but they come after the gift deadline. Compare the same item's Black Friday and Boxing Day prices to see which event was better for it.