API de Capturas de Pantalla de Sitios Web#

El endpoint POST /v1/convert/url-to-screenshot captura una captura de pantalla de página completa de cualquier URL de acceso público como una imagen PNG de alta fidelidad. Gestiona automáticamente banners de cookies, modales, contenido de carga diferida, animaciones activadas por scroll y encabezados fijos (sticky) para producir una captura limpia y precisa. Ejecútalo de forma síncrona para obtener una URL de descarga prefirmada o los bytes PNG en bruto, o usa el modo asíncrono para capturar varias URLs en un lote (batch).


Endpoint#

POST /v1/convert/url-to-screenshot

Content-Type: application/json

Formato de salida: PNG (siempre). El formato de salida no es configurable: todas las capturas se generan como imágenes PNG de página completa.


Autenticación#

Este endpoint admite autenticación tanto con clave privada como con clave pública.

Clave Privada#

Incluye tu clave secreta en el header X-API-Key. Úsala para llamadas servidor a servidor donde la clave nunca se expone al cliente.

X-API-Key: sk_your_private_key

Clave Pública con JWT#

Para uso en el cliente, primero genera un token JWT usando tu clave pública y luego pásalo como token Bearer.

Paso 1 -- Obtén un token:

POST /v1/auth/token
X-API-Key: pk_your_public_key

Paso 2 -- Usa el token:

Authorization: Bearer eyJhbGciOiJIUzI1NiIs...
Nota: Las solicitudes con clave pública están restringidas a una sola URL, modo síncrono y descarga directa. El modo asíncrono, el procesamiento por lotes, los webhooks y los correos de notificación no están disponibles con claves públicas.

Parámetros de la Solicitud#

Parámetros de Nivel Superior#

Parámetro Tipo Obligatorio Predeterminado Descripción Restricción de Plan
url string o string[] -- Una cadena de URL única o un array de URLs para capturar. Varias URLs requieren modo asíncrono. --
async_mode boolean No false Ejecuta la captura de forma asíncrona. Devuelve un batch_id inmediatamente. Obligatorio para lotes (múltiples URLs). Requiere acceso asíncrono
direct_download boolean No false Devuelve los bytes PNG en bruto en el cuerpo de la respuesta en lugar de una respuesta JSON con una URL prefirmada. Se fuerza a true para claves públicas. Incompatible con async_mode y con varias URLs. --
output_format boolean No false Cuando es true con varias URLs, agrupa todos los PNG de salida en un único archivo ZIP. Requiere varias URLs. Requiere acceso a salida ZIP
output_filename string No Generado automáticamente Nombre de archivo personalizado para el archivo de salida. La extensión .png se añade automáticamente. Formato predeterminado: {domain}_{timestamp}.png. --
job_id string No -- ID de job proporcionado por el cliente para recuperación ante timeout. Solo claves públicas. Cuando una conversión síncrona supera los límites de timeout del proxy inverso, el cliente puede sondear GET /v1/convert/status/{job_id} para obtener el resultado. Se ignora en claves privadas. --
notification_email string No Email del propietario del proyecto Dirección de email a notificar cuando finaliza un job asíncrono. Solo claves privadas. --
callback_url string No -- URL de webhook que recibe una solicitud POST cuando finaliza la captura. Solo claves privadas. Requiere acceso a webhooks

Parámetros de Navegador y Renderizado#

