Confronto prezzi Amazon Europa: differenze tra gli store UE, misurate

Abbiamo confrontato 80 prodotti su amazon.de, .fr, .it, .es e .nl il 19 ottobre 2026: scarto mediano del 19%, nessuno store sempre più economico. Metodo, tabelle e Python.

Lo stesso prodotto Amazon costa in mediana il 18,6% in più nello store UE più caro rispetto a quello più economico. L’abbiamo misurato il 19 ottobre 2026: 35 prodotti nuovi venduti da Amazon stessa, ciascuno con il prezzo su amazon.de, .fr, .it, .es e .nl, IVA inclusa. Il 71% differiva di oltre il 10%, e nessuno store ha vinto in modo costante: Spagna, Francia e Germania si sono alternate in fondo alla classifica, i Paesi Bassi erano di solito i più cari. Lo script Python che ha prodotto i numeri è in fondo.

Quanto differiscono i prezzi Amazon tra gli store UE?

Qui “scarto” significa prezzo più alto diviso prezzo più basso, meno 1, per uno stesso prodotto negli store in cui c’era un prezzo. L’abbiamo calcolato su tre insiemi annidati degli 80 prodotti di test:

Insieme Prodotti Scarto mediano Oltre il 10% Oltre il 25%
Ogni offerta con un prezzo in euro valido, 2+ store 80 18,6% 58 (72%) 29
Nuovi, venduti da Amazon, 3+ store 35 18,6% (metà centrale dal 5,4% al 29,1%) 25 (71%) 10
Nuovi, venduti da Amazon, con prezzo in tutti e 5 gli store 19 19,4% 11 (58%) 6

Solo 5 dei 35 prodotti venduti da Amazon avevano lo stesso prezzo ovunque. Il caso più ampio è stato un set di 52 utensili manuali Bosch: 69,99 EUR in Germania, Francia e Spagna, 96,95 nei Paesi Bassi, 105,98 in Italia (51%).

Uno studio su DEV Community del 18 ottobre 2026 di Atlas DataWorks riporta una mediana del 29,3% su 86 prodotti dei Best Sellers di amazon.de, con 73 su 86 sopra il 10% e la Germania più economica 50 volte. La direzione coincide con la nostra, la dimensione è minore, e la Germania non domina. I loro prodotti vengono dalla classifica dei più venduti tedesca, i nostri da marchi presenti nei risultati di ricerca di almeno quattro store, quindi uno scarto diverso non sorprende. Considera entrambi dei campioni.

Qual è lo store Amazon più economico?

Nessuno, in modo affidabile. Contando solo i prodotti con un unico store più economico (pareggi esclusi), sui 35 venduti da Amazon:

Store Più economico in Prezzo mediano rispetto al minimo del prodotto Prodotti con prezzo
amazon.fr 8 +0,7% 35
amazon.es 7 +0,0% 23
amazon.de 6 +0,9% 34
amazon.it 4 +1,0% 28
amazon.nl 2 +14,6% 35

La Spagna aveva un prezzo solo per 23 dei 35 prodotti (vedi la copertura più sotto). L’andamento che regge è quello dei Paesi Bassi, circa il 15% sopra il prezzo minimo in mediana. Un esempio: le cuffie Sony WH-1000XM5 costano 227,19 EUR in Germania, 269,99 in Francia, 199,00 in Italia e 239,00 nei Paesi Bassi.

Per categoria, dispositivi e console (Kindle, Nintendo, Garmin, Samsung, 11 prodotti) hanno avuto uno scarto mediano dell’1,2%. L’audio (JBL, cuffie Sony, 13 prodotti) il 23,9%, casa e utensili (5) il 23,7%, giocattoli (6) il 17,9%.

L’IVA spiega la differenza?

Difficilmente. Le aliquote IVA ordinarie nel 2026 sono il 19% in Germania, il 20% in Francia, il 21% in Spagna e nei Paesi Bassi, il 22% in Italia (Tax Foundation, gennaio 2026). L’IVA da sola può spiegare al massimo il 2,5% (Germania contro Italia). Dividendo ogni prezzo per l’aliquota del suo paese, lo scarto mediano dei 35 prodotti è sceso solo dal 18,6% al 18,4%. Il resto è la politica di prezzo di Amazon per ciascun marketplace.

