Tutorial

How to Detect Amazon Page Layout Changes via API

Monitor Amazon search result structure changes via API. Detect when product positions, features, or ad placements shift.

Amazon frequently changes search result layouts, ad placements, and product card structures. Scrapers break when this happens. This tutorial monitors Amazon SERP structure via the Scavio API, detecting when result formats change, new elements appear, or product positions shift. Each check costs $0.005.

Prerequisites

  • Python 3.8+
  • requests library
  • A Scavio API key from scavio.dev
  • Amazon product categories to monitor

Walkthrough

Step 1: Capture Amazon SERP structure snapshots

Record the structure of Amazon search results including result types and positions.

Python
import os, requests, json, hashlib
from datetime import datetime

API_KEY = os.environ['SCAVIO_API_KEY']
SH = {'x-api-key': API_KEY, 'Content-Type': 'application/json'}

def snapshot_amazon_serp(query):
    data = requests.post('https://api.scavio.dev/api/v1/search',
        headers=SH, json={'query': query, 'platform': 'amazon', 'country_code': 'us'}, timeout=10).json()
    organic = data.get('organic_results', [])
    structure = {
        'query': query,
        'timestamp': datetime.now().isoformat(),
        'total_results': len(organic),
        'result_types': [],
        'has_sponsored': any('sponsored' in json.dumps(r).lower() for r in organic),
        'has_ratings': sum(1 for r in organic if r.get('rating')),
        'has_prices': sum(1 for r in organic if r.get('price') or r.get('extracted_price')),
        'top_domains': list(set(r.get('displayed_link', '').split('/')[0] for r in organic[:5])),
    }
    for r in organic:
        rtype = 'sponsored' if 'sponsored' in json.dumps(r).lower() else 'organic'
        structure['result_types'].append(rtype)
    content_hash = hashlib.md5(json.dumps(structure['result_types']).encode()).hexdigest()
    structure['layout_hash'] = content_hash
    return structure

QUERIES = ['wireless earbuds', 'protein powder', 'usb c hub']
for q in QUERIES:
    snap = snapshot_amazon_serp(q)
    sponsored = snap['result_types'].count('sponsored')
    organic = snap['result_types'].count('organic')
    print(f'  {q:30} | Results: {snap["total_results"]} | Organic: {organic} | Sponsored: {sponsored} | Hash: {snap["layout_hash"][:8]}')
print(f'\nCost: ${len(QUERIES) * 0.005:.3f}')

Step 2: Compare snapshots to detect layout changes

Diff daily snapshots to find structural changes in Amazon results.

Python
HISTORY_FILE = 'amazon_layout_history.json'

def save_and_compare(snapshots):
    try:
        with open(HISTORY_FILE) as f:
            history = json.load(f)
    except FileNotFoundError:
        history = []
    today = {'date': datetime.now().strftime('%Y-%m-%d'), 'snapshots': snapshots}
    changes = []
    if history:
        prev_snaps = {s['query']: s for s in history[-1]['snapshots']}
        for snap in snapshots:
            prev = prev_snaps.get(snap['query'])
            if not prev:
                continue
            if snap['layout_hash'] != prev['layout_hash']:
                changes.append({'query': snap['query'], 'type': 'layout_change',
                    'detail': f'Layout hash changed: {prev["layout_hash"][:8]} -> {snap["layout_hash"][:8]}'})
            if snap['total_results'] != prev['total_results']:
                changes.append({'query': snap['query'], 'type': 'result_count',
                    'detail': f'Results: {prev["total_results"]} -> {snap["total_results"]}'})
            prev_sponsored = prev['result_types'].count('sponsored')
            curr_sponsored = snap['result_types'].count('sponsored')
            if prev_sponsored != curr_sponsored:
                changes.append({'query': snap['query'], 'type': 'ad_change',
                    'detail': f'Sponsored: {prev_sponsored} -> {curr_sponsored}'})
    history.append(today)
    with open(HISTORY_FILE, 'w') as f:
        json.dump(history, f, indent=2)
    return changes