Parámetro Tipo Obligatorio Predeterminado Descripción Restricción de Plan
viewport_width integer No 1920 Ancho del viewport del navegador en píxeles. El ancho de la captura coincide con este valor. --
viewport_height integer No 1080 Alto del viewport del navegador en píxeles. Se usa como referencia para el renderizado y el cálculo de unidades de viewport. El alto real de la captura lo determina el alto completo del contenido de la página. --
load_media boolean No true Espera a que todas las imágenes y videos terminen de cargar antes de capturar. Cuando es false, la captura es más rápida pero los medios pueden aparecer como placeholders. --
enable_scroll boolean No true Recorre la página de arriba a abajo con scroll para activar el contenido de carga diferida (loaders basados en IntersectionObserver). --
handle_sticky_header boolean No true Detecta encabezados sticky/fijos y hace scroll hasta arriba antes de capturar para que el encabezado se renderice correctamente en la parte superior de la captura. --
handle_cookies boolean No true Cierra automáticamente los banners de consentimiento de cookies (OneTrust, Cookiebot, Didomi, Usercentrics y banners genéricos). --
wait_for_images boolean No true Espera a que todos los elementos <img> terminen de cargar (timeout de 5 segundos por imagen). --
wait_for_selector string No null Selector CSS que se espera antes de la captura. Devuelve 422 si nunca aparece dentro de wait_for_selector_timeout. Útil para SPAs que hidratan el contenido después de la carga. --
wait_for_selector_timeout integer No 10000 Milisegundos de espera para wait_for_selector (máximo 60000). --
block_ads boolean No false Aborta las solicitudes a dominios conocidos de anuncios/rastreadores para que nunca se carguen, rendericen ni ralenticen la captura. --
block_media boolean No false Aborta por completo las solicitudes de imágenes y audio/vídeo para un renderizado más rápido y ligero. A diferencia de load_media (que solo controla la espera), esto evita por completo que los medios se descarguen. --

Autenticación y Solicitudes Personalizadas#

Parámetro Tipo Obligatorio Predeterminado Descripción Restricción de Plan
auth object No null Credenciales de HTTP Basic Auth para la URL de destino. Formato: {"username": "...", "password": "..."}. No se puede usar junto con un header Authorization personalizado. Requiere acceso a basic auth
cookies array No null Array de objetos de cookie a inyectar antes de la navegación. Máximo 50 cookies. Cada cookie debe tener name, value y domain o url. Requiere acceso a basic auth
headers object No null Diccionario de headers HTTP personalizados enviados en cada solicitud a la URL de destino. Máximo 20 headers. Headers bloqueados: host, content-length, transfer-encoding, connection, upgrade, te, trailer. Requiere acceso a basic auth
No compatible: Los parámetros single_page y pdf_options del endpoint url-to-pdf no son aplicables a las capturas de pantalla. Las capturas siempre capturan la página completa como una única imagen continua.

Cada elemento del array cookies debe seguir esta estructura:

Campo Tipo Obligatorio Predeterminado Descripción
name string -- Nombre de la cookie.
value string -- Valor de la cookie.
domain string Condicional -- Dominio de la cookie. Se debe proporcionar domain o url.
url string Condicional -- URL con la que asociar la cookie. Se debe proporcionar domain o url.
path string No "/" Ruta de la cookie. Por defecto es "/" cuando se establece domain.

Respuesta#

Síncrono con Descarga Directa (direct_download=true)#

Clave privada -- devuelve los bytes PNG en bruto:

HTTP 200 OK
Content-Type: image/png
Content-Disposition: inline; filename="example_20260405_123456789.png"
X-Object-Key: env/files/{project_id}/url-to-screenshot/example_20260405_123456789.png
X-File-Size: 456789
X-Conversion-Time: 8.2
X-Filename: example_20260405_123456789.png

(binary PNG data)

Clave pública -- devuelve JSON con una URL prefirmada:

{
    "presigned_url": "https://spaces.example.com/...",
    "object_key": "env/files/{project_id}/url-to-screenshot/example_20260405_123456789.png",
    "filename": "example_20260405_123456789.png",
    "file_size": 456789,
    "conversion_time_seconds": 8.2,
    "job_id": "client-provided-id"
}

Síncrono sin Descarga Directa (direct_download=false)#

Disponible solo con claves privadas.

{
    "presigned_url": "https://spaces.example.com/...",
    "object_key": "env/files/{project_id}/url-to-screenshot/example_20260405_123456789.png",
    "filename": "example_20260405_123456789.png",
    "file_size": 456789,
    "conversion_time_seconds": 8.2
}

Modo Asíncrono#

Devuelve inmediatamente un batch_id para seguimiento.

HTTP 202 Accepted
{
    "status": "processing",
    "batch_id": "550e8400-e29b-41d4-a716-446655440000",
    "url_count": 5,
    "output_format": "individual"
}

Cuando output_format=true (agrupación en ZIP):

