Comparar preços Amazon na UE: diferença de 19% entre lojas

Medimos 80 produtos na amazon.de, .fr, .it, .es e .nl em 19 de outubro de 2026: diferença mediana de 19%, sem uma loja sempre mais barata. Método, tabelas e Python para monitoramento de preços.

O mesmo produto da Amazon custa, na mediana, 18,6% mais na loja mais cara da UE do que na mais barata. Medimos isso em 19 de outubro de 2026: 35 produtos novos vendidos pela própria Amazon, cada um com o preço consultado na amazon.de, .fr, .it, .es e .nl, com IVA incluído. 71% deles variaram mais de 10%, e nenhuma loja ganhou de forma consistente: Espanha, França e Alemanha se revezaram na ponta mais barata, e a Holanda foi quase sempre a mais cara. O script em Python que gerou os números está no final.

Quanto os preços da Amazon variam entre as lojas da UE?

“Diferença” (spread) aqui significa o preço mais alto dividido pelo mais baixo, menos 1, para um mesmo produto nas lojas que tinham preço. Calculamos em três conjuntos aninhados dos nossos 80 produtos de teste:

Conjunto Produtos Diferença mediana Acima de 10% Acima de 25%
Toda oferta com preço válido em euro, 2+ lojas 80 18,6% 58 (72%) 29
Novo, vendido pela Amazon, 3+ lojas 35 18,6% (metade central de 5,4% a 29,1%) 25 (71%) 10
Novo, vendido pela Amazon, com preço nas 5 lojas 19 19,4% 11 (58%) 6

Só 5 dos 35 produtos vendidos pela Amazon tinham o mesmo preço em todo lugar. O caso mais extremo foi um kit de 52 ferramentas manuais da Bosch: 69,99 EUR na Alemanha, na França e na Espanha, 96,95 na Holanda e 105,98 na Itália (51%).

Um estudo da DEV Community de 18 de outubro de 2026, da Atlas DataWorks, reporta uma mediana de 29,3% em 86 produtos dos Mais Vendidos da amazon.de, com 73 dos 86 acima de 10% e a Alemanha mais barata em 50 casos. A direção bate com a nossa, o tamanho é menor no nosso caso, e a Alemanha não domina. Os produtos deles vêm da lista de mais vendidos alemã, os nossos de marcas encontradas nos resultados de busca de pelo menos quatro lojas, então uma diferença de tamanho não surpreende. Trate os dois como amostras.

Qual loja da Amazon é a mais barata?

Nenhuma, de forma confiável. Contando só os produtos com uma única loja mais barata (empates descartados), nos 35 produtos vendidos pela Amazon:

Loja Mais barata em Preço mediano vs o mínimo do produto Produtos com preço
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

A Espanha teve preço em apenas 23 dos 35 (veja a cobertura abaixo). O padrão que se sustenta é o da Holanda, cerca de 15% acima do preço mais baixo na mediana. Exemplo: o fone Sony WH-1000XM5 custa 227,19 EUR na Alemanha, 269,99 na França, 199,00 na Itália e 239,00 na Holanda.

Por categoria, dispositivos e consoles (Kindle, Nintendo, Garmin, Samsung, 11 produtos) tiveram diferença mediana de 1,2%. Áudio (JBL, fones Sony, 13 produtos) teve 23,9%, casa e ferramentas (5) 23,7%, brinquedos (6) 17,9%.

O IVA explica a diferença?

Quase nada. As alíquotas padrão de IVA em 2026 são 19% na Alemanha, 20% na França, 21% na Espanha e na Holanda e 22% na Itália (Tax Foundation, janeiro de 2026). O IVA sozinho explica no máximo 2,5% (Alemanha contra Itália). Dividir cada preço pela alíquota do seu país moveu a diferença mediana dos 35 produtos apenas de 18,6% para 18,4%. O resto é a política de preços da própria Amazon em cada marketplace.

Como medimos?

  1. Amostra inicial. 16 buscas por marca (Logitech, Anker, Lego, Kindle, Nintendo, Philips, Samsung, Bosch, Tefal, Oral-B, JBL, fones Sony, Playmobil, Braun, Garmin, livros de bolso em inglês) em cada uma das cinco lojas: 80 buscas, 80 créditos. Ficamos com os ASINs presentes nos resultados de pelo menos quatro lojas e sorteamos 80, com um teto por busca (52 vistos nas cinco lojas, 28 em quatro).
  2. Preços. Uma consulta de produto por ASIN por loja: 400 consultas a 1 crédito cada, com país e CEP definidos por loja.
  3. Novas tentativas. 87 consultas vieram sem preço na primeira passada. Uma segunda passada recuperou 50 delas (abaixo).
  4. Validação. Só mantivemos um preço se estivesse em euros e se o texto exibido coincidisse com extracted_price até o centavo. Em 400 consultas, nenhuma divergiu. Também lemos os títulos dos oito maiores spreads do conjunto vendido pela Amazon: o mesmo modelo em todos os idiomas.