snapshots = [snapshot_amazon_serp(q) for q in QUERIES]
changes = save_and_compare(snapshots)
print(f'\nLayout changes detected: {len(changes)}')
for c in changes:
    print(f'  [{c["type"]:15}] {c["query"]:25} | {c["detail"]}')

Step 3: Generate layout change report

Compile changes into an actionable report for e-commerce teams.

Python
def layout_change_report(changes, snapshots):
    print(f'\n{"=" * 60}')
    print(f'  Amazon Layout Change Report - {datetime.now().strftime("%Y-%m-%d")}')
    print(f'{"=" * 60}')
    print(f'\n  Queries monitored: {len(snapshots)}')
    print(f'  Changes detected: {len(changes)}')
    # Current structure summary
    print(f'\n  Current Layout Summary:')
    for snap in snapshots:
        sponsored = snap['result_types'].count('sponsored')
        organic = snap['result_types'].count('organic')
        print(f'    {snap["query"]:25} | Organic: {organic} | Sponsored: {sponsored} | Prices: {snap["has_prices"]} | Ratings: {snap["has_ratings"]}')
    if changes:
        by_type = {}
        for c in changes:
            by_type.setdefault(c['type'], []).append(c)
        for ctype, items in by_type.items():
            print(f'\n  {ctype.replace("_", " ").title()} ({len(items)}):')
            for item in items:
                print(f'    {item["query"]}: {item["detail"]}')
    else:
        print(f'\n  No layout changes since last scan.')
    print(f'\n  Daily cost: ${len(snapshots) * 0.005:.3f}')
    print(f'  No scrapers needed. No proxy rotation. No HTML parsing.')

layout_change_report(changes, snapshots)

Python Example

Python
import os, requests, json, hashlib
SH = {'x-api-key': os.environ['SCAVIO_API_KEY'], 'Content-Type': 'application/json'}

def amazon_layout(query):
    data = requests.post('https://api.scavio.dev/api/v1/search',
        headers=SH, json={'query': query, 'platform': 'amazon', 'country_code': 'us'}, timeout=10).json()
    results = data.get('organic_results', [])
    h = hashlib.md5(json.dumps([r.get('position') for r in results]).encode()).hexdigest()[:8]
    print(f'{query[:30]:30} | {len(results)} results | hash: {h}')

amazon_layout('wireless earbuds')

JavaScript Example

JavaScript
const SH = { 'x-api-key': process.env.SCAVIO_API_KEY, 'Content-Type': 'application/json' };
const data = await fetch('https://api.scavio.dev/api/v1/search', {
  method: 'POST', headers: SH,
  body: JSON.stringify({ query: 'wireless earbuds', platform: 'amazon', country_code: 'us' })
}).then(r => r.json());
console.log(`Results: ${(data.organic_results || []).length}`);

Expected Output

JSON
  wireless earbuds              | Results: 10 | Organic: 7 | Sponsored: 3 | Hash: 4f2a8b1c
  protein powder                | Results: 10 | Organic: 8 | Sponsored: 2 | Hash: 7d3e9f5a
  usb c hub                     | Results: 10 | Organic: 8 | Sponsored: 2 | Hash: 1b6c4d8e

Cost: $0.015

Layout changes detected: 1
  [ad_change       ] wireless earbuds          | Sponsored: 2 -> 3

============================================================
  Amazon Layout Change Report - 2026-05-21
============================================================

  Queries monitored: 3
  Changes detected: 1
  No scrapers needed. No proxy rotation. No HTML parsing.

Related Tutorials

Frequently Asked Questions

Most developers complete this tutorial in 15 to 30 minutes. You will need a Scavio API key (free tier works) and a working Python or JavaScript environment.

Python 3.8+. requests library. A Scavio API key from scavio.dev. Amazon product categories to monitor. A Scavio API key gives you 250 free credits per month.

Yes. The free tier includes 250 credits per month, which is more than enough to complete this tutorial and prototype a working solution.

Scavio has a native LangChain package (langchain-scavio), an MCP server, and a plain REST API that works with any HTTP client. This tutorial uses the raw REST API, but you can adapt to your framework of choice.

Start Building

Monitor Amazon search result structure changes via API. Detect when product positions, features, or ad placements shift.