{
    "status": "processing",
    "batch_id": "550e8400-e29b-41d4-a716-446655440000",
    "url_count": 5,
    "output_format": "zip"
}

Sondeo de Estado del Job (Solo Claves Públicas)#

Para recuperación ante timeout con clave pública:

GET /v1/convert/status/{job_id}
Authorization: Bearer <jwt_token>
Estado Respuesta
Procesando {"status": "processing"}
Éxito {"status": "success", "presigned_url": "...", "object_key": "..."}
Fallido {"status": "failed", "error": "..."}

Sondeo de Estado de Lote (Solo Claves Privadas)#

Para jobs de lote asíncronos, sondea con el batch_id de la respuesta 202:

GET /v1/convert/batch/{batch_id}
X-API-Key: sk_your_private_key

Devuelve el estado agregado, los estados por URL y las URLs de descarga prefirmadas. Consulta Sondeo de Estado de Lote para ver el esquema de respuesta completo.

Payload del Webhook de Callback#

Cuando se proporciona un callback_url, EnConvert envía una solicitud POST a esa URL al finalizar.

Job de una sola URL:

{
    "job_id": "activity_id",
    "status": "success",
    "batch_id": "...",
    "gcs_uri": "object_key",
    "filename": "example_20260405_123456789.png",
    "file_size": 456789
}

Job de lote:

{
    "job_id": "activity_id",
    "status": "success",
    "batch_id": "...",
    "total_tasks": 10,
    "successful_tasks": 8,
    "failed_tasks": 2,
    "tasks": [
        {"url": "https://example.com/page1", "status": "success", "filename": "page1.png"},
        {"url": "https://example.com/page2", "status": "failed", "filename": null}
    ]
}

Funcionalidades#

Captura de Página Completa#

Cada captura incluye todo el contenido de la página, no solo el viewport visible. El conversor:

  1. Renderiza la página con los valores especificados de viewport_width y viewport_height
  2. Recorre la página con scroll para activar todo el contenido de carga diferida
  3. Calcula el alto real del contenido usando un recorrido del árbol DOM que mide la posición inferior máxima de todos los elementos visibles
  4. Redimensiona el viewport para abarcar todo el alto del contenido
  5. Captura la pantalla con full_page=true

El resultado es una única imagen PNG alargada de la página completa.

Modo de Captura Limpia#

EnConvert gestiona automáticamente los obstáculos habituales de las páginas web para producir capturas limpias:

  • Banners de consentimiento de cookies -- Cierra automáticamente banners de OneTrust, Cookiebot, Didomi, Usercentrics e implementaciones genéricas. Funciona en la página principal y en iframes.
  • Cierre de modales y popups -- Cierra overlays usando varias estrategias: tecla Escape, botones de cierre ARIA, botones de cierre basados en clase ("Close", "Not now", "No thanks", "Skip") y botones de diálogo basados en role. Elimina los efectos residuales de blur, backdrop e inert tras el cierre.
  • Revelado de animaciones por scroll -- Fuerza la visibilidad de elementos ocultos por librerías de animación activadas por scroll, incluyendo WOW.js, AOS, ScrollReveal, GSAP ScrollTrigger y clases de animación genéricas (.fadeIn, .slideIn, etc.). También revela todos los slides de Swiper.
  • Limpieza de dropdowns -- Cierra todos los dropdowns abiertos, convierte los elementos button de navegación en enlaces anchor reales para que se mantengan visualmente limpios, oculta los elementos role="menu" y reposiciona los encabezados fijos a posición static.

Normalización de Unidades de Viewport#

Las capturas requieren un manejo especial de las unidades de viewport de CSS (vh, svh, lvh, dvh) porque el viewport se redimensiona al alto completo de la página. Sin normalización, los elementos dimensionados con unidades de viewport se estirarían a tamaños enormes. El conversor:

  • Convierte todas las unidades relativas al viewport en valores de píxeles fijos basados en el alto de viewport original
  • Limita las imágenes y videos anormalmente altos a 1.5 veces el alto de viewport original
  • Gestiona particularidades de altura específicas de Elementor (contenedores flex, efectos de movimiento, contenedores de fondo)
  • Preserva las dimensiones de video durante el proceso de normalización

HTTP Basic Auth#