Come l’abbiamo misurato?

  1. Seed. 16 query per marchio (Logitech, Anker, Lego, Kindle, Nintendo, Philips, Samsung, Bosch, Tefal, Oral-B, JBL, cuffie Sony, Playmobil, Braun, Garmin, tascabili in inglese) cercate su ciascuno dei cinque store: 80 ricerche, 80 crediti. Abbiamo tenuto gli ASIN comparsi nei risultati di almeno quattro store e ne abbiamo estratti 80 a caso, con un tetto per query (52 visti in tutti e cinque gli store, 28 in quattro).
  2. Prezzi. Una lettura del prodotto per ASIN per store: 400 letture da 1 credito ciascuna, con paese e CAP impostati per ogni store.
  3. Nuovi tentativi. 87 letture non avevano un prezzo al primo passaggio. Un secondo passaggio ne ha recuperate 50 (vedi sotto).
  4. Validazione. Abbiamo tenuto un prezzo solo se era in euro e la stringa mostrata coincideva con extracted_price al centesimo. Su 400 letture, nessuna discordava. Abbiamo anche letto i titoli degli otto scarti più ampi nell’insieme dei venduti da Amazon: stesso modello in ogni lingua.

Prezzo utilizzabile per store, su 80 prodotti dopo il nuovo tentativo: Germania 78, Francia 79, Paesi Bassi 77, Italia 67, Spagna 62. Mancante significa che la chiamata è fallita (422 target_unavailable, non addebitata) oppure che la pagina non aveva un prezzo nella buy box. L’intera esecuzione ha usato circa 510 crediti il 19 ottobre 2026; le letture di prodotto andate a buon fine hanno impiegato in mediana 9,2 secondi, p90 30 secondi.

Cosa va storto quando si confrontano i marketplace?

  • I parametri di localizzazione contano. Per un ASIN, amazon.fr, .it, .es e .co.uk chiamati senza country e postal_code hanno restituito una pagina senza prezzo (località “United States”). Con i parametri, i prezzi sono arrivati. amazon.nl è l’eccezione: ha risposto 400 a qualunque postal_code, quindi manda solo country=nl. La città di consegna può differire dal CAP richiesto (Milano 20121 è tornata come “Figino 20153”), quindi leggi i prezzi come “un indirizzo tipico di quel paese”.
  • Le stringhe di prezzo hanno quattro forme: €69.99 with 36 percent savings, 9,99 €, € 10,99 e 135,24 € con 32 un risparmio percentuale. Prendi il primo numero, decidi il separatore decimale dall’ultimo separatore e rifiuta tutto ciò che non ha il simbolo dell’euro. Una pagina tedesca ha restituito una volta extracted_average_rating 47 per “4,7”, quindi incrocia sempre i numeri letti.
  • amazon.co.uk è in sterline. Funziona (country gb più un CAP ha restituito un prezzo in sterline), ma l’abbiamo escluso per evitare discussioni sui tassi di cambio.
  • “Venduto da” cambia la risposta. Le offerte di magazzino e di terzi (“Amazon Seconde main”, rivenditori su un solo store) hanno prodotto i nostri outlier più grandi, quindi gli insiemi principali tengono solo le offerte nuove vendute da Amazon.
  • I fallimenti sono transitori. 31 letture italiane su 80 e 32 spagnole su 80 sono fallite con 422 al primo passaggio; un nuovo tentativo ne ha recuperate 20 e 17. Riprova, poi tratta il resto come “non offerto”.

Come eseguo questo confronto in Python?

Lo script prende degli ASIN, li legge su cinque store, tiene solo i prezzi in euro validati delle offerte vendute da Amazon, ritenta su 422/429/5xx e si ferma a un tetto di crediti. Usa l’endpoint Amazon (resource=product, 1 credito).

import os, re, statistics, sys, threading, time
from concurrent.futures import ThreadPoolExecutor

import requests

API = "https://api.fastaf.co/v1/amazon"
HEADERS = {"Authorization": f"Bearer {os.environ['FASTAF_API_KEY']}"}
CREDIT_CAP = int(os.environ.get("CREDIT_CAP", 200))
WORKERS = int(os.environ.get("WORKERS", 5))
STORES = {  # amazon.nl rifiuta postal_code, quindi lì si manda solo il paese
    "de": {"domain": "de", "country": "de", "postal_code": "10115"},
    "fr": {"domain": "fr", "country": "fr", "postal_code": "75001"},
    "it": {"domain": "it", "country": "it", "postal_code": "20121"},
    "es": {"domain": "es", "country": "es", "postal_code": "28013"},
    "nl": {"domain": "nl", "country": "nl"},
}
spent, lock = 0, threading.Lock()


def fetch(asin, store):
    global spent
    params = {"resource": "product", "asin": asin, **STORES[store]}
    for attempt in range(4):
        with lock:
            if spent >= CREDIT_CAP:
                return None  # ci si ferma prima del tetto, non dopo
        r = requests.get(API, params=params, headers=HEADERS, timeout=120)
        with lock:
            spent += int(r.headers.get("X-FastAF-Credits-Used", 0))
        if r.status_code == 200:
            return r.json()
        if r.status_code in (422, 429, 500, 502, 503, 504):  # le chiamate fallite sono gratis
            time.sleep(3 * (attempt + 1))
            continue
        return None
    return None


