PageWatch
Extrae precios, listas y tablas de páginas web públicas y recibe alertas de cambios mediante RSS, ICS o webhooks. Plan gratuito más pago por llamada x402.
Servidor MCP alojado
npx add-mcp 'https://pagewatch.erikssonanton2001.workers.dev/mcp'Se instala en Claude Code, Codex, Cursor y más
Documentación
PageWatch: extracción de páginas estáticas (precio, lista, tabla, eventos) + monitoreo de cambios en Cloudflare Workers, con MCP y x402
- JavaScript 100%
Convierte páginas web públicas en datos, y recibe avisos cuando cambian.
PageWatch se ejecuta como un único Cloudflare Worker en el plan gratuito (D1 para almacenamiento, un disparador cron). Lee una página, extrae lo que pidas (un precio, una lista de elementos, una tabla, un calendario de eventos, un hash de contenido), y puede repetirlo según un horario e informar cambios mediante un feed RSS, un registro de eventos JSON, un calendario ICS o un webhook firmado.
Solo HTML estático y JSON. No hay renderizado de JavaScript, ni inicio de sesión, ni cookies. Las páginas que construyen su contenido en el navegador devolverán poco o nada. Se leen JSON-LD, Open Graph y microdatos.
Para qué se usa
- Vigilar una página de producto y recibir un mensaje cuando el precio baja (modo precio, webhook o RSS).
- Convertir la página de eventos de un club en un calendario al que puedas suscribirte (modo eventos, ICS).
- Mantener una lista de ofertas de empleo o lanzamientos y ver qué se añadió o eliminó (modo lista).
- Notar cuando un competidor cambia sus precios o su página de aterrizaje (modo página: título, encabezados, hash de contenido).
- Permitir que un agente o un flujo de n8n/Zapier llame a una URL y reciba JSON.
Inicio rápido
Extracción única (gratis, 30 por día por cliente):
curl -G 'https://pagewatch.erikssonanton2001.workers.dev/v1/extract' \
--data-urlencode 'url=https://shop.example/product/1' -d mode=price
Una lista como CSV:
curl -G 'https://pagewatch.erikssonanton2001.workers.dev/v1/extract' \
--data-urlencode 'url=https://example.org/jobs' -d mode=list \
--data-urlencode 'selector=li.job' \
--data-urlencode 'fields={"title":"h3","link":"a@href"}' -d format=csv
Vigilar una página (gratis: comprueba como máximo cada 60 minutos, 3 monitores activos por cliente, 30 días):
curl -X POST 'https://pagewatch.erikssonanton2001.workers.dev/v1/monitors' \
-H 'content-type: application/json' \
-d '{"url":"https://shop.example/product/1","mode":"price","interval_minutes":360,
"webhook_url":"https://hooks.example/pagewatch"}'
La respuesta contiene el id del monitor, un manage_token y (con un webhook) un webhook_secret. Ambos se muestran una sola vez. Lee los cambios en /m/<id>/feed.rss, /m/<id>/events.json, /m/<id>/latest.json y, para el modo eventos, /m/<id>/calendar.ics. Cualquiera que conozca el id puede leerlos, por lo que el id tiene 128 bits de aleatoriedad.
Gestiona un monitor con el token:
curl -X POST 'https://pagewatch.erikssonanton2001.workers.dev/v1/monitors/<id>/check-now' -H 'Authorization: Bearer <manage_token>'
curl -X POST 'https://pagewatch.erikssonanton2001.workers.dev/v1/monitors/<id>/renew' -H 'Authorization: Bearer <manage_token>'
curl -X DELETE 'https://pagewatch.erikssonanton2001.workers.dev/v1/monitors/<id>' -H 'Authorization: Bearer <manage_token>'
check-now está limitado a una vez cada 10 minutos por monitor.
Modos de extracción
| Modo | Necesita | Devuelve |
|---|---|---|
| text | selector | texto de la primera coincidencia (o todas con all=true) |
| attr | selector, attr | valor del atributo |
| list | selector, fields | array de objetos; los valores de los campos son css o css@attr |
| table | selector opcional, tableIndex | encabezados y filas (CSV compatible) |
| price | selector opcional | precio, moneda, disponibilidad, nombre, fuente (JSON-LD, Open Graph, microdatos, texto) |
| events | ninguno | eventos JSON-LD (JSON, CSV o ICS) |
| page | ninguno | título, descripción, canonical, h1, recuento de palabras, hash de contenido |
| links | match opcional, sameHost | enlaces absolutos con texto |
| json | jsonpath opcional | valor en una expresión JSONPath-lite como $.a.b[0].c |
Los selectores admiten etiqueta, clase, id, operadores de atributo, combinadores descendientes e hijos, listas separadas por comas, :first-child, :nth-child(n) y :not(simple). Los monitores usan por defecto el modo page.
Webhooks
Un POST por cambio con JSON {monitor_id, url, kind, old, new, at, summary} y el encabezado X-PageWatch-Signature: sha256=<hex>, el HMAC-SHA256 del cuerpo sin procesar con la clave del webhook_secret del monitor. Las URL de webhook deben ser https y pasar las mismas comprobaciones SSRF que las URL de página. El tiempo de espera es de 5 segundos, no se siguen redirecciones, y una entrega fallida se reintenta en ejecuciones cron posteriores, tres intentos en total. Después, el monitor se marca como webhook_failed y deja de enviar hasta que se renueva. El feed RSS y events.json siguen siendo la fuente de verdad. Un evento broken (monitoreo detenido tras 10 comprobaciones fallidas) se entrega de la misma manera.
Límites
| Qué | Límite |
|---|---|
| Tamaño de página | 600 KB por página; las páginas más grandes se rechazan (422) |
| Extracciones gratuitas | 30 por día por cliente, luego 429 con instrucciones para el endpoint de pago |
| Monitores gratuitos | 3 activos por cliente, 30 días (renovables), intervalo de 60 minutos o más (por defecto 360) |
| Monitor Pro | 30 días, intervalo desde 15 minutos (por defecto 15), hasta 5 URL de webhook, sin límite por cliente |
| Capacidad global | 400 monitores activos (MAX_ACTIVE) |
| Cortesía | robots.txt respetado (en caché 1 hora); las comprobaciones programadas obtienen un host como máximo una vez cada 15 minutos |
| Solicitudes | http/https, puertos 80/443, solo nombres DNS públicos; sin credenciales, sin literales IP |
| Protección de bucle | este servicio, *.workers.dev y *.pages.dev se rechazan como destinos y webhooks |
| Entrada | selector de hasta 300 caracteres, hasta 20 campos, cuerpos JSON de hasta 8 KB |
| Fallos | 3 fallos seguidos ralentizan un monitor (duplicando, hasta 24 h); 10 lo detienen con un evento broken |
| Cron | cada 15 minutos, hasta 20 monitores por ejecución dentro de 45 solicitudes salientes y 45 sentencias D1 |
Endpoints de pago (x402)
Dos endpoints se pagan por llamada en USDC en Base con x402. Sin cuenta, sin clave API. Una solicitud no pagada devuelve HTTP 402 con un encabezado PAYMENT-REQUIRED. El pago se liquida solo cuando el estado de la respuesta es inferior a 400, por lo que los errores de validación, los bloqueos de robots.txt y los fallos ascendentes son gratuitos.
| Endpoint | Precio |
|---|---|
GET /v1/pro/extract | $0.002, sin límite diario |
POST /v1/pro/monitors | $0.25, 30 días, comprobaciones cada 15 minutos, hasta 5 webhooks |
OpenAPI con x-payment-info: /openapi.json. Listado: /.well-known/x402. Documentación para agentes: /llms.txt.
MCP
POST /mcp habla MCP sobre HTTP transmisible (JSON-RPC 2.0). Herramientas: extract_page, watch_page, get_changes. Las dos primeras siguen las reglas del nivel gratuito; con paid: true devuelven los requisitos de pago x402 del endpoint de pago (o ejecutan la llamada cuando se envía un encabezado PAYMENT-SIGNATURE).
Privacidad
PageWatch almacena la configuración de cada monitor, el último valor extraído y un registro de cambios, hasta que el monitor se elimina o ha estado expirado durante 30 días. Las direcciones IP nunca se almacenan: las cuotas usan un hash SHA-256 con sal que rota diariamente, y el límite de monitores por cliente usa un hash con sal de la IP creadora (establece el secreto SALT; sin él se usa una sal fija de respaldo y los hashes son tan privados como esa sal). El token de gestión se almacena solo como un hash SHA-256. El secreto del webhook se almacena para que las entregas puedan firmarse. No vigiles páginas cuyo contenido no puedas compartir: los feeds son públicos para cualquiera que tenga el id.
Límites honestos
- Sin renderizado de JavaScript, sin páginas con inicio de sesión o muro de pago, sin evasión de protección anti-bots.
- Las comprobaciones son de mejor esfuerzo en un plan gratuito. Una ejecución cron maneja alrededor de una docena de monitores, por lo que con muchos monitores pendientes algunas comprobaciones llegan tarde (se recogen en la siguiente ejecución).
- Los selectores se rompen cuando un sitio cambia. Los monitores que siguen fallando se ralentizan y eventualmente se detienen.
- El plan gratuito permite pocas sentencias D1 por invocación; el trabajo cron cuenta las suyas y se detiene temprano.
- El analizador de robots.txt sigue RFC 9309 para Allow/Disallow, comodines y coincidencia más larga; ignora Crawl-delay.
- Un webhook o destino en un nombre público que resuelve a una dirección privada no puede detectarse desde dentro de un Worker; Cloudflare no enruta las fetches de Workers a rangos privados, que es la protección real allí.
- El límite por cliente se comprueba antes de la inserción, no en una transacción, por lo que una ráfaga de solicitudes paralelas puede superarlo ligeramente.
Desarrollo
npm test # builds, then runs node --test (no network; fakes for fetch, clock, database, facilitator)
npm run build # dist/worker.js, fails if the bundle imports node: modules
npm run make-deploy # writes deploy-fn.js (one async function for the Cloudflare MCP execute tool)
node scripts/migrate-sql.mjs # prints schema.sql
Layout: src/index.js (cableado), src/routes/*, src/service/* (ciclo de vida del monitor, comprobaciones, cron, webhooks, cuotas), src/db/* (adaptador D1 y un gemelo en memoria usado en pruebas), src/extract/* (motor de extracción puro), src/lib/* (protección SSRF, fetch acotado, robots.txt), src/docs.js y src/paid-routes.js (tablas de rutas).
Entorno: PAY_TO, NETWORK, FACILITATOR_URL, PUBLIC_BASE_URL, opcional MAX_ACTIVE y SALT; enlace D1 DB.
Licencia MIT.