Pasa auth con username y password para capturar páginas protegidas con HTTP Basic Authentication.

{
    "url": "https://staging.example.com/dashboard",
    "auth": {
        "username": "admin",
        "password": "secret"
    }
}

Inyección de Cookies#

Inyecta hasta 50 cookies antes de que cargue la página. Útil para capturar páginas que requieren una sesión activa.

{
    "url": "https://example.com/dashboard",
    "cookies": [
        {"name": "session_id", "value": "abc123", "domain": "example.com"},
        {"name": "locale", "value": "en-US", "domain": "example.com"}
    ]
}

Headers Personalizados#

Envía hasta 20 headers HTTP personalizados en cada solicitud a la página de destino.

{
    "url": "https://example.com/report",
    "headers": {
        "X-Custom-Token": "my-token-value",
        "Accept-Language": "en-US"
    }
}

Carga Diferida de Imágenes#

Cuando load_media y enable_scroll están habilitados (ambos por defecto en true), el conversor recorre la página lentamente con scroll (120px cada 90ms) para activar los loaders diferidos y luego espera a que todas las imágenes terminen de cargar, con un periodo de estabilización de layout de 500ms.

Establece load_media=false para una captura más rápida: el conversor usa scroll rápido (300px cada 30ms) con una estabilización más corta de 100ms, pero los medios pueden aparecer como placeholders.

Manejo de Encabezados Sticky#

Cuando está habilitado (por defecto true), el conversor detecta elementos con posición fixed y sticky que parecen ser encabezados, los reposiciona a posición static para una captura limpia, y hace scroll hasta arriba de la página antes de capturar.

Funcionalidades Adicionales de Renderizado#

  • Emulación de medio screen -- La página se renderiza usando el medio CSS screen (no print), de modo que la captura coincide con lo que los usuarios ven en su navegador.
  • Modo stealth -- Usa enmascaramiento de fingerprint del navegador para evitar la detección de bots en páginas protegidas.
  • Intercepción de popups -- Cierra automáticamente cualquier pestaña nueva del navegador o popup activado por la página.
  • Bypass de CSP -- Gestiona las restricciones de Content Security Policy y Trusted Types que de otro modo bloquearían la manipulación de la página.
  • Preservación de sombras -- Los elementos con box-shadow y text-shadow se etiquetan para garantizar que las sombras se rendericen correctamente en la captura de salida.

Restricciones por Plan de Suscripción#

Funcionalidad Founding Indie Studio Enterprise
Captura básica (una URL, síncrona)
Dimensionamiento de viewport
Modo asíncrono No
Procesamiento por lotes (varias URLs) No
Agrupación de salida en ZIP No No
Callbacks de webhook No No
HTTP Basic Auth No
Inyección de cookies No
Headers personalizados No
Conversiones mensuales 100 Según el plan Según el plan Ilimitadas
Límite de tamaño de lote 0 Según el plan Según el plan Ilimitado
Retención de archivos 1 hora Según el plan Según el plan Según el plan

Modo Asíncrono#

El modo asíncrono es útil para capturas de larga duración o al capturar varias URLs.

Cómo Funciona#

  1. Envía una solicitud con async_mode=true (o pasa varias URLs, lo que habilita el modo asíncrono automáticamente).
  2. La API devuelve HTTP 202 inmediatamente con un batch_id y un url_count.
  3. Cada URL se captura en segundo plano, se sube al almacenamiento y se rastrea individualmente.
  4. Supervisa la finalización mediante sondeo de estado de lote, notificación por email o callback de webhook.

Notificación por Email#

Por defecto, se envía un email de finalización a la dirección de correo del propietario del proyecto. Puedes anularlo con notification_email:

{
    "url": ["https://example.com/page1", "https://example.com/page2"],
    "async_mode": true,
    "notification_email": "[email protected]"
}

Callback de Webhook#

Proporciona un callback_url para recibir una notificación POST automática al finalizar:

{
    "url": ["https://example.com/page1", "https://example.com/page2"],
    "async_mode": true,
    "callback_url": "https://your-server.com/webhook/enconvert"
}

Procesamiento por Lotes y en Volumen#

Captura varias URLs en una sola solicitud. Requiere modo asíncrono y una clave privada.

