---
title: "Cómo hacer seguimiento de precios de la competencia"
description: "Seguir los precios de la competencia exige emparejar productos, leer a intervalos y lanzar alertas. Vemos las API de vendedor, un script Python y la frecuencia."
url: https://proxynet.io/es/blog/competitor-price-tracking
date: 2026-09-19
author: "Enver Kaya"
category: "Casos de uso, Web scraping"
lang: es
---

# Cómo hacer seguimiento de precios de la competencia

Lunes por la mañana, abres el informe de ventas: los pedidos de tus tres productos más vendidos cayeron a la mitad durante el fin de semana. La razón la encuentras por la tarde, cuando miras a mano la página de un competidor. Bajó el precio el viernes por la noche y tú estuviste dos días más caro. Ese es el problema que resuelve el seguimiento de precios de la competencia: enterarte del cambio por el propio cambio y no por las ventas que perdiste.

En este artículo montamos el trabajo de principio a fin. Primero comparamos de dónde pueden venir los datos (la API de vendedor, una herramienta ya hecha, tu propio script); después vemos el emparejado de productos, qué precio conviene guardar y cómo se calcula la frecuencia de lectura. Para quienes programan hay un ejemplo probado en Python que lee el precio de la página, guarda el historial en SQLite y avisa cuando se supera un umbral. Al final están los límites: condiciones del sitio, robots.txt y derecho de la competencia.

> **Nota: Respuesta breve**
>
> El seguimiento de precios de la competencia tiene cuatro piezas: una lista que empareja tus productos con sus equivalentes en el competidor, un rastreador que lee esas páginas a intervalos fijos, una tabla de historial que guarda cada lectura con su fecha y una alerta que avisa cuando el cambio supera el umbral que elegiste. Los datos de tu propio catálogo salen de la API de vendedor del marketplace. Los datos del competidor se leen de páginas de producto públicas, respetando robots.txt y con un ritmo de peticiones bajo.

## ¿Qué pasos tiene el seguimiento de precios de la competencia?

El seguimiento de precios consiste en leer y guardar a intervalos regulares el precio y el stock que aparecen en las páginas de producto de la competencia. La definición completa y la parte de infraestructura están en nuestra página de [seguimiento de precios](/es/price-monitoring); aquí vemos cómo se monta el trabajo. Sea cual sea tu herramienta, el flujo es el mismo:

1. **Elige el alcance.** No sigas todo el catálogo, sino los productos que sostienen la facturación y son sensibles al precio.
2. **Empareja los productos.** Junto a cada una de tus referencias (SKU), anota la dirección del mismo producto en el competidor.
3. **Lee las páginas a intervalos regulares.** Se recogen el precio, la moneda, el estado del stock y, si existe, el nombre del vendedor.
4. **Guarda cada lectura con su fecha.** Una tabla que solo conserva el último precio no puede responder a «¿cuándo cambió?».
5. **Compara el cambio con un umbral y avisa.** Si llega un correo por cada céntimo de variación, a los tres días nadie lee las alertas.
6. **Deja la decisión a una persona.** Una alerta es una sugerencia. Quién cambia el precio, y con qué regla, es una decisión de negocio aparte.

## ¿De dónde salen los datos: API de vendedor, herramienta ya hecha o tu propio script?

