← Back to skills

Domain skill

amazon

Markdown synced from browser-harness domain skills.

Agent prompt

Use this skill

Copy this prompt into your coding agent to make it enable browser-harness domain skills and read this exact domain folder before automating.

Set up https://github.com/browser-use/browser-harness for me if it is not already installed. If setup is needed, read `install.md` first to install and connect it to my real browser. Then read `SKILL.md` for normal usage and always read `helpers.py` because that is where the browser-harness functions are.

Enable domain skills if they are not already enabled by setting `BH_DOMAIN_SKILLS=1` for browser-harness. Use the `amazon` domain skill from `agent-workspace/domain-skills/amazon/`. Read every markdown file for this domain before inventing an approach:
- agent-workspace/domain-skills/amazon/order-history.md
- agent-workspace/domain-skills/amazon/product-search.md

Use those domain-skill notes to complete my task for `amazon` in my real browser. When you open a setup, verification, or task tab, activate it so I can see the active browser tab.

Skill contents

What the agent will read

Order History Extraction

order-history.md

Source
  • Field-tested against amazon.co.uk on a 2010-2026 account (1,007 orders), first on 2026-07-16 and re-verified end-to-end on 2026-07-29. Complements product-search.md, which covers the public catalogue; this file covers...
  • Order history requires login. An unauthenticated visit to any orders URL redirects to /ap/signin (title "Amazon Sign-In") — detect this and stop for a human login rather than typing credentials:
  • A one-time headed login persists in the profile; no re-challenge was observed on later headless runs from the same profile/IP.
  • First visit uses newtab(), the same gotcha documented in product-search.md; gotourl() is fine for every page after that.
Show full markdown

Field-tested against amazon.co.uk on a 2010-2026 account (1,007 orders), first on 2026-07-16 and re-verified end-to-end on 2026-07-29. Complements product-search.md, which covers the public catalogue; this file covers the logged-in /your-orders pages.

Auth

Order history requires login. An unauthenticated visit to any orders URL redirects to /ap/signin (title "Amazon Sign-In") — detect this and stop for a human login rather than typing credentials:

python
if "/ap/signin" in page_info()["url"]:
    raise RuntimeError("Amazon session not logged in — do a one-time headed login first")

A one-time headed login persists in the profile; no re-challenge was observed on later headless runs from the same profile/IP.

Navigation

First visit uses new_tab(), the same gotcha documented in product-search.md; goto_url() is fine for every page after that.

python
new_tab("https://www.amazon.co.uk/your-orders/orders?timeFilter=year-2025&startIndex=0")
wait_for_load()
wait(2)  # the order list hydrates after readyState=complete
  • timeFilter takes last30, months-3, then year-YYYY back to the account's first year. Read the valid values off the page rather than guessing:
    python
    years = json.loads(js(
        "JSON.stringify(Array.from(document.querySelectorAll('#time-filter option')).map(o => o.value))"
    ))
    
  • startIndex steps in 10s (0, 10, 20, ...) — 10 orders per page.

Know the expected count before you paginate

The page states its own total, so a scrape can verify itself instead of guessing when to stop. This is the difference between a complete run and a short one that looks complete.

python
import re
count_text = js("document.querySelector('.num-orders')?.innerText")  # '186 orders' / '1 order'
count_match = re.fullmatch(r"\s*(\d{1,3}(?:,\d{3})+|\d+)\s+orders?\s*", count_text or "")
if not count_match:
    raise RuntimeError("Order count is missing or invalid; inspect the page before extracting orders")
expected = int(count_match.group(1).replace(",", ""))

If Amazon shows a separate empty-state message, verify that message before recording zero orders.

Stop on the pagination control, not on a short page — .a-last carries a-disabled on the final page and the whole .a-pagination block is absent when a year fits on one page:

python
is_last_page = bool(js("""
  (function () {
    const l = document.querySelector('.a-pagination .a-last');
    return !l || l.classList.contains('a-disabled');
  })()
"""))

Then assert what you collected against what the page promised, and fail loudly on a mismatch rather than writing a truncated file:

python
if len(collected) != expected:
    raise RuntimeError(f"year-{year}: got {len(collected)} of {expected} orders — investigate before trusting output")

Verified on year-2026: 57 expected, 6 pages, 57 collected, every order dated.

Extraction

Each order is an .order-card. Titles are the product links inside it; the order date is loose text in the card. js() hands back whatever the expression evaluates to, so a JSON.stringify payload needs json.loads on the Python side to become a list:

python
import json

page_orders = json.loads(js("""JSON.stringify(Array.from(document.querySelectorAll('.order-card')).map(c => {
  const m = c.innerText.match(/(\\d{1,2}\\s+[A-Za-z]+\\s+\\d{4}|[A-Za-z]+\\s+\\d{1,2},\\s+\\d{4})/);
  const titles = Array.from(new Set(Array.from(
    c.querySelectorAll('.yohtmlc-product-title, a.a-link-normal[href*="/dp/"], a.a-link-normal[href*="/gp/product/"]')
  ).map(a => a.innerText.trim()).filter(t => t && t.length > 2)));
  return {date: m ? m[1] : '', titles};
}))"""))

The date alternation covers both 31 December 2024 (co.uk) and December 31, 2024 (.com); a UK-only pattern silently yields date: '' for every order on the US site.

Gotchas

  • Wait ~2s after wait_for_load() before querying cards. readyState=complete fires before the list renders, exactly as on search results.
  • Trust .num-orders, not an empty page. A year returning zero cards is a signal to inspect the DOM, never evidence of "no orders" — an earlier run on this account read year-2024+ as empty and was wrong. Re-verified 2026-07-29: year-2010 through year-2026 all extract with .order-card, 10 per page.
  • Don't stop on a short page alone. The final page is legitimately short (7 of 10 on year-2026), so a short-page-means-done rule is indistinguishable from a page that under-rendered. Use .a-last plus the count assertion.
  • Digital items (Kindle, Prime Video) sit alongside physical ones with no reliable in-card type marker — classify by title downstream.
  • Long runs: ~2-3s per page, and grocery-heavy years run 20+ pages. Checkpoint to disk after each year and print(..., flush=True) — a killed run with buffered stdout loses everything.