Glanceshot
Documentación para desarrolladores

API Documentación

URL base: https://glanceshot.com/v1. Todos los endpoints usan HTTP estándar. Puedes enviar los parámetros en la cadena de consulta (GET) o en el cuerpo JSON (POST).

Inicio rápido

  1. Regístrate gratis y crea una API Key en la página «API Key» de la consola.
  2. Incluye la API Key en la cabecera X-Access-Key y llama al endpoint de capturas:
curl "https://glanceshot.com/v1/screenshot?url=https://example.com&full_page=true" \
  -H "X-Access-Key: $GLANCESHOT_KEY" \
  -o screenshot.png

Autenticación

Todas las peticiones requieren una API Key. Puedes enviarla de cualquiera de estas formas:

  • Cabecera X-Access-Key: gls_… (recomendada para llamadas desde el servidor)
  • Parámetro de consulta access_key=gls_… (solo para llamadas desde el servidor; no lo incluyas en páginas web)
  • URL firmada (para incluir la dirección en una página web sin exponer la API Key ni el secreto)

La API Key solo se muestra en texto plano una vez, al crearla. Guárdala en un lugar seguro. Si se filtra, puedes desactivarla en la consola.

Endpoint de capturas

GET/v1/screenshot
POST/v1/screenshot

Parámetros exclusivos de las capturas:

ParámetroTipoValor predeterminadoDescripción
formatenumpngpng, jpeg o webp.
device_scale_factornumber1Factor de escala, 1–3. Con 2 se obtiene nitidez Retina y un tamaño de archivo aproximadamente 4 veces mayor.
qualityinteger80Calidad JPEG / WebP, 1–100.
full_pagebooleanfalseCaptura la página completa. Se desplaza por ella para activar la carga diferida, con un límite de altura de 20,000 píxeles.
selectorstring—Captura solo el primer elemento que coincida con este selector CSS. No se puede combinar con full_page.

Parámetros comunes a capturas y PDF:

ParámetroTipoValor predeterminadoDescripción
urlstring—URL de la página que quieres capturar, con http o https. Debes indicar este parámetro o html, pero no ambos. Se conserva el fragmento # de la URL, por lo que puedes capturar directamente páginas de aplicaciones de página única con rutas hash, como https://app.com/#/report/1.
htmlstring—HTML enviado directamente (máximo 2 MB). Solo admite POST.
viewport_widthinteger1280Ancho del área de visualización, 320–3840. Con valores inferiores a 768, se renderiza como en un dispositivo móvil.
viewport_heightinteger800Alto del área de visualización, 200–3840.
wait_untilenumnetworkidleEstrategia de espera: domcontentloaded (la más rápida, ideal para páginas estáticas), load o networkidle (supervisa todas las peticiones desde que se abre la página; tras la carga, espera hasta que no haya peticiones en curso durante 500 milisegundos, con un máximo de 8 segundos adicionales; ideal para aplicaciones de página única).
wait_for_selectorstring—Tras cargar la página, espera hasta 10 segundos a que aparezca y sea visible un elemento que coincida con este selector CSS antes de capturar. Si no aparece, devuelve selector_not_found (sin coste). Ideal para aplicaciones de página única que renderizan contenido con temporizadores o animaciones, sin depender de peticiones de red, como .chart-ready.
delayinteger0Espera adicional en milisegundos una vez completada la espera inicial, 0–10000. Útil para animaciones o componentes con carga diferida.
block_adsbooleantrueBloquea las solicitudes a dominios de publicidad y rastreo y oculta los espacios publicitarios de la página.
block_cookie_bannersbooleantrueOculta los avisos de consentimiento de cookies.
dark_modebooleanfalseEmula prefers-color-scheme: dark.
hide_selectorsstring—Lista de selectores CSS de los elementos que quieres ocultar, por ejemplo, .ad,#popup.
cachebooleantrueIndica si se consulta la caché. Con false, fuerza un nuevo renderizado, pero el resultado se sigue guardando en caché.
cache_ttlinteger86400Tiempo de validez de la caché en segundos, 60–2592000.
response_typeenumbinarybinary devuelve el archivo directamente; json devuelve la URL del archivo y sus metadatos.
proxystring—Opcional. Usa tu propio proxy para acceder a la página de destino (el coste del proxy corre por tu cuenta), por ejemplo, si necesitas acceder desde un país o una región específicos. Admite socks5:// y http://, con usuario y contraseña opcionales, en el formato socks5://user:pass@203.0.113.10:1080. Solo se puede incluir en el cuerpo de una solicitud POST; no está disponible para solicitudes GET, URL firmadas ni tareas por lotes. Las solicitudes que usan un proxy se almacenan en caché por separado. Si no se puede conectar con el proxy, falla la autenticación o el proxy no puede acceder al sitio de destino, se devuelve proxy_failed y no se cobra la solicitud.
cookiesstring | object[]—Cookies del sitio de destino, en una sola línea como sid=abc; theme=dark o en un array como [{"name":"sid","value":"abc"}] (puede incluir domain y path). Solo se envían al sitio de destino, no a terceros presentes en la página.
headersobject—Cabeceras de solicitud que se añadirán, como Authorization: Bearer …. Solo se envían al sitio de destino; no se pueden definir las que gestiona el navegador (Host, Cookie, User-Agent, etc.).
local_storageobject—Datos que se escribirán en el localStorage del origen de la URL de destino. Tanto las claves como los valores deben ser cadenas de texto.
session_storageobject—Igual que local_storage, pero escribe en sessionStorage.
login_profilestring—Identificador del perfil de sesión guardado con la extensión del navegador (lp_8f2c…). Es más cómodo que enviar cookies directamente y también se puede usar en tareas por lotes (las tareas no almacenan credenciales).