Salida Individual (predeterminado)#

Cada URL genera un archivo PNG separado:

{
    "url": [
        "https://example.com/page1",
        "https://example.com/page2",
        "https://example.com/page3"
    ],
    "async_mode": true
}

Salida en Paquete ZIP#

Agrupa todas las capturas en un único archivo ZIP:

{
    "url": [
        "https://example.com/page1",
        "https://example.com/page2",
        "https://example.com/page3"
    ],
    "async_mode": true,
    "output_format": true,
    "output_filename": "monthly-screenshots"
}

Ejemplos de Código#

Python (Clave Privada)#

import requests

response = requests.post(
    "https://api.enconvert.com/v1/convert/url-to-screenshot",
    headers={"X-API-Key": "sk_your_private_key"},
    json={
        "url": "https://example.com",
        "viewport_width": 1440,
        "viewport_height": 900
    }
)

data = response.json()
print(data["presigned_url"])

PHP (Clave Privada)#

$ch = curl_init("https://api.enconvert.com/v1/convert/url-to-screenshot");
curl_setopt_array($ch, [
    CURLOPT_POST => true,
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER => [
        "Content-Type: application/json",
        "X-API-Key: sk_your_private_key"
    ],
    CURLOPT_POSTFIELDS => json_encode([
        "url" => "https://example.com",
        "viewport_width" => 1440,
        "viewport_height" => 900
    ])
]);

$response = curl_exec($ch);
curl_close($ch);

$data = json_decode($response, true);
echo $data["presigned_url"];

Node.js (Clave Privada)#

const response = await fetch("https://api.enconvert.com/v1/convert/url-to-screenshot", {
    method: "POST",
    headers: {
        "Content-Type": "application/json",
        "X-API-Key": "sk_your_private_key"
    },
    body: JSON.stringify({
        url: "https://example.com",
        viewport_width: 1440,
        viewport_height: 900
    })
});

const data = await response.json();
console.log(data.presigned_url);

Go (Clave Privada)#

package main

import (
    "bytes"
    "encoding/json"
    "fmt"
    "io"
    "net/http"
)

func main() {
    body, _ := json.Marshal(map[string]interface{}{
        "url":             "https://example.com",
        "viewport_width":  1440,
        "viewport_height": 900,
    })

    req, _ := http.NewRequest("POST", "https://api.enconvert.com/v1/convert/url-to-screenshot", bytes.NewBuffer(body))
    req.Header.Set("Content-Type", "application/json")
    req.Header.Set("X-API-Key", "sk_your_private_key")

    resp, _ := http.DefaultClient.Do(req)
    defer resp.Body.Close()

    respBody, _ := io.ReadAll(resp.Body)
    fmt.Println(string(respBody))
}

JavaScript -- Navegador (Clave Pública)#

// Step 1: Get a JWT token
const tokenRes = await fetch("https://api.enconvert.com/v1/auth/token", {
    method: "POST",
    headers: { "X-API-Key": "pk_your_public_key" }
});
const { token } = await tokenRes.json();

// Step 2: Capture screenshot
const convertRes = await fetch("https://api.enconvert.com/v1/convert/url-to-screenshot", {
    method: "POST",
    headers: {
        "Content-Type": "application/json",
        "Authorization": `Bearer ${token}`
    },
    body: JSON.stringify({
        url: "https://example.com"
    })
});

const data = await convertRes.json();
// Display the screenshot
const img = document.createElement("img");
img.src = data.presigned_url;
document.body.appendChild(img);

React (Clave Pública)#

import { useState } from "react";

function UrlToScreenshot() {
    const [loading, setLoading] = useState(false);
    const [imageUrl, setImageUrl] = useState(null);

    async function captureScreenshot() {
        setLoading(true);
        try {
            // Get JWT token
            const tokenRes = await fetch("https://api.enconvert.com/v1/auth/token", {
                method: "POST",
                headers: { "X-API-Key": "pk_your_public_key" }
            });
            const { token } = await tokenRes.json();

            // Capture screenshot
            const convertRes = await fetch("https://api.enconvert.com/v1/convert/url-to-screenshot", {
                method: "POST",
                headers: {
                    "Content-Type": "application/json",
                    "Authorization": `Bearer ${token}`
                },
                body: JSON.stringify({ url: "https://example.com" })
            });

            const data = await convertRes.json();
            setImageUrl(data.presigned_url);
        } finally {
            setLoading(false);
        }
    }

    return (
        <div>
            <button onClick={captureScreenshot} disabled={loading}>
                {loading ? "Capturing..." : "Take Screenshot"}
            </button>
            {imageUrl && <img src={imageUrl} alt="Screenshot" style={{ maxWidth: "100%" }} />}
        </div>
    );
}

