Comparar precios de Amazon en Europa: 5 tiendas medidas

Medimos 80 productos en amazon.de, .fr, .it, .es y .nl el 19 de octubre de 2026: diferencia mediana del 19 %, sin una tienda siempre más barata. Método y Python.

El mismo producto de Amazon cuesta de mediana un 18,6 % más en la tienda europea más cara que en la más barata. Lo medimos el 19 de octubre de 2026: 35 productos nuevos vendidos por la propia Amazon, cada uno con su precio en amazon.de, .fr, .it, .es y .nl, IVA incluido. El 71 % difería en más de un 10 %, y ninguna tienda ganó de forma constante: España, Francia y Alemania se turnaron en lo más bajo, y Países Bajos fue casi siempre la más cara. El script de Python que produjo las cifras está al final.

¿Cuánto difieren los precios de Amazon entre tiendas de la UE?

Aquí “diferencia” significa precio más alto dividido entre el más bajo, menos 1, para un mismo producto en las tiendas que tenían precio. La calculamos sobre tres conjuntos anidados de nuestros 80 productos de prueba:

Conjunto Productos Diferencia mediana Por encima del 10 % Por encima del 25 %
Toda oferta con precio válido en euros, 2+ tiendas 80 18,6 % 58 (72 %) 29
Nuevos, vendidos por Amazon, 3+ tiendas 35 18,6 % (mitad central de 5,4 % a 29,1 %) 25 (71 %) 10
Nuevos, vendidos por Amazon, con precio en las 5 tiendas 19 19,4 % 11 (58 %) 6

Solo 5 de los 35 productos vendidos por Amazon costaban lo mismo en todas partes. El caso más extremo fue un juego de herramientas manuales Bosch de 52 piezas: 69,99 EUR en Alemania, Francia y España, 96,95 en Países Bajos y 105,98 en Italia (51 %).

Un estudio en DEV Community del 18 de octubre de 2026 de Atlas DataWorks da una mediana del 29,3 % sobre 86 productos de los más vendidos de amazon.de, con 73 de 86 por encima del 10 % y Alemania como la más barata en 50 ocasiones. Nuestra dirección coincide, nuestra magnitud es menor y Alemania no domina. Sus productos salen de la lista de más vendidos alemana; los nuestros, de marcas que aparecen en los resultados de búsqueda de al menos cuatro tiendas, así que una diferencia distinta no sorprende. Trata ambos estudios como muestras.

¿Qué tienda de Amazon es la más barata?

Ninguna, de forma fiable. Contando solo los productos con una única tienda más barata (sin empates), en los 35 productos vendidos por Amazon:

Tienda Más barata en Precio mediano frente al mínimo del producto Productos con precio
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

España tuvo precio en solo 23 de los 35 (ver la cobertura más abajo). El patrón que se mantiene es el de Países Bajos, alrededor de un 15 % por encima del precio más bajo en la mediana. Ejemplo: los auriculares Sony WH-1000XM5 cuestan 227,19 EUR en Alemania, 269,99 en Francia, 199,00 en Italia y 239,00 en Países Bajos.

Por categorías, los dispositivos y consolas (Kindle, Nintendo, Garmin, Samsung, 11 productos) tuvieron una diferencia mediana del 1,2 %. El audio (JBL, auriculares Sony, 13 productos) el 23,9 %, hogar y herramientas (5) el 23,7 %, juguetes (6) el 17,9 %.

¿Explica el IVA la diferencia?

Apenas. Los tipos generales de IVA en 2026 son del 19 % en Alemania, 20 % en Francia, 21 % en España y Países Bajos, y 22 % en Italia (Tax Foundation, enero de 2026). El IVA por sí solo puede explicar como máximo un 2,5 % (Alemania frente a Italia). Al dividir cada precio entre el tipo de su país, la diferencia mediana de los 35 productos pasó solo del 18,6 % al 18,4 %. El resto es la política de precios de Amazon en cada marketplace.

¿Cómo lo medimos?

  1. Semillas. 16 consultas de marca (Logitech, Anker, Lego, Kindle, Nintendo, Philips, Samsung, Bosch, Tefal, Oral-B, JBL, auriculares Sony, Playmobil, Braun, Garmin, libros de bolsillo en inglés) buscadas en cada una de las cinco tiendas: 80 búsquedas, 80 créditos. Nos quedamos con los ASIN que aparecían en los resultados de al menos cuatro tiendas y sorteamos 80, con un tope por consulta (52 vistos en las cinco tiendas, 28 en cuatro).
  2. Precios. Una consulta de producto por ASIN y tienda: 400 consultas a 1 crédito cada una, con país y código postal fijados por tienda.
  3. Reintentos. 87 consultas no devolvieron precio en la primera pasada. Una segunda pasada recuperó 50 (más abajo).
  4. Validación. Solo conservamos un precio si estaba en euros y la cadena mostrada coincidía con extracted_price al céntimo. De las 400 consultas, ninguna discrepó. También leímos los títulos de las ocho mayores diferencias del conjunto vendido por Amazon: el mismo modelo en todos los idiomas.