Capturar aplicaciones de página única (React, Vue, etc.):La página ejecuta JavaScript en un navegador real antes de capturarse. La opción predeterminada, networkidle, espera a que termine la descarga de datos. Si el contenido se renderiza después mediante temporizadores o animaciones, añade wait_for_selector para indicar un elemento que confirme que está listo. Si no hay un elemento adecuado, usa delay para esperar un poco más.

Endpoint de PDF

GET/v1/pdf
POST/v1/pdf

Cada PDF consume 2 créditos. Antes de imprimir, se recorre la página para activar el contenido con carga diferida. Además de los parámetros comunes, admite:

ParámetroTipoValor predeterminadoDescripción
paperenuma4Formato de papel: a4 o letter. Si la página incluye una regla @page, se da prioridad a sus ajustes.
print_backgroundbooleantrueImprime los colores y las imágenes de fondo.
pdf_mediaenumprintprint usa los estilos de impresión; screen usa los estilos de pantalla (tal como se ve en el navegador).
De HTML a PDF
curl -X POST "https://glanceshot.com/v1/pdf" \
  -H "X-Access-Key: $GLANCESHOT_KEY" \
  -H "Content-Type: application/json" \
  -d '{"html": "<h1>Informe trimestral</h1><p>Contenido…</p>", "paper": "a4"}' \
  -o report.pdf

Respuestas

Por defecto (response_type=binary), se devuelve el archivo directamente, con Content-Type correspondiente a su formato. Cabeceras de respuesta:

  • X-Request-Id: ID de esta petición; inclúyelo al comunicar un problema
  • X-Glanceshot-Cache: HIT o MISS
  • X-Glanceshot-Truncated: true: la página completa se ha recortado por superar los 20,000 píxeles
  • X-Glanceshot-Format-Changed: webp: el PNG supera los 5 MB y se ha convertido automáticamente a WebP (no se convierte si se especifica format=png de forma explícita)

Con response_type=json, se devuelven la URL del archivo y sus metadatos:

Respuesta JSON
curl -X POST "https://glanceshot.com/v1/screenshot" \
  -H "X-Access-Key: $GLANCESHOT_KEY" \
  -H "Content-Type: application/json" \
  -d '{"url": "https://example.com", "response_type": "json"}'