Preço utilizável por loja, entre 80 produtos depois da nova tentativa: Alemanha 78, França 79, Holanda 77, Itália 67, Espanha 62. “Faltando” quer dizer que a chamada falhou (422 target_unavailable, não cobrada) ou que a página não tinha preço na buy box. A execução inteira usou cerca de 510 créditos em 19 de outubro de 2026; as consultas de produto bem-sucedidas levaram em mediana 9,2 segundos, p90 de 30 segundos.

O que dá errado ao comparar marketplaces?

  • Os parâmetros de localidade importam. Para um mesmo ASIN, amazon.fr, .it, .es e .co.uk chamadas sem country e postal_code devolveram uma página sem preço (localização “United States”). Com eles, vieram preços. A amazon.nl é a exceção: respondeu 400 a qualquer postal_code, então envie só country=nl. A cidade de entrega pode diferir do CEP que você pede (Milão 20121 voltou como “Figino 20153”), então leia os preços como “um endereço típico daquele país”.
  • As strings de preço têm quatro formatos: €69.99 with 36 percent savings, 9,99 €, € 10,99 e 135,24 € con 32 un risparmio percentuale. Pegue o primeiro número, decida o separador decimal pelo último separador, e recuse qualquer coisa sem o símbolo do euro. Uma página alemã chegou a devolver extracted_average_rating 47 para “4,7”, então confira os números interpretados.
  • A amazon.co.uk é em GBP. Funciona (país gb mais um CEP devolveu um preço em libras), mas a deixamos de fora para evitar discussão sobre câmbio.
  • “Vendido por” muda a resposta. Ofertas de armazém e de terceiros (“Amazon Seconde main”, revendedores em uma loja) produziram nossos maiores outliers, então os conjuntos principais mantêm só ofertas novas vendidas pela Amazon.
  • As falhas são transitórias. 31 de 80 consultas italianas e 32 de 80 espanholas falharam com 422 na primeira passada; uma nova tentativa recuperou 20 e 17. Tente de novo e trate o resto como “não oferecido”.

Como rodar essa comparação em Python?

O script recebe ASINs, consulta cada um em cinco lojas, mantém só preços em euro validados de ofertas vendidas pela Amazon, repete 422/429/5xx e para em um teto de créditos. Ele usa o endpoint da Amazon (resource=product, 1 crédito).

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 rejects postal_code, so there only the country is sent
    "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  # stop before the cap, not after
        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):  # failed calls are free
            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  # refuse anything that is not euros
    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):  # last separator = decimal mark
        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  # the two parses disagree: drop it rather than guess
    sold_by = page.get("sold_by") or ""
    return shown if sold_by.lower().startswith("amazon") else None  # Amazon-sold only


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})")

Rodado em produção em 19 de outubro de 2026 com python price_compare.py B0DXKNBQS6 B0C3CRCMKJ (uma caixa de som JBL e o kit de ferramentas 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)

O traço é real: a consulta da JBL na amazon.es não devolveu nenhum preço utilizável nessa execução. Dois produtos em cinco lojas custaram 9 créditos, então uma watchlist de 30 produtos dá cerca de 150 créditos por verificação, uns 8 centavos de dólar em um pacote Starter. Para uma rotina recorrente com alertas, veja o bot de monitoramento de preços; para os campos do produto, raspagem de dados de produtos da Amazon; se você vem da PA-API desativada, este guia de migração.

Quais são os limites desses dados?

  • Um dia. Tudo foi coletado em 19 de outubro de 2026. Preços e ofertas mudam todo dia; outra semana daria outra tabela.
  • Preço da buy box, frete excluído. Custo de entrega, cupons e ofertas exclusivas para Prime podem mudar o preço real. Preços “with 36 percent savings” incluem a oferta atual.
  • A amostra favorece marcas muito vendidas. Ela não diz nada sobre produtos de nicho ou só locais, e um ASIN nem sempre é o mesmo produto em todo lugar, por isso conferimos os títulos.
  • Amostra pequena. 35 produtos no conjunto principal; a metade central vai de 5% a 29%, então leia a mediana como “em torno de 19%”.

Resumindo

Mesmo ASIN, cinco lojas Amazon da UE, um dia: diferença mediana de 18,6% entre a mais barata e a mais cara, acima de 10% para 71% dos produtos vendidos pela Amazon, quase nada disso por causa do IVA. A Holanda foi a mais cara na mediana; a loja mais barata mudou de produto para produto. Envie country e postal_code por loja (menos para a amazon.nl), valide cada preço interpretado, repita os 422 e coloque um teto nos créditos. Pegue 500 créditos grátis e rode o script, ou leia primeiro a documentação.