Precio utilizable por tienda, sobre 80 productos tras el reintento: Alemania 78, Francia 79, Países Bajos 77, Italia 67, España 62. Que falte significa que la llamada falló (422 target_unavailable, sin cargo) o que la página no tenía precio en la buy box. La ejecución completa usó unos 510 créditos el 19 de octubre de 2026; las consultas de producto correctas tardaron una mediana de 9,2 segundos, p90 de 30 segundos.

¿Qué falla al comparar marketplaces?

  • Los parámetros de localización importan. Para un mismo ASIN, amazon.fr, .it, .es y .co.uk llamados sin country ni postal_code devolvieron una página sin precio (ubicación “United States”). Con ellos, obtuvimos precios. amazon.nl es la excepción: respondió 400 a cualquier postal_code, así que envía solo country=nl. La ciudad de entrega puede diferir del código postal que pides (Milán 20121 volvió como “Figino 20153”), así que lee los precios como “una dirección típica de ese país”.
  • Las cadenas de precio tienen cuatro formas: €69.99 with 36 percent savings, 9,99 €, € 10,99 y 135,24 € con 32 un risparmio percentuale. Analiza el primer número, decide el separador decimal por el último separador y rechaza cualquier cosa sin el signo del euro. Una página alemana devolvió una vez extracted_average_rating 47 para “4,7”, así que contrasta los números analizados.
  • amazon.co.uk va en libras. Funciona (country gb más un código postal devolvió un precio en libras), pero lo dejamos fuera para evitar discusiones sobre el tipo de cambio.
  • “Vendido por” cambia la respuesta. Las ofertas de almacén y de terceros (“Amazon Seconde main”, revendedores en una sola tienda) produjeron nuestros mayores valores atípicos, así que los conjuntos principales conservan solo ofertas nuevas vendidas por Amazon.
  • Los fallos son transitorios. 31 de 80 consultas italianas y 32 de 80 españolas fallaron con 422 en la primera pasada; un reintento recuperó 20 y 17. Reintenta y trata el resto como “no ofrecido”.

¿Cómo hago esta comparación en Python?

El script recibe ASIN, consulta cada uno en cinco tiendas, conserva solo precios en euros validados de ofertas vendidas por Amazon, reintenta 422/429/5xx y se detiene en un tope de créditos. Usa el endpoint de 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})")

Ejecutado contra producción el 19 de octubre de 2026 con python price_compare.py B0DXKNBQS6 B0C3CRCMKJ (un altavoz JBL y el juego de herramientas 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)

El guion es real: la consulta de amazon.es del JBL no devolvió ningún precio utilizable en esa ejecución. Dos productos en cinco tiendas cuestan 9 créditos, así que una lista de 30 productos son unos 150 créditos por comprobación, aproximadamente 8 céntimos de dólar con un pack Starter. Para una monitorización de precios recurrente con alertas mira el bot de seguimiento de precios; para los campos del producto, scraping de datos de producto de Amazon; si vienes de la PA-API retirada, esta guía de migración.

¿Qué límites tienen estos datos?

  • Un solo día. Todo se obtuvo el 19 de octubre de 2026. Los precios y las ofertas cambian a diario; otra semana daría otra tabla.
  • Precio de la buy box, sin envío. El coste de entrega, los cupones y las ofertas solo para Prime pueden cambiar el precio real. Los precios “with 36 percent savings” incluyen la oferta vigente.
  • La muestra favorece a las marcas muy vendidas. No dice nada de productos de nicho o solo locales, y un ASIN no siempre es el mismo producto en todas partes, por eso comprobamos los títulos.
  • Muestra pequeña. 35 productos en el conjunto principal; la mitad central va del 5 % al 29 %, así que lee la mediana como “alrededor del 19 %”.

En resumen

Mismo ASIN, cinco tiendas europeas de Amazon, un día: una diferencia mediana del 18,6 % entre la más barata y la más cara, por encima del 10 % para el 71 % de los productos vendidos por Amazon, casi nada por el IVA. Países Bajos fue la más cara en la mediana; la tienda más barata cambió de un producto a otro. Envía country y postal_code por tienda (no para amazon.nl), valida cada precio analizado, reintenta los 422 y limita tus créditos. Consigue 500 créditos gratis y ejecuta el script, o lee primero la documentación.