# Devuelve
{
  "url": "https://glanceshot.com/i/3f/3f9c….png",
  "cache_hit": false,
  "duration_ms": 1284,
  "format": "png",
  "width": 1280,
  "height": 800,
  "request_id": "req_01j…"
}

URL firmadas

¿Quieres usar la dirección de una captura directamente en <img src> sin exponer tu API Key? Las URL firmadas no incluyen la API Key, solo el identificador público key_id. La firma se calcula en tu servidor con el secreto asociado a esa clave. Puedes consultar el ID de la clave y su secreto en la sección API Key de la consola:

Reglas de firma
canonical = Ordena todos los parámetros, salvo signature, por nombre en orden lexicográfico.
            Codifica cada par k=urlencode(v) según RFC 3986 (los espacios como %20) y únelos con &
signature = hex(HMAC-SHA256(secret, canonical))

Una firma válida autentica la solicitud. Si falta o es incorrecta, la solicitud se rechaza. Cualquier cambio en los parámetros (por ejemplo, en url) invalida la firma. El parámetro opcional expires (tiempo Unix en segundos) se incluye en la firma; una vez caducado, se devuelve signature_expired. Para renovar la caché, basta con cambiar un parámetro sin efecto (como v=2) y volver a firmar la URL. Las URL firmadas solo admiten solicitudes GET a los endpoints de capturas y PDF, y no pueden incluir parámetros de proxy.

key_id es público y, por sí solo, no permite llamar a la API. No incluyas la API Key (access_key) en una URL firmada: si la incluyes, se devolverá invalid_params directamente. Si ya has incluido una API Key en una página web, desactívala en la consola y crea una nueva.

import crypto from "node:crypto";