export default UrlToScreenshot;

Respuestas de Error#

Estado Condición
400 Bad Request Parámetro url faltante o vacío
400 Bad Request output_format=true con una sola URL (requiere varias URLs)
400 Bad Request direct_download=true con varias URLs
400 Bad Request direct_download=true con async_mode=true
400 Bad Request Objeto auth inválido (falta username o password)
400 Bad Request cookies inválido (no es un array, supera las 50 entradas, faltan campos obligatorios)
400 Bad Request headers inválido (no es un objeto, supera las 20 entradas, nombres de header bloqueados, valores no string)
400 Bad Request Conflicto entre auth y el header Authorization personalizado
400 Bad Request Clave pública intentando usar varias URLs
401 Unauthorized Clave de API o token JWT faltante o inválido
402 Payment Required Cuota mensual de ops agotada
402 Payment Required El lote superaría la cuota mensual de ops restante
402 Payment Required Límite de almacenamiento alcanzado
403 Forbidden El endpoint no está entre los endpoints permitidos de la clave de API
403 Forbidden Funcionalidad no disponible en el plan actual (async, webhook, ZIP, basic auth)
403 Forbidden El tamaño del lote supera el límite de lote del plan
404 Not Found ID de job no encontrado (al sondear el estado)
500 Internal Server Error Fallo en la captura (crash del navegador, error de renderizado)

Límites#

Límite Valor
Timeout de navegación de página 60 segundos
Timeout de carga por imagen 5 segundos
Timeout de cierre de banner de cookies 3 segundos
Máximo de cookies por solicitud 50
Máximo de headers personalizados por solicitud 20
Operaciones mensuales Según el plan (Founding: 500)
Tamaño de lote Según el plan (Founding: deshabilitado)
Retención de archivos Según el plan (Founding: 1 hora)
Timeout de entrega de webhook 30 segundos

Preguntas frecuentes#

¿Cómo tomo una captura de pantalla de página completa de un sitio web con una API?#

Envía una solicitud POST a /v1/convert/url-to-screenshot con un cuerpo JSON que contenga url, autenticándote con tu clave privada en el header X-API-Key (o un token Bearer JWT de una clave pública). Cada captura incluye todo el contenido de la página, no solo el viewport visible: el conversor redimensiona el viewport al alto completo del contenido y captura con full_page=true.

¿Puedo cambiar el formato de salida de la captura a JPEG o WebP?#

No. El formato de salida no es configurable: todas las capturas se generan como imágenes PNG de página completa.

¿Cómo controlo el ancho y el tamaño de la captura?#

Establece viewport_width (por defecto 1920): el ancho de la captura coincide con este valor. El alto de la captura lo determina el alto completo del contenido de la página, usando viewport_height (por defecto 1080) como referencia para el renderizado y el cálculo de unidades de viewport.

¿Puedo capturar una página protegida por login?#

Sí. Usa el parámetro auth para HTTP Basic Auth, inyecta hasta 50 cookies de sesión con cookies, o envía hasta 20 headers HTTP personalizados con headers. Estas opciones requieren acceso a basic auth en tu plan.

¿Cómo elimina la API los banners de cookies y popups de las capturas?#

Con handle_cookies habilitado (por defecto true), el conversor cierra automáticamente los banners de consentimiento de OneTrust, Cookiebot, Didomi, Usercentrics e implementaciones genéricas, funcionando tanto en la página principal como en iframes. Los modales y popups se cierran usando la tecla Escape, botones de cierre ARIA, botones de cierre basados en clase y botones de diálogo basados en role, eliminando los efectos residuales de blur y backdrop.