Mira primero la vía oficial. Los marketplaces ofrecen a sus vendedores API para gestionar sus propios datos de producto, stock y precio. La [Selling Partner API](https://developer-docs.amazon.com/sp-api/) de Amazon, por ejemplo, cubre publicaciones, precios, pedidos e inventario, y es la misma que se usa para vender en Amazon.es. Estas API existen para tu propia tienda y lo que cuentan sobre la competencia es limitado. La [Product Pricing API](https://developer-docs.amazon.com/sp-api/docs/product-pricing-api) de Amazon devuelve datos de precios competitivos de productos del catálogo de Amazon, como la oferta destacada. No dice nada sobre la tienda propia de un competidor, sobre otro marketplace ni sobre un producto que tú no vendes. Aun así, la columna izquierda de la comparación, es decir, tu precio actual, debería leerse de aquí y no de una hoja de cálculo mantenida a mano.

Para el lado del competidor, las opciones quedan así una junto a otra:

| Método | Qué aporta | Esfuerzo | Cuándo encaja |
|---|---|---|---|
| API de vendedor | Tus propios datos de precio, stock y pedidos; en algunos marketplaces, el precio de la oferta destacada de los productos que vendes | Una integración, una sola vez | Siempre, para tu lado de la comparación |
| Revisión manual y hoja de cálculo | El precio del momento de unos pocos productos | Trabajo humano que se repite cada día | 20-30 productos, un vistazo semanal |
| Herramienta de seguimiento de precios ya hecha | Emparejado, rastreo y pantalla de informes | Suscripción mensual, poca puesta en marcha | No hay equipo de desarrollo y bastan los marketplaces habituales |
| Tu propio script | El sitio, el campo y la frecuencia que quieras | Desarrollo y mantenimiento | Sitios particulares, flujo hacia tu propio almacén de datos, reglas de alerta flexibles |
| Flujo en una herramienta de automatización | El script, pero sin código | Medio | Lista pequeña, conectores listos para hojas de cálculo y avisos |

Un ejemplo de la última fila lo montamos en nuestro artículo sobre [web scraping con n8n](/es/blog/n8n-proxy). Las formas de sacar una tabla de una página sin escribir código las explicamos paso a paso en [cómo extraer datos de un sitio web](/es/blog/extract-data-from-website).

## ¿Cómo se emparejan los productos?

Un producto mal emparejado hace más daño que uno que no se sigue: una alerta que compara el precio del modelo de 64 GB con el de 128 GB te empuja a una rebaja innecesaria. Por orden de fiabilidad, el emparejado funciona así:

- **Código de barras (GTIN/EAN).** Si dos páginas llevan el mismo código, el producto es el mismo. En los datos estructurados está en el campo `gtin13` o `gtin`, y a veces en la tabla de características del producto.
- **Referencia del fabricante (MPN) y marca.** En electrónica y recambios aparece con más frecuencia que el código de barras.
- **Comparación de título y características.** Sin código de barras, se comparan por separado marca, modelo, capacidad, color y cantidad.

En los productos con variantes (talla, color, capacidad) cada variante es una fila aparte y en la mayoría de los sitios tiene su propia dirección o un parámetro propio en la dirección. En los packs (de 3, de 6) no compares sin pasar antes el precio a precio por unidad. En los marketplaces una misma página de producto puede tener varios vendedores; el «competidor» no es la página, sino un vendedor concreto dentro de ella.

A la tabla de emparejado le bastan las columnas `sku`, `competidor`, `url`, `tipo de emparejado` y `última revisión`. Repasa cada cierto tiempo las filas emparejadas a mano: el competidor puede retirar un producto y poner el modelo nuevo en la misma dirección.

## ¿Qué precio deberías guardar?

En una página de producto no hay un solo precio. Si no decides desde el principio qué guardas, tu tabla de historial acabará comparando peras con manzanas.

- **Precio de lista y precio rebajado.** Guárdalos en columnas distintas. El que paga el cliente es el rebajado.
- **Precio que baja en la cesta o con cupón.** Puede no aparecer en la página de producto. Si no figura en la página pública, déjalo fuera del alcance; la navegación automática con una cuenta iniciada no es el tema de este artículo.
- **Envío.** El umbral de envío gratis marca la diferencia real en los productos baratos.
- **Moneda.** Con competidores de otros países, guarda la moneda junto al precio y deja la conversión para la fase de informes.
- **Estado del stock.** El precio de un competidor sin stock no te obliga a nada.

## ¿Cómo se decide la frecuencia de lectura?

La frecuencia no la marca la curiosidad, sino dos preguntas: ¿a qué velocidad cambian los precios en esta categoría y a qué velocidad puedes reaccionar tú? Para un equipo que actualiza precios una vez por semana, una lectura cada hora supone carga para el servidor del competidor y, para ti, datos que nadie mira.

| Clase de producto | Punto de partida razonable | Motivo |
|---|---|---|
| Superventas en periodo de campaña | Varias veces al día | El precio puede cambiar a lo largo del día y el tiempo de reacción es corto |
| Productos principales con venta regular | Una vez al día | La mayoría de las decisiones de precio se toman a diario |
| Cola larga, productos de rotación lenta | Una vez por semana | Cambian poco y apenas piden reacción |
| Productos sin stock en el competidor | Una vez al día, solo el stock | La vuelta del stock es un dato tan valioso como el precio |

Calcula el número de peticiones con una multiplicación sencilla: **número de productos × lecturas diarias**. Leer 500 productos cuatro veces al día son 2.000 peticiones. Si esperas dos segundos entre peticiones, una ronda dura unos 17 minutos, y eso es un ritmo tranquilo para un solo sitio. Cuando haces la misma cuenta con 50.000 productos y lecturas cada hora, entran en juego las colas, el límite de peticiones y el reparto entre varias IP. Las formas de respetar el límite de peticiones están en [web scraping sin bloqueos](/es/blog/web-scraping-without-getting-blocked), y lo que significa que el servidor diga «más despacio», en nuestro artículo sobre [429 Too Many Requests](/es/blog/http-429-too-many-requests).

## ¿En qué parte de la página está el precio?

Pasamos al lado de desarrollo. Leer el precio de donde lo ves en pantalla, es decir, del texto dentro de alguna clase CSS, es el camino más frágil: cambia el diseño, cambia el nombre de la clase y el script se rompe sin avisar. La mayoría de las tiendas ponen esa misma información una segunda vez en la página, de forma estructurada, para los buscadores. Esos datos van dentro de una etiqueta `<script type="application/ld+json">` y llevan los campos `price`, `priceCurrency` y `availability` del tipo [Offer](https://schema.org/Offer) de schema.org. La [documentación de Google sobre datos estructurados para fichas de comerciante](https://developers.google.com/search/docs/appearance/structured-data/merchant-listing?hl=es) marca `price` y `priceCurrency` como obligatorios; una tienda que quiere aparecer en la búsqueda con su precio mantiene esos datos al día.

Por eso el orden de lectura debería ser este:

1. Si la página tiene datos JSON-LD de tipo `Product`, toma el precio de ahí.
2. Si no, escribe un selector según la estructura HTML de la propia página.
3. Si el precio no está en el HTML, la página carga los datos con JavaScript. Ese caso, y otras formas de leer JSON incrustado, los tratamos en [páginas estáticas y dinámicas](/es/blog/static-vs-dynamic-pages); para escribir selectores, consulta [selectores CSS y XPath](/es/blog/css-selector-vs-xpath).

## Ejemplo en Python que funciona: historial de precios y alerta por umbral

El ejemplo no lo ejecutamos contra una tienda real, sino contra [books.toscrape.com](https://toscrape.com/). El sitio es una librería ficticia publicada para practicar scraping y, según el aviso de su página de inicio, los precios se asignaron al azar. Sus páginas no tienen JSON-LD. En tiendas reales te vas a encontrar los dos casos; por eso el script prueba primero con JSON-LD y, si no lo encuentra, recurre a la tabla de información de producto del sitio.

Hace falta Python 3 y dos paquetes (`pip install requests beautifulsoup4`). SQLite viene con el módulo [`sqlite3`](https://docs.python.org/3/library/sqlite3.html) de la biblioteca estándar de Python; no instalas ningún servidor de base de datos aparte.

```python
import json
import re
import sqlite3
import time
from datetime import datetime, timezone
from decimal import Decimal

import requests
from bs4 import BeautifulSoup

DB = "prices.db"
THRESHOLD_PCT = Decimal("5")   # un cambio mayor que este genera una alerta
DELAY = 2.0                    # pausa entre dos peticiones al mismo sitio (segundos)
PROXY = None                   # ejemplo: "http://user:pass@pr.proxynet.io:8000"
USER_AGENT = "ExamplePriceBot/1.0 (+https://example.com/about-the-bot)"

# Tu propio SKU -> página del mismo producto en el competidor
PRODUCTS = {
    "BK-001": "https://books.toscrape.com/catalogue/a-light-in-the-attic_1000/index.html",
    "BK-002": "https://books.toscrape.com/catalogue/tipping-the-velvet_999/index.html",
    "BK-003": "https://books.toscrape.com/catalogue/soumission_998/index.html",
}

CURRENCIES = {"£": "GBP", "€": "EUR", "$": "USD", "₺": "TRY", "TL": "TRY"}

def parse_price(text):
    """Convierte un texto como '£51.77' o '1.299,90' en un Decimal."""
    number = re.sub(r"[^\d.,]", "", text)
    if re.fullmatch(r"\d{1,3}(\.\d{3})+", number):
        number = number.replace(".", "")                     # 1.299 -> 1299 (sin decimales)
    elif number.rfind(",") > number.rfind("."):
        number = number.replace(".", "").replace(",", ".")   # 1.299,90 -> 1299.90
    else:
        number = number.replace(",", "")                     # 1,299.90 -> 1299.90
    return Decimal(number)

def read_jsonld(soup):
    """Lee el precio de los datos Product de schema.org si la página los tiene."""
    for tag in soup.find_all("script", type="application/ld+json"):
        try:
            data = json.loads(tag.string or "")
        except json.JSONDecodeError:
            continue
        candidates = data if isinstance(data, list) else data.get("@graph", [data])
        for candidate in candidates:
            kind = candidate.get("@type")
            if "Product" not in (kind if isinstance(kind, list) else [kind]):
                continue
            offer = candidate.get("offers") or {}
            if isinstance(offer, list):
                offer = offer[0]
            price = offer.get("price") or offer.get("lowPrice")
            if price is None:
                continue
            return {
                "price": Decimal(str(price)),
                "currency": offer.get("priceCurrency"),
                "in_stock": str(offer.get("availability", "")).endswith("InStock"),
                "source": "json-ld",
            }
    return None

def read_html(soup):
    """Sin JSON-LD: la estructura propia de la página, aquí la tabla de producto de books.toscrape.com."""
    table = {
        row.th.get_text(strip=True): row.td.get_text(strip=True)
        for row in soup.select("table.table-striped tr")
    }
    raw = table.get("Price (incl. tax)") or soup.select_one("p.price_color").get_text()
    symbol = next((s for s in CURRENCIES if s in raw), None)
    return {
        "price": parse_price(raw),
        "currency": CURRENCIES.get(symbol),
        "in_stock": table.get("Availability", "").startswith("In stock"),
        "source": "html",
    }

def fetch_product(session, url):
    response = session.get(url, timeout=20)
    response.raise_for_status()
    response.encoding = "utf-8"   # el servidor no envía charset; así el símbolo £ no se estropea
    soup = BeautifulSoup(response.text, "html.parser")
    if soup.select_one("h1") is None:
        raise ValueError("devolvió 200, pero no es una página de producto")
    return read_jsonld(soup) or read_html(soup)

def database():
    db = sqlite3.connect(DB)
    db.execute(
        """CREATE TABLE IF NOT EXISTS prices (
               sku TEXT NOT NULL,
               url TEXT NOT NULL,
               price_cents INTEGER NOT NULL,
               currency TEXT,
               in_stock INTEGER NOT NULL,
               source TEXT,
               checked_at TEXT NOT NULL
           )"""
    )
    db.execute("CREATE INDEX IF NOT EXISTS idx_sku_checked ON prices (sku, checked_at)")
    return db

def previous_row(db, sku):
    return db.execute(
        "SELECT price_cents, in_stock FROM prices WHERE sku = ? ORDER BY checked_at DESC LIMIT 1",
        (sku,),
    ).fetchone()

def alert(message):
    print("ALERTA:", message)   # aquí se conecta el correo o un webhook de chat

def main():
    db = database()
    session = requests.Session()
    session.headers["User-Agent"] = USER_AGENT
    if PROXY:
        session.proxies = {"http": PROXY, "https": PROXY}

    for sku, url in PRODUCTS.items():
        try:
            product = fetch_product(session, url)
        except (requests.RequestException, ValueError, AttributeError) as error:
            print(f"{sku}: no se pudo leer ({error})")
            time.sleep(DELAY)
            continue

        cents = int(product["price"] * 100)
        previous = previous_row(db, sku)
        if previous:
            old_cents, old_stock = previous
            change = Decimal(cents - old_cents) * 100 / Decimal(old_cents)
            if abs(change) >= THRESHOLD_PCT:
                alert(f"{sku}: {old_cents / 100:.2f} -> {cents / 100:.2f} "
                      f"{product['currency']} ({change:+.1f} %)")
            if bool(old_stock) != product["in_stock"]:
                alert(f"{sku}: cambió el stock, ahora {'hay' if product['in_stock'] else 'no hay'}")

        db.execute(
            "INSERT INTO prices VALUES (?, ?, ?, ?, ?, ?, ?)",
            (sku, url, cents, product["currency"], int(product["in_stock"]), product["source"],
             datetime.now(timezone.utc).isoformat(timespec="seconds")),
        )
        db.commit()
        print(f"{sku}: {product['price']} {product['currency']} "
              f"stock={'sí' if product['in_stock'] else 'no'} ({product['source']})")
        time.sleep(DELAY)

    db.close()

if __name__ == "__main__":
    main()
```

En la primera ejecución se escriben tres filas y no sale ninguna alerta, porque no hay un registro anterior con el que comparar:

```text
BK-001: 51.77 GBP stock=sí (html)
BK-002: 53.74 GBP stock=sí (html)
BK-003: 50.10 GBP stock=sí (html)
```

Los precios del sitio de prácticas no cambian, así que para ver la alerta modificamos a mano el último registro de la base de datos: pusimos el precio de `BK-001` en 45,00 y marcamos `BK-002` como sin stock. La salida de la segunda ejecución:

```text
ALERTA: BK-001: 45.00 -> 51.77 GBP (+15.0 %)
BK-001: 51.77 GBP stock=sí (html)
ALERTA: BK-002: cambió el stock, ahora hay
BK-002: 53.74 GBP stock=sí (html)
BK-003: 50.10 GBP stock=sí (html)
```

Hay cuatro decisiones deliberadas en el código:

- **El precio se guarda como un entero en céntimos.** Los números de coma flotante (`float`) acumulan errores de redondeo en las cuentas con dinero. Analizar con `Decimal` y pasar a céntimos mantiene exacto el cálculo del porcentaje.
- **Cada lectura es una fila nueva.** Se escribe una fila aunque el precio no haya cambiado; así se distingue «ese día miramos y era el mismo» de «ese día no pudimos mirar».
- **El producto que no se puede leer se salta y la ronda sigue.** El error se muestra en pantalla y se pasa al siguiente producto. Puedes combinarlo con el código de reintento de [códigos de estado HTTP en web scraping](/es/blog/http-status-codes-web-scraping) para decidir con qué código se reintenta y con cuál se para.
- **El User-Agent dice quién es el bot.** Cómo elegir el valor está en nuestro artículo [qué es el User-Agent](/es/blog/what-is-user-agent).

Con la línea `PROXY` rellena, ejecutamos el mismo script también a través de un proxy local de pruebas con autenticación. El resultado no cambió; con una contraseña incorrecta, cada producto falló con `ProxyError` (407) y la ronda se completó igualmente.

## ¿Cómo se saca la lista de productos: páginas de categoría y paginación?

Para reunir todas las direcciones de producto de una categoría del competidor hay que recorrer las páginas de categoría, y la lista casi siempre está repartida en varias páginas. El método más sólido no es inventarse números de página, sino seguir el enlace «siguiente» de la propia página:

```python
import time
from urllib.parse import urljoin

import requests
from bs4 import BeautifulSoup

def product_urls(session, start, max_pages=3, delay=2.0):
    """Recorre las páginas de categoría siguiendo el enlace 'next' y reúne las URL de producto."""
    url, found = start, []
    for _ in range(max_pages):
        response = session.get(url, timeout=20)
        response.raise_for_status()
        soup = BeautifulSoup(response.text, "html.parser")
        found += [urljoin(url, a["href"]) for a in soup.select("article.product_pod h3 a")]
        next_link = soup.select_one("li.next a")
        if next_link is None:
            break
        url = urljoin(url, next_link["href"])
        time.sleep(delay)
    return found

session = requests.Session()
session.headers["User-Agent"] = "ExamplePriceBot/1.0 (+https://example.com/about-the-bot)"
urls = product_urls(session, "https://books.toscrape.com/catalogue/category/books/mystery_3/index.html")
print(len(urls), "productos encontrados")
```

En la categoría «Mystery» del sitio de prácticas, esta función recorrió dos páginas y devolvió 32 direcciones de producto. El tope de páginas (`max_pages`) evita que un enlace «siguiente» defectuoso meta al script en un bucle sin fin. El scroll infinito, los botones «cargar más», las API basadas en cursor y una cola de URL que retoma un rastreo interrumpido los tratamos en [paginación en web scraping](/es/blog/pagination-web-scraping). Si prefieres montar el mismo trabajo sobre un framework ya hecho, consulta [Scrapy y cómo usarlo con un proxy](/es/blog/scrapy-proxy).

## ¿Cómo se consulta el historial y cómo se programa el script?

El precio más bajo y el más alto de los últimos 30 días son una sola consulta:

```sql
SELECT sku,
       MIN(price_cents) / 100.0 AS lowest,
       MAX(price_cents) / 100.0 AS highest,
       COUNT(*)                 AS readings
FROM prices
WHERE checked_at >= date('now', '-30 days')
GROUP BY sku
ORDER BY sku;
```

Esta tabla muestra cuántos días duró una rebaja y si el precio anunciado como «oferta» era simplemente el precio normal del mes anterior.

Para programarlo, en Linux sirve una línea de `cron` (`0 */6 * * * python3 /opt/prices/price_tracker.py`, cada seis horas) y en Windows el Programador de tareas. Elige horas nocturnas para las rondas; el sitio del competidor atiende a sus clientes de día, igual que el tuyo.

## ¿Por qué importa el país desde el que miras?

El precio y el stock que muestra una tienda pueden cambiar según el país del visitante: moneda, impuestos, zona de entrega, campañas regionales. Si tu script corre en un servidor en la nube situado en otro país, puede que estés guardando la página que se muestra a esa ubicación y no la que ve tu cliente.

La solución es enviar la petición desde el mismo país que tu cliente. Con [Proxies residenciales](https://proxynet.io/es/residential-proxy) puedes elegir el país de salida; la lista de países disponibles está en nuestra página de [ubicaciones de proxy](/es/locations). Cómo se determina la ubicación de una IP, y por qué a veces sale mal, lo explicamos en [qué precisión tiene la geolocalización por IP](/es/blog/ip-geolocation-accuracy). El segundo papel del proxy en este trabajo es repartir la carga entre varias direcciones cuando se rastrea una gran cantidad de productos. La lógica de la rotación está en nuestro artículo sobre [rotación de IP](/es/blog/ip-rotation-explained). La elección entre proxies rotativos y de sesión fija la responde la sección de preguntas de nuestra página de seguimiento de precios.

Un proxy no es un permiso. El límite de peticiones del sitio y las reglas de robots.txt valen igual, salga la petición de la IP que salga.

## ¿Se puede montar un aviso de stock con el mismo flujo?

Sí. El script de arriba ya guarda el estado del stock y avisa cuando cambia. Que el competidor se quede sin stock te dice que esos días no necesitas rebajar; que vuelva a tenerlo es el momento de revisar el precio.

La línea está aquí: recibir un aviso y comprar de forma automática no son lo mismo. Usar un bot para acaparar productos de unidades limitadas en cuanto entran en stock y revenderlos perjudica a los demás compradores, y la mayoría de las tiendas lo prohíben en sus condiciones. El flujo de este artículo recoge información y no toca la cesta. Por qué se bloquea a los agentes que automatizan el paso de compra lo tratamos en [por qué se bloquea a los agentes de compra con IA](/es/blog/ai-shopping-agents-blocked).

## Límites: condiciones del sitio, robots.txt y derecho de la competencia

Mirar el precio en el escaparate del competidor es tan antiguo como el comercio. Lo que mantiene legítimo el seguimiento automático es el método:

- **Solo páginas de producto públicas.** Las zonas que exigen iniciar sesión, los datos personales de las reseñas de clientes y la automatización de cuentas quedan fuera.
- **robots.txt y condiciones del sitio.** Lee las dos cosas antes de rastrear. Las condiciones de uso de los marketplaces pueden incluir cláusulas que limitan el acceso automatizado; si existe una cláusula así, valora la API oficial, un acuerdo de datos o pedir permiso. Cómo se lee el archivo está en [qué es el archivo robots.txt](/es/blog/robots-txt), y el marco legal, en [¿es legal el web scraping?](/es/blog/is-data-web-scraping-legal).
- **Ritmo bajo y caché.** Espera entre peticiones y no pidas la misma página más a menudo de lo que exige el ritmo de tus decisiones.
- **Si aparece una pantalla de protección, para.** Una página de verificación no es una avería, es una respuesta del sitio. Cómo funcionan estos sistemas lo explicamos en [cómo funciona la detección de bots](/es/blog/how-bot-detection-works).

Queda el derecho de la competencia. Observar el precio público de un competidor y fijar el tuyo por tu cuenta es una conducta comercial normal. Acordar precios con competidores, o actuar juntos intercambiando información de precios, es otra cosa. En la UE, el [artículo 101 del Tratado de Funcionamiento de la Unión Europea](https://eur-lex.europa.eu/legal-content/ES/TXT/?uri=CELEX:12008E101) prohíbe los acuerdos y las prácticas concertadas que restringen la competencia, y su primer ejemplo es fijar los precios de compra o de venta. En Türkiye, el artículo 4 de la [Ley n.º 4054 de Protección de la Competencia](https://www.mevzuat.gov.tr/mevzuatmetin/1.5.4054.pdf) traza la misma línea, y la mayoría de las jurisdicciones tienen una norma equivalente. Si vas a montar reglas de fijación automática de precios, habla de este límite con un abogado.

## Casos de uso

- **Vendedores de marketplace:** El precio y el stock de otros vendedores que ofrecen el mismo producto. Las notas de infraestructura por plataforma están en nuestras páginas [Amazon Proxy](/es/amazon-proxy), [Walmart Proxy](/es/walmart-proxy) y [Alibaba Proxy](/es/alibaba-proxy).
- **Marcas que venden desde su propio sitio:** Comprobar si los distribuidores autorizados respetan el precio recomendado. El planteamiento general está en nuestra página de [solución para comercio electrónico](/es/e-commerce-proxy).
- **Equipos de datos:** Usar el historial de precios como entrada para la previsión de demanda y el análisis de campañas. Cómo escalar la recogida está en nuestra página de [extracción de datos](/es/data-scraping).

## Errores frecuentes

- **Guardar solo el último precio.** Sin historial no puedes ver cuándo empezó una rebaja, cuántos días duró ni si se repite.
- **Emparejar una vez y olvidarse.** El competidor renueva el producto, la dirección sigue igual y tú comparas el modelo viejo con el nuevo.
- **Tomar una respuesta `200` por un precio.** Una pantalla de verificación o una página de «producto no encontrado» también pueden devolver `200`. Una comprobación como la del `h1` en el script es imprescindible; si el campo de precio está vacío, no escribas la fila.
- **Confundir la coma y el punto.** `1.299,90` y `1,299.90` son el mismo número. Prueba la función de análisis con ejemplos reales del sitio de destino.
- **Igualar el precio del competidor de forma automática y sin límite.** Si dos reglas automáticas trabajan una contra otra, el precio se hunde. El suelo lo pones tú, según tu coste.

## Guía de decisión

| Necesidad | Recomendación |
|---|---|
| Mantener al día tu propio precio y stock | La API de vendedor del marketplace |
| 20-30 productos, un vistazo semanal | Revisión manual y hoja de cálculo |
| Cientos de productos, sin equipo de desarrollo | Herramienta de seguimiento ya hecha o flujo de automatización |
| Sitios particulares, almacén de datos propio, alertas flexibles | Tu propio script en Python y SQLite |
| Decenas de miles de productos, lecturas cada hora | Rastreador con cola (Scrapy), base de datos en servidor, reparto entre varias IP |
| El precio cambia según el país | Una IP que salga del país de tu cliente |
| El sitio muestra una pantalla de verificación | Para; revisa el ritmo, las condiciones y la opción de la API oficial |

## Preguntas frecuentes

### ¿Es legal el seguimiento de precios de la competencia?

Mirar el precio de una página de producto pública y apuntarlo es una actividad comercial normal. El problema aparece con el método: acceder a datos tras un inicio de sesión, ignorar las condiciones del sitio o robots.txt, rastrear a un ritmo que castiga al servidor. Además, usar la información recogida para pactar precios con competidores va contra el derecho de la competencia.

### ¿Se puede hacer seguimiento de precios con Excel?

Para pocos productos, sí. Una tabla rellenada a mano o la función de Excel para obtener datos de la web bastan para empezar. El límite aparece en el historial y en las alertas: la tabla muestra el estado actual y no avisa de los cambios. Cuando el número de productos pasa de unas decenas, una base de datos pequeña como SQLite da menos trabajo.

### ¿La API de vendedor de Amazon da los precios de la competencia?

En parte. Las API de vendedor existen ante todo para gestionar los datos de producto, stock, precio y pedidos de tu propia tienda. La Product Pricing API de Amazon devuelve datos de precios competitivos, como la oferta destacada, de productos del catálogo de Amazon; no tiene ningún endpoint para el historial de precios de un competidor, su stock o sus precios en otros sitios. Comprueba en la documentación de integración vigente qué ofrece cada marketplace. El trabajo principal de estas API es leer tu precio actual y escribir la decisión de precio de vuelta en tu tienda.

### ¿Cuántas veces al día debería leer los precios?

Tantas como decisiones de precio tomes. Para la mayoría de los catálogos basta una lectura diaria; en los superventas en periodo de campaña se sube a varias rondas al día, y los productos de rotación lenta bajan a una por semana.

### ¿Hace falta un proxy para el seguimiento de precios?

Para una lista pequeña y poca frecuencia, no. Hace falta en dos casos: cuando el precio cambia según el país y tu script corre en un país distinto al de tu cliente, o cuando el número de productos es tan grande que enviar todas las peticiones desde una sola dirección se vuelve un problema. Qué tipo de IP encaja con cada trabajo lo comparamos en nuestra página de [seguimiento de precios](/es/price-monitoring).

### ¿De dónde leo el precio si la página no tiene JSON-LD?

Primero mira en el código fuente de la página si el precio está en el HTML plano; si está, escribe un selector como el del ejemplo y, cuando puedas, apóyate en una estructura más estable, como la tabla de información de producto, en lugar de un nombre de clase. Si el precio no aparece en el código fuente, la página carga los datos después. En ese caso, encontrar en la pestaña de red del navegador la petición de la que vienen los datos es una solución más ligera que lanzar un navegador headless.

## En resumen

El seguimiento de precios de la competencia no es un programa, sino un flujo: una lista de productos bien emparejada, una frecuencia de lectura acorde con el ritmo de tus decisiones, una tabla de historial con fecha y pocas alertas, pero con sentido. Toma los datos de tu lado de la API de vendedor; en el lado del competidor, quédate en las páginas públicas, en robots.txt y en un ritmo bajo. Un script en Python de unas 150 líneas y una base de datos SQLite de un solo archivo hacen este trabajo para la mayoría de los catálogos. Cuando la escala crezca y necesites ver el precio desde el país de tu cliente, encontrarás las opciones de infraestructura en nuestra página de [seguimiento de precios](/es/price-monitoring).