// Codificación RFC 3986 (espacios como %20); debe coincidir con las reglas del servidor
const enc = (v) =>
  encodeURIComponent(v).replace(/[!'()*]/g, (c) => "%" + c.charCodeAt(0).toString(16).toUpperCase());

function signedUrl(params, keyId, secret) {
  const all = { ...params, key_id: keyId };
  const canonical = Object.keys(all).sort().map((k) => `${k}=${enc(String(all[k]))}`).join("&");
  const signature = crypto.createHmac("sha256", secret).update(canonical).digest("hex");
  return `https://glanceshot.com/v1/screenshot?${canonical}&signature=${signature}`;
}

const src = signedUrl({ url: "https://example.com", viewport_width: 1200 }, KEY_ID, SECRET);
// <img src={src} alt="Vista previa de la página web" />

Uso de proxies

Si necesitas acceder a una página desde un país o una región concretos (por ejemplo, para ver el idioma, el contenido y los precios locales), puedes incluir en la solicitud un proxy que hayas contratado por tu cuenta. Los costes del proxy corren a tu cargo. Tanto la página como sus imágenes, scripts y solicitudes a API se cargarán a través de ese proxy. Se admiten proxies socks5:// y http://, con usuario y contraseña opcionales.

curl -X POST "https://glanceshot.com/v1/screenshot" \
  -H "X-Access-Key: $GLANCESHOT_KEY" \
  -H "Content-Type: application/json" \
  -d '{"url": "https://example.com", "proxy": "socks5://user:pass@203.0.113.10:1080"}' \
  -o screenshot.png
  • proxy solo se puede incluir en el cuerpo de una solicitud POST: no se admite en solicitudes GET, URL firmadas ni tareas por lotes, para evitar que las credenciales del proxy aparezcan en la URL, el historial del navegador o una página web.
  • Si el usuario o la contraseña contienen caracteres especiales como @ o :, debes aplicarles codificación URL (por ejemplo, @ se escribe como %40).
  • Las solicitudes con proxy se almacenan en una caché independiente y no comparten resultados con las solicitudes sin proxy o con otro proxy.
  • Si no se puede conectar con el proxy, falla la autenticación o no se puede acceder al sitio de destino, se devuelve proxy_failed y no se cobra la solicitud. message indica el motivo concreto.
  • El proxy debe tener una dirección pública. La URL de destino está sujeta a las mismas restricciones que sin proxy: no se permite acceder a direcciones de redes privadas.
  • Al acceder a sitios web a través de un proxy, respeta sus condiciones de uso y la legislación local.

Capturar páginas con sesión iniciada

Para capturar páginas que requieren iniciar sesión, puedes incluir la sesión de tu propia cuenta. Hay dos opciones: enviar cookies, headers, local_storage y session_storage directamente en la solicitud, o usar la extensión de Glanceshot para guardar la sesión como un «perfil de sesión» e incluir solo login_profile en la solicitud.

curl -X POST "https://glanceshot.com/v1/screenshot"   -H "X-Access-Key: $GLANCESHOT_KEY"   -H "Content-Type: application/json"   -d '{
    "url": "https://app.example.com/dashboard",
    "cookies": "sid=YOUR_SESSION; theme=dark",
    "local_storage": {"token": "YOUR_TOKEN"}
  }' -o screenshot.png
  • La extensión puede leer credenciales de sesión a las que los scripts de la página no tienen acceso (incluidas las cookies HttpOnly), así como localStorage e IndexedDB. Es adecuada para la mayoría de los sitios. Instálala y conéctala desde la página «Perfiles de sesión» de la consola.
  • Los datos de sesión solo se pueden enviar en el cuerpo de una solicitud POST (se rechazan GET y las URL firmadas) y no se guardan en los logs ni en el historial de llamadas. Las solicitudes que incluyen una sesión se almacenan en una caché separada para esa sesión, que no se comparte con otros usuarios.
  • Si incluyes una sesión pero la página redirige al inicio de sesión, se devuelve login_expired y no se cobra la solicitud (la sesión ha caducado; vuelve a sincronizarla con la extensión).
  • En sitios cuya sesión está vinculada a un dispositivo o una IP y no puede reproducirse en la nube (como la banca en línea), usa «Capturar página actual» en la extensión para hacer la captura en tu propio navegador y subirla.

Caché

Las peticiones con los mismos parámetros usan la caché (omitir un parámetro equivale a enviar su valor predeterminado; los parámetros de autenticación y caché no se comparan). Si hay un resultado en caché, el modo binario devuelve una redirección 302 a la URL del archivo junto con Cache-Control; el navegador sigue la redirección y guarda el archivo en caché automáticamente. El modo JSON devuelve cache_hit: true.

Las respuestas desde caché no se facturan.Las peticiones simultáneas con los mismos parámetros solo generan un renderizado.

Límites y facturación

  • Solo se facturan las peticiones correctas (2xx) que no usan la caché: 1 crédito por captura y 2 por PDF.
  • No se descuenta cuota en caso de tiempo de espera agotado, fallos de renderizado, errores o bloqueos de acceso del sitio de destino, parámetros incorrectos o fallos de autenticación.
  • Los créditos se renuevan cada mes natural (UTC). El plan gratuito incluye 100 créditos al mes; al agotarlos, se devuelve quota_exceeded.
  • Si se supera el límite de peticiones por segundo o de concurrencia, se devuelve 429. La cabecera Retry-After indica cuántos segundos se recomienda esperar.
  • Tiempo máximo de renderizado: 30 segundos; descarga máxima por página: 50 MB; tamaño máximo del archivo de salida: 50 MB.

Consulta los límites de cada plan en Precios.

Códigos de error

Todas las respuestas de error usan JSON:

Formato de error
{
  "error": {
    "code": "render_failed",
    "message": "target responded with an error status",
    "upstream_status": 404,
    "request_id": "req_01j…"
  }
}
HTTPcodeSignificadoSe factura
400invalid_paramsFalta un parámetro o su valor está fuera de rango; message indica cuálNo
400html_too_largehtml supera los 2 MBNo
401unauthorizedFalta la API Key, no es válida o está desactivadaNo
401signature_invalidLa firma no coincide (se han modificado los parámetros o el secreto es incorrecto)No
401signature_expiredLa URL firmada ha caducado según expiresNo
403url_blockedNo se permite acceder a la URL de destino (por ejemplo, direcciones de red interna o localhost)No
408render_timeoutEl renderizado ha superado los 30 segundosNo
422render_failedEl sitio de destino devuelve 4xx/5xx, falla el DNS o hay un error de certificado; incluye upstream_statusNo
422selector_not_foundselector no coincide con ningún elemento o se ha agotado el tiempo de espera de wait_for_selectorNo
422too_many_redirectsSe han superado las 5 redireccionesNo
422site_blockedEl sitio de destino ha mostrado una verificación de que no eres un robot, un captcha, una pantalla de inicio de sesión o una página de acceso denegado en lugar del contenido esperado; no se factura.No
422login_expiredSe ha incluido una sesión, pero el sitio de destino sigue mostrando la página de inicio de sesión, lo que indica que la sesión ha caducado.No
422proxy_failedNo se puede conectar con el proxy indicado, ha fallado la autenticación o el proxy no puede acceder al sitio de destino. message indica el motivo concreto. No se cobra la solicitud.No
429rate_limitedSe ha superado el límite de peticiones por segundo; vuelve a intentarlo según Retry-AfterNo
429concurrency_exceededSe ha superado el límite de peticiones simultáneasNo
429quota_exceededSe han agotado los créditos del mes (plan gratuito)No
429too_many_batchesHay más de 5 tareas por lotes sin finalizar a la vezNo
500internalError del servidor; contacta con nosotros e incluye request_idNo

Lotes asíncronos

POST/v1/batch
GET/v1/batch/{id}

Envía entre 1 y 100 URL en una sola petición. El endpoint devuelve de inmediato un ID de tarea (HTTP 202) y las capturas se procesan una a una en segundo plano. Es ideal para capturar páginas por lotes sin mantener la conexión abierta. Puedes consultar el estado periódicamente o indicar webhook_url para recibir una notificación cuando todo haya terminado. La autenticación es la misma que en el endpoint síncrono (solo mediante cabecera o el parámetro access_key; no admite URL firmadas).

ParámetroTipoValor predeterminadoDescripción
typeenumscreenshotscreenshot (captura) o pdf.
urlsstring[]—URL que se van a procesar, entre 1 y 100. Si alguna no es válida, toda la petición devuelve 400 y message indica su posición.
optionsobject{}Parámetros comunes a todas las URL. Son los mismos que los de la API síncrona (como full_page, format, viewport_width y cache_ttl), pero no pueden incluir url, html, response_type ni proxy.
webhook_urlstring—Opcional. URL a la que se enviará una notificación cuando todo haya terminado. Consulta «Notificación al finalizar».
Enviar una tarea por lotes
curl -X POST "https://glanceshot.com/v1/batch" \
  -H "X-Access-Key: $GLANCESHOT_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "type": "screenshot",
    "urls": ["https://example.com", "https://example.org"],
    "options": { "full_page": true, "format": "webp" },
    "webhook_url": "https://your-app.com/hooks/glanceshot"
  }'