def parse_eur(text):
    """'€69.99 with 36 percent savings', '9,99 €', '€ 1.299,00' -> float."""
    if not text or "€" not in text:
        return None  # rifiuta tutto ciò che non è in euro
    m = re.search(r"\d[\d.,\s  ]*\d|\d", text)
    s = re.sub(r"[\s  ]", "", m.group())
    cut = max(s.rfind(","), s.rfind("."))
    if cut != -1 and len(s) - cut - 1 in (1, 2):  # ultimo separatore = separatore decimale
        return float(re.sub(r"[.,]", "", s[:cut]) + "." + s[cut + 1:])
    return float(re.sub(r"[.,]", "", s))


def price(page):
    if not page or not page.get("price"):
        return None
    shown, extracted = parse_eur(page["price"]), page.get("extracted_price")
    if shown is None or extracted is None or abs(shown - extracted) > 0.011:
        return None  # le due letture non coincidono: meglio scartare che tirare a indovinare
    sold_by = page.get("sold_by") or ""
    return shown if sold_by.lower().startswith("amazon") else None  # solo venduto da Amazon


asins = sys.argv[1:]
jobs = [(a, s) for a in asins for s in STORES]
with ThreadPoolExecutor(WORKERS) as pool:
    prices = dict(zip(jobs, pool.map(lambda j: price(fetch(*j)), jobs)))

spreads, wins = [], dict.fromkeys(STORES, 0)
print("asin        " + "".join(f"{s:>9}" for s in STORES) + "   spread")
for a in asins:
    row = {s: prices[(a, s)] for s in STORES if prices[(a, s)]}
    spread = max(row.values()) / min(row.values()) - 1 if len(row) > 1 else None
    if spread is not None:
        spreads.append(spread)
        low = [s for s, p in row.items() if p == min(row.values())]
        if len(low) == 1:
            wins[low[0]] += 1
    cells = "".join(f"{row[s]:9.2f}" if s in row else f"{'-':>9}" for s in STORES)
    print(f"{a}  {cells}   " + (f"{spread:6.1%}" if spread is not None else "   n/a"))
if spreads:
    print(f"\nmedian spread {statistics.median(spreads):.1%} over {len(spreads)} products")
    print("cheapest store:", wins)
print(f"credits used: {spent} (cap {CREDIT_CAP})")

Eseguito in produzione il 19 ottobre 2026 con python price_compare.py B0DXKNBQS6 B0C3CRCMKJ (uno speaker JBL e il set di utensili Bosch):

asin               de       fr       it       es       nl   spread
B0DXKNBQS6     127.99   140.82   135.24        -   150.00    17.2%
B0C3CRCMKJ      69.99    69.99   105.98    69.99    96.95    51.4%

median spread 34.3% over 2 products
cheapest store: {'de': 1, 'fr': 0, 'it': 0, 'es': 0, 'nl': 0}
credits used: 9 (cap 12)

Il trattino è reale: la lettura su amazon.es dello speaker JBL non ha restituito un prezzo utilizzabile in quell’esecuzione. Due prodotti su cinque store costano 9 crediti, quindi una watchlist di 30 prodotti costa circa 150 crediti a ogni controllo, grosso modo 8 centesimi di dollaro con un pacchetto Starter. Per un job ricorrente con avvisi vedi il bot di monitoraggio prezzi; per i campi del prodotto, come estrarre dati prodotto da Amazon; se arrivi dalla PA-API dismessa, questa guida alla migrazione.

Quali sono i limiti di questi dati?

  • Un solo giorno. Tutto è stato letto il 19 ottobre 2026. Prezzi e offerte cambiano ogni giorno; un’altra settimana darebbe una tabella diversa.
  • Prezzo della buy box, spedizione esclusa. Costo di consegna, coupon e offerte riservate a Prime possono cambiare il prezzo reale. I prezzi “with 36 percent savings” includono l’offerta del momento.
  • Il campione favorisce i marchi molto diffusi. Non dice nulla sui prodotti di nicchia o venduti solo localmente, e un ASIN non è sempre lo stesso prodotto ovunque, per questo abbiamo controllato i titoli.
  • Campione piccolo. 35 prodotti nell’insieme principale; la metà centrale va dal 5% al 29%, quindi leggi la mediana come “intorno al 19%”.

La versione breve

Stesso ASIN, cinque store Amazon UE, un giorno: uno scarto mediano del 18,6% tra il più economico e il più caro, sopra il 10% per il 71% dei prodotti venduti da Amazon, quasi per niente dovuto all’IVA. I Paesi Bassi erano i più cari in mediana; lo store più economico cambiava da prodotto a prodotto. Se vuoi fare monitoraggio prezzi su più marketplace: manda country e postal_code per ogni store (non per amazon.nl), valida ogni prezzo letto, ritenta i 422 e metti un tetto ai crediti. Prendi 500 crediti gratis ed esegui lo script, oppure leggi prima la documentazione.