API de Capturas de Pantalla de Sitios Web#
El endpoint POST /v1/convert/website-to-screenshot descubre cada página de un sitio web (mediante análisis de sitemap o un rastreo completo en anchura), captura una captura de pantalla PNG de página completa de cada página, y agrupa los resultados en un único archivo ZIP. Los trabajos siempre se ejecutan de forma asíncrona: la API devuelve HTTP 202 con un batch_id de inmediato, la finalización se señala mediante sondeo del estado del batch, un callback de webhook, o una notificación por correo electrónico, y la respuesta del estado del batch incluye una URL de descarga prefirmada para el ZIP terminado. Requiere un plan de pago y una clave de API privada.
Endpoint#
POST /v1/convert/website-to-screenshot
Content-Type: application/json
Formato de salida: Archivo ZIP que contiene una captura de pantalla PNG por cada página descubierta.
Modo: Siempre asíncrono. Devuelve HTTP 202 de inmediato.
Autenticación#
Este endpoint requiere una clave de API privada. Las claves públicas no son compatibles con la captura de sitios web.
X-API-Key: sk_your_private_key
Parámetros de la solicitud#
Parámetros de descubrimiento del sitio web#
| Parámetro | Tipo | Requerido | Predeterminado | Descripción | Restricción por plan |
|---|---|---|---|---|---|
url |
string |
Sí | -- | La URL base del sitio web (p. ej. https://example.com). Se usa como raíz para el descubrimiento de páginas. |
-- |
crawl_mode |
string |
No | "auto" |
Método de descubrimiento de URLs. Uno de "auto", "sitemap", o "full". Consulta Modos de rastreo más abajo. |
Sitemap requiere Indie+, Full requiere Studio+ |
include_patterns |
string[] |
No | null |
Patrones regex para incluir en lista blanca las URLs descubiertas. Solo se usa en el modo de rastreo full. |
-- |
exclude_patterns |
string[] |
No | Valores predeterminados del sistema | Patrones regex para poner en lista negra las URLs. Solo se usa en el modo de rastreo full. Cuando se omite, usa valores predeterminados integrados que excluyen recursos estáticos, páginas de login/admin/carrito, y paginación profunda. |
-- |
Parámetros de notificación#
| Parámetro | Tipo | Requerido | Predeterminado | Descripción | Restricción por plan |
|---|---|---|---|---|---|
output_filename |
string |
No | Generado automáticamente | Nombre base personalizado para el archivo ZIP de salida. La marca de tiempo se añade automáticamente. | -- |
notification_email |
string |
No | Correo del propietario del proyecto | Dirección de correo electrónico para notificar cuando el trabajo finalice. | -- |
callback_url |
string |
No | -- | URL de webhook para recibir una solicitud POST al finalizar. | Requiere acceso a webhooks |
Parámetros del navegador y renderizado#
Estos ajustes se aplican a la captura de cada página individual dentro del sitio web.
| Parámetro | Tipo | Requerido | Predeterminado | Descripción | Restricción por 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. | -- |
load_media |
boolean |
No | true |
Espera a que todas las imágenes y videos terminen de cargar antes de la captura. | -- |
enable_scroll |
boolean |
No | true |
Desplaza cada página para activar contenido con carga diferida. | -- |
handle_sticky_header |
boolean |
No | true |
Detecta encabezados fijos (sticky/fixed) y los gestiona antes de la captura. | -- |
handle_cookies |
boolean |
No | true |
Cierra automáticamente los banners de consentimiento de cookies. | -- |
wait_for_images |
boolean |
No | true |
Espera a que todos los elementos <img> terminen de cargar. |
-- |
wait_for_selector |
string |
No | null |
Selector CSS que se espera antes de la captura, aplicado a cada página. 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 | Requerido | Predeterminado | Descripción | Restricción por plan |
|---|---|---|---|---|---|
auth |
object |
No | null |
Credenciales de HTTP Basic Auth aplicadas a cada página. Formato: {"username": "...", "password": "..."}. |
Requiere acceso a autenticación básica |
cookies |
array |
No | null |
Array de objetos de cookies inyectados antes de cada carga de página. Máximo 50 cookies. | Requiere acceso a autenticación básica |
headers |
object |
No | null |
Encabezados HTTP personalizados enviados con cada solicitud. Máximo 20 encabezados. | Requiere acceso a autenticación básica |
single_page y pdf_options no son aplicables a las capturas de pantalla. Cada página siempre se captura como una única imagen PNG de página completa.
Modos de rastreo#
"auto" (predeterminado)#
Usa el modo de rastreo más alto que permite tu plan. Si tu plan admite rastreo completo, ejecuta un rastreo completo. Si tu plan solo admite sitemap, ejecuta descubrimiento por sitemap.
"sitemap"#
Descubre páginas analizando el sitemap.xml del sitio web:
- Obtiene
{base_url}/sitemap.xml(tiempo límite de 30 segundos) - Si el elemento raíz es
<sitemapindex>, obtiene recursivamente cada sitemap hijo - Extrae todas las entradas
<url><loc>de los elementos<urlset> - Devuelve la lista completa de URLs descubiertas
Devuelve un error si falta el sitemap, si devuelve un estado distinto de 200, si contiene XML inválido, o si no tiene URLs.
"full"#
Realiza un rastreo exhaustivo en dos fases:
Fase 1 -- Descubrimiento de semillas:
- Analiza
robots.txten busca de directivas de sitemap y reglas de rastreo - Verifica rutas estándar de sitemap (
/sitemap.xml,/wp-sitemap.xml,/sitemap_index.xml, etc.) - Descubre feeds RSS/Atom a partir de etiquetas
<link>y rutas de feed comunes - Extrae URLs semilla de todas las fuentes descubiertas
Fase 2 -- Rastreo de enlaces en anchura:
- Comienza desde la URL base más todas las URLs semilla
- Visita cada página y encola los enlaces del mismo dominio
- Aplica
include_patternsyexclude_patternspara filtrar enlaces - Respeta las reglas de
robots.txt - Detecta y evita trampas de URL infinitas (páginas de calendario, filtros facetados, etc.)
- Deduplica URLs normalizando el esquema, el host, los parámetros de consulta, y eliminando parámetros de seguimiento (
utm_*,fbclid,gclid, etc.)
Patrones de exclusión predeterminados (cuando no se proporciona exclude_patterns):
- Recursos estáticos:
*.pdf,*.zip,*.jpg,*.png,*.gif,*.svg,*.css,*.js,*.xml,*.json,*.mp4,*.webm,*.woff,*.woff2 - Rutas protegidas:
/login,/admin,/cart,/checkout - Paginación profunda: URLs con parámetros
page=que superan los 3 dígitos
Respuesta#
202 Accepted (inmediato)#
{
"status": "processing",
"batch_id": "550e8400-e29b-41d4-a716-446655440000",
"url_count": 42,
"total_discovered": 42,
"discovery_method": "sitemap",
"output_format": "zip"
}
| Campo | Descripción |
|---|---|
batch_id |
UUID para hacer seguimiento del trabajo mediante sondeo del estado del batch o webhook. |
url_count |
Número de páginas que se capturarán. |
total_discovered |
Total de páginas descubiertas por el rastreo. |
discovery_method |
"sitemap" o "full_crawl" según el modo de rastreo efectivo. |
Sondeo del estado del batch#
Consulta 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 una URL de descarga prefirmada para el ZIP cuando finaliza. Consulta Sondeo del estado del batch para ver el esquema completo de la respuesta.
Payload del callback de webhook#
Cuando se proporciona callback_url, EnConvert envía una solicitud POST al finalizar:
{
"job_id": "batch-uuid",
"status": "success",
"batch_id": "550e8400-e29b-41d4-a716-446655440000",
"gcs_uri": "env/files/{project_id}/url-to-screenshot/website_20260405_123456789.zip",
"filename": "website_20260405_123456789.zip",
"file_size": 12345678,
"total_tasks": 42,
"successful_tasks": 40,
"failed_tasks": 2,
"tasks": [
{"url": "https://example.com/", "status": "success", "filename": "example_20260405_001.png"},
{"url": "https://example.com/about", "status": "success", "filename": "example_20260405_002.png"},
{"url": "https://example.com/broken", "status": "failed", "error": "Timeout"}
]
}
Notificación por correo electrónico#
Se envía un correo de finalización a notification_email (o al correo del propietario del proyecto por defecto) cuando el trabajo termina, independientemente de si tuvo éxito o falló.
Restricciones por plan de suscripción#
| Función | Founding | Indie | Studio | Enterprise |
|---|---|---|---|---|
| Captura de sitio web | No | Sí | Sí | Sí |
| Modo de rastreo por sitemap | No | Sí | Sí | Sí |
| Modo de rastreo completo | No | No | Sí | Sí |
| Callbacks de webhook | No | No | Sí | Sí |
| HTTP Basic Auth | No | Sí | Sí | Sí |
| Inyección de cookies | No | Sí | Sí | Sí |
| Encabezados personalizados | No | Sí | Sí | Sí |
| Límite de tamaño de batch | 0 | Según el plan | Según el plan | Ilimitado |
| Conversiones mensuales | 100 | Según el plan | Según el plan | Ilimitado |
403 Forbidden.
Ejemplos de código#
Python (clave privada)#
import requests
import time
# Start the website capture
response = requests.post(
"https://api.enconvert.com/v1/convert/website-to-screenshot",
headers={"X-API-Key": "sk_your_private_key"},
json={
"url": "https://example.com",
"crawl_mode": "sitemap",
"output_filename": "example-screenshots",
"viewport_width": 1440,
"viewport_height": 900
}
)
data = response.json()
print(f"Batch ID: {data['batch_id']}")
print(f"Pages found: {data['url_count']}")
# Poll for completion
batch_id = data["batch_id"]
while True:
status = requests.get(
f"https://api.enconvert.com/v1/convert/batch/{batch_id}",
headers={"X-API-Key": "sk_your_private_key"}
).json()
print(f"Status: {status['status']} ({status['completed']}/{status['total']})")
if status["status"] in ("completed", "partial", "failed"):
if status.get("zip_download_url"):
print(f"Download: {status['zip_download_url']}")
break
time.sleep(5)
PHP (clave privada)#
$ch = curl_init("https://api.enconvert.com/v1/convert/website-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",
"crawl_mode" => "sitemap",
"output_filename" => "example-screenshots",
"viewport_width" => 1440,
"viewport_height" => 900
])
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
echo "Batch ID: " . $response["batch_id"] . "\n";
echo "Pages found: " . $response["url_count"] . "\n";
Node.js (clave privada)#
const response = await fetch("https://api.enconvert.com/v1/convert/website-to-screenshot", {
method: "POST",
headers: {
"Content-Type": "application/json",
"X-API-Key": "sk_your_private_key"
},
body: JSON.stringify({
url: "https://example.com",
crawl_mode: "sitemap",
output_filename: "example-screenshots",
viewport_width: 1440,
viewport_height: 900
})
});
const data = await response.json();
console.log(`Batch ID: ${data.batch_id}`);
console.log(`Pages found: ${data.url_count}`);
// Poll for completion
const poll = async () => {
const status = await fetch(
`https://api.enconvert.com/v1/convert/batch/${data.batch_id}`,
{ headers: { "X-API-Key": "sk_your_private_key" } }
).then(r => r.json());
console.log(`Status: ${status.status} (${status.completed}/${status.total})`);
if (["completed", "partial", "failed"].includes(status.status)) {
if (status.zip_download_url) console.log(`Download: ${status.zip_download_url}`);
return;
}
setTimeout(poll, 5000);
};
poll();
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",
"crawl_mode": "sitemap",
"output_filename": "example-screenshots",
"viewport_width": 1440,
"viewport_height": 900,
})
req, _ := http.NewRequest("POST", "https://api.enconvert.com/v1/convert/website-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))
}
Con callback de webhook#
{
"url": "https://example.com",
"crawl_mode": "full",
"callback_url": "https://your-server.com/webhook/enconvert",
"output_filename": "example-full-site-screenshots",
"include_patterns": [".*\\/blog\\/.*", ".*\\/docs\\/.*"]
}
Con autenticación (sitio protegido con contraseña)#
{
"url": "https://staging.example.com",
"crawl_mode": "sitemap",
"auth": {
"username": "admin",
"password": "staging-password"
},
"cookies": [
{"name": "session_token", "value": "abc123", "domain": "staging.example.com"}
]
}
Respuestas de error#
| Estado | Condición |
|---|---|
400 Bad Request |
Falta el parámetro url o está vacío |
400 Bad Request |
No se encontraron URLs en el sitemap |
400 Bad Request |
Tiempo de espera agotado al obtener el sitemap (límite de 30 segundos) |
400 Bad Request |
Respuesta distinta de 200 desde la URL del sitemap |
400 Bad Request |
XML inválido en el sitemap |
400 Bad Request |
Formato de sitemap no reconocido |
400 Bad Request |
No se descubrieron páginas (el rastreo completo encontró cero URLs) |
400 Bad Request |
Estructura inválida de auth, cookies, o headers |
402 Payment Required |
El número de páginas descubiertas supera la cuota mensual de ops |
402 Payment Required |
Se alcanzó el límite de almacenamiento |
403 Forbidden |
El rastreo de sitios web no está disponible en el plan actual (plan Founding) |
403 Forbidden |
El modo de rastreo completo requiere el plan Studio o superior |
403 Forbidden |
El número de páginas descubiertas supera el límite de tamaño del batch |
403 Forbidden |
Función no disponible en el plan (webhook, autenticación básica) |
500 Internal Server Error |
Fallo de rastreo o de captura |
Límites#
| Límite | Valor |
|---|---|
| Tiempo límite para obtener el sitemap | 30 segundos |
| Tiempo límite global de rastreo (modo full) | 10 minutos |
| Profundidad máxima de rastreo (modo full) | 10 niveles |
| Tiempo límite de rastreo por página (modo full) | 30 segundos |
| Límite de memoria del rastreador | 512 MB |
| Umbral de trampa infinita | 20 URLs por patrón de URL |
| Tiempo límite para obtener robots.txt | 10 segundos |
| Máximo de páginas por rastreo | Límite de tamaño de batch del plan |
| Máximo de cookies por solicitud | 50 |
| Máximo de encabezados personalizados por solicitud | 20 |
| Tiempo límite de entrega de webhook | 30 segundos |
| Conversiones mensuales | Según el plan |
| Retención de archivos | Según el plan |
Preguntas frecuentes#
¿Cómo capturo cada página de un sitio web con una API?#
Envía una solicitud POST a /v1/convert/website-to-screenshot con la url base del sitio y tu clave privada en el encabezado X-API-Key. La API descubre cada página (sitemap o rastreo completo), captura un PNG de página completa de cada una, las agrupa en un archivo ZIP, y devuelve HTTP 202 con un batch_id que puedes sondear para obtener el enlace de descarga.
¿Puedo controlar el tamaño o el formato de las capturas de pantalla?#
El ancho de la captura coincide con viewport_width (predeterminado 1920), y viewport_height se usa como referencia de renderizado. Cada página siempre se captura como un único PNG de página completa. Los parámetros single_page y pdf_options no se aplican a las capturas de pantalla.
¿Cómo descargo las capturas de pantalla cuando el trabajo finaliza?#
Sondea GET /v1/convert/batch/{batch_id} con tu clave privada para obtener el estado agregado, los estados por URL, y una URL de descarga prefirmada del ZIP, o pasa un callback_url para recibir un POST de webhook al finalizar. También se envía un correo de finalización a notification_email (o al propietario del proyecto por defecto).
¿Puedo capturar un sitio protegido con contraseña o en staging?#
Sí, en planes con acceso a autenticación básica: pasa auth con username y password para aplicar HTTP Basic Auth a cada página, inyecta hasta 50 cookies de sesión, o envía hasta 20 headers personalizados.
¿Por qué el endpoint de captura de pantalla de sitios web devuelve 403 Forbidden?#
Las causas más comunes: la captura de sitios web no está disponible en el plan Founding, crawl_mode: "full" requiere Studio o superior, el número de páginas descubiertas supera el límite de tamaño de batch de tu plan, o una función solicitada (webhook, autenticación básica) no está incluida en tu plan.