# Devuelve 202
{
  "id": "bat_01j…",
  "type": "screenshot",
  "status": "queued",
  "total": 2, "succeeded": 0, "failed": 0,
  "created_at": "2026-10-18T09:30:00Z", "completed_at": null,
  "webhook": { "url": "https://your-app.com/hooks/glanceshot", "status": "none", "attempts": 0 },
  "items": [
    { "index": 0, "url": "https://example.com", "status": "pending", "result": null, "error": null },
    { "index": 1, "url": "https://example.org", "status": "pending", "result": null, "error": null }
  ]
}

Consulta del progreso: el estado de la tarea, status, pasa por queued (en cola), running (en curso) y completed (todo finalizado, con éxito o con errores). El estado de cada URL es pending, running, succeeded o failed. Si se procesa correctamente, result contiene la URL del archivo; si falla, error contiene el código de error (los mismos que en el endpoint síncrono).

Consultar una tarea por lotes
curl "https://glanceshot.com/v1/batch/bat_01j…" -H "X-Access-Key: $GLANCESHOT_KEY"

{
  "id": "bat_01j…",
  "status": "completed",
  "total": 2, "succeeded": 1, "failed": 1,
  "completed_at": "2026-10-18T09:30:12Z",
  "items": [
    {
      "index": 0, "url": "https://example.com", "status": "succeeded", "duration_ms": 1830,
      "result": { "url": "https://glanceshot.com/i/3f/3f9c….webp", "mime": "image/webp", "width": 1280, "height": 2410, "cache_hit": false },
      "error": null
    },
    {
      "index": 1, "url": "https://example.org", "status": "failed", "duration_ms": 640,
      "result": null,
      "error": { "code": "render_failed", "message": "target responded with an error status" }
    }
  ]
}
  • Se aplican las mismas reglas de facturación que en el endpoint síncrono: cada URL procesada correctamente consume 1 crédito por captura o 2 por PDF. Los resultados en caché y los fallos no consumen créditos.
  • Si ya no quedan créditos al enviar la tarea, se devuelve quota_exceeded directamente. Si se agotan durante el procesamiento, las URL restantes fallan con quota_exceeded.
  • Cada cuenta puede tener como máximo 5 tareas por lotes sin finalizar a la vez. Si se supera el límite, se devuelve too_many_batches.
  • La validez de los archivos resultantes depende de options.cache_ttl (por defecto, 1 día). Descárgalos y guárdalos antes de que caduquen.
  • En los resultados de consulta y las notificaciones, webhook.url solo se muestra hasta la ruta, sin la cadena de consulta.

Notificación al finalizar

Si indicas webhook_url, enviaremos una petición POST a esa URL cuando finalice toda la tarea. El cuerpo es JSON: { "event": "batch.completed", "batch": { … } }, donde batch es idéntico a la respuesta del endpoint de consulta. Cabeceras:

  • X-Glanceshot-Signature: t=1760779812,v1=5d3f…: t es la hora de envío (segundos Unix) y v1 es la firma
  • X-Glanceshot-Event: batch.completed, X-Glanceshot-Delivery-Attempt: 1 (número de intento de entrega)
Reglas de firma
v1 = hex(HMAC-SHA256(secret, t + "." + Cuerpo original de la solicitud))
secret es el secreto de firma de la API Key utilizada al enviar la tarea (disponible en la página API Key de la consola).
  • Devuelve una respuesta 2xx en un plazo de 10 segundos; de lo contrario, se considerará un fallo. En caso de fallo, se reintentará al cabo de 10 segundos, 1 minuto y 5 minutos (hasta 4 intentos en total).
  • En casos excepcionales, puedes recibir notificaciones duplicadas de una misma tarea. Usa batch.id para descartar los duplicados.
  • Para verificar la firma, calcúlala a partir de los bytes originales recibidos y comprueba que la diferencia entre t y la hora actual no supere unos minutos para evitar ataques de repetición.
  • La URL de callback debe ser una dirección http(s) accesible públicamente y no puede apuntar a una red interna.
import crypto from "node:crypto";

// rawBody debe contener los bytes originales recibidos, sin aplicar JSON.parse y volver a serializar
function verifyGlanceshotWebhook(rawBody, header, secret, toleranceSec = 300) {
  const parts = Object.fromEntries(header.split(",").map((p) => p.split("=")));
  const t = Number(parts.t);
  if (!t || Math.abs(Date.now() / 1000 - t) > toleranceSec) return false; // Protección contra ataques de repetición
  const expected = crypto.createHmac("sha256", secret).update(`${t}.${rawBody}`).digest("hex");
  const got = Buffer.from(parts.v1 ?? "");
  return got.length === expected.length && crypto.timingSafeEqual(Buffer.from(expected), got);
}

// verifyGlanceshotWebhook(req.rawBody, req.headers["x-glanceshot-signature"], GLANCESHOT_SECRET)

Consulta de uso

GET/v1/usage
shell
curl "https://glanceshot.com/v1/usage" -H "X-Access-Key: $GLANCESHOT_KEY"

{ "month": "202610", "used": 37, "limit": 100, "remaining": 63, "overage": 0, "resets_at": "2026-11-01T00:00:00Z", ... }

Comprobación de estado

GET/v1/health

No requiere autenticación. Devuelve el estado del nodo y del pool de navegadores (total, ocupados y libres).