API de Conversión de Archivos por Lotes#

La API de lotes de EnConvert convierte varias URLs en una sola solicitud: pasa un array de URLs a /v1/convert/url-to-pdf o /v1/convert/url-to-screenshot y recibe una respuesta HTTP 202 con un batch_id. Cada URL se convierte de forma asíncrona en segundo plano, y los resultados se entregan como URLs de descarga prefirmadas, ya sea una por archivo o agrupadas en un único archivo ZIP. Sigue el progreso consultando GET /v1/convert/batch/{batch_id}, o recibe una notificación por webhook o email al completarse.

Solo claves privadas: El procesamiento por lotes solo está disponible al autenticarte con una clave de API privada (X-API-Key: sk_...). Las claves públicas están restringidas a solicitudes síncronas de una sola URL.

Cómo Funciona#

  1. Envía una solicitud con un array de URLs en el parámetro url a /v1/convert/url-to-pdf o /v1/convert/url-to-screenshot.
  2. La API valida el lote según los límites de tu plan y devuelve HTTP 202 con un batch_id.
  3. Cada URL se convierte en segundo plano y factura una op. La cantidad total del lote se verifica previamente contra tu cuota mensual de ops restante antes de que comience cualquier procesamiento.
  4. Sigue el progreso mediante GET /v1/convert/batch/{batch_id}, o recibe una notificación por webhook o email al completarse.
  5. Descarga los resultados mediante URLs prefirmadas en la respuesta de estado del lote.

Modos de Salida#

Modo Individual (Predeterminado)#

Cada URL genera un archivo separado. Cada archivo obtiene su propia URL de descarga prefirmada en la respuesta de estado del lote.

Solicitud:

curl -X POST https://api.enconvert.com/v1/convert/url-to-pdf \
  -H "X-API-Key: sk_your_private_key" \
  -H "Content-Type: application/json" \
  -d '{
    "url": [
      "https://example.com/page-1",
      "https://example.com/page-2",
      "https://example.com/page-3"
    ]
  }'

Respuesta (HTTP 202 Accepted):

{
    "status": "processing",
    "batch_id": "550e8400-e29b-41d4-a716-446655440000",
    "url_count": 3,
    "output_format": "individual"
}
Nota: Al pasar varias URLs, async_mode se establece automáticamente en true, independientemente de si lo incluyes explícitamente en la solicitud.

Modo de Paquete ZIP#

Establece output_format en true para recibir todos los archivos convertidos agrupados en un único archivo ZIP. Requiere un plan con acceso a salida ZIP.

Solicitud:

curl -X POST https://api.enconvert.com/v1/convert/url-to-pdf \
  -H "X-API-Key: sk_your_private_key" \
  -H "Content-Type: application/json" \
  -d '{
    "url": [
      "https://example.com/page-1",
      "https://example.com/page-2",
      "https://example.com/page-3"
    ],
    "output_format": true,
    "output_filename": "monthly-reports"
  }'

Respuesta (HTTP 202 Accepted):

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

En modo ZIP, todas las URLs se procesan secuencialmente, y los resultados exitosos se agrupan en un único archivo ZIP llamado {output_filename}_{timestamp}.zip (o batch_{timestamp}.zip si no se proporciona un nombre personalizado).


Parámetros del Lote#

Parámetro Tipo Predeterminado Descripción Restricción de Plan
url string[] (obligatorio) Array de URLs a convertir. --
output_format boolean false Establece en true para agrupar todos los resultados en un archivo ZIP. Requiere varias URLs. Requiere acceso a salida ZIP
output_filename string Autogenerado Nombre de archivo personalizado para la salida. En modo ZIP, este nombra el archivo ZIP. --
async_mode boolean true (implícito) Siempre true para lotes. Se habilita automáticamente cuando se proporcionan varias URLs. Requiere acceso asíncrono
notification_email string Email del propietario del proyecto Dirección de correo a notificar al completarse. Si se omite, usa por defecto el email del propietario del proyecto. --
callback_url string null URL de webhook para recibir un POST al completarse. Requiere acceso a webhooks
direct_download -- -- No compatible con lotes. Devuelve un error 400 si se establece con varias URLs. --

Parámetros de Navegador y Renderizado#

Estos ajustes se aplican a cada URL del lote:

Parámetro Tipo Predeterminado Descripción
viewport_width integer 1920 Ancho del viewport del navegador en píxeles.
viewport_height integer 1080 Alto del viewport del navegador en píxeles.
single_page boolean true Renderiza como una única página continua (solo url-to-pdf).
load_media boolean true Espera a que las imágenes y los medios se carguen.
enable_scroll boolean true Desplaza las páginas para activar el contenido de carga diferida.
handle_sticky_header boolean true Detecta y gestiona encabezados fijos/sticky.
handle_cookies boolean true Descarta automáticamente los banners de consentimiento de cookies.
wait_for_images boolean true Espera a que todas las imágenes terminen de cargarse.

Autenticación y Solicitudes Personalizadas#

Se aplican a cada URL del lote. Requieren un plan con acceso a autenticación básica.

Parámetro Tipo Predeterminado Descripción
auth object null Credenciales de HTTP Basic Auth: {"username": "...", "password": "..."}.
cookies array null Array de objetos de cookies inyectados antes de cada carga de página. Máx. 50.
headers object null Encabezados HTTP personalizados enviados con cada solicitud. Máx. 20.

Opciones de PDF (solo url-to-pdf)#

Pasa un objeto pdf_options para controlar el formato de salida del PDF de cada página del lote:

Parámetro Tipo Predeterminado Descripción
page_size string "A4" Tamaño de página con nombre.
orientation string "portrait" "portrait" o "landscape".
margins object {"top": 10, "bottom": 10, "left": 10, "right": 10} Márgenes en mm.
grayscale boolean false Salida en escala de grises mediante Ghostscript.

Consulta del Estado del Lote#

Usa el endpoint de estado del lote para verificar el progreso y obtener las URLs de descarga.

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

Reemplaza {batch_id} con el batch_id devuelto por la solicitud inicial.

Estado de Procesamiento#

Mientras las conversiones siguen en curso:

{
    "batch_id": "550e8400-e29b-41d4-a716-446655440000",
    "status": "processing",
    "total": 3,
    "completed": 1,
    "failed": 0,
    "in_progress": 2,
    "output_mode": "individual",
    "zip_download_url": null,
    "items": [
        {
            "source_url": "https://example.com/page-1",
            "status": "Success",
            "download_url": "https://spaces.example.com/...",
            "output_file_size": 184320,
            "duration": "3.12"
        },
        {
            "source_url": "https://example.com/page-2",
            "status": "In Progress",
            "download_url": null,
            "output_file_size": null,
            "duration": null
        },
        {
            "source_url": "https://example.com/page-3",
            "status": "In Progress",
            "download_url": null,
            "output_file_size": null,
            "duration": null
        }
    ]
}

Estado Completado#

Cuando todas las URLs se han convertido correctamente:

{
    "batch_id": "550e8400-e29b-41d4-a716-446655440000",
    "status": "completed",
    "total": 3,
    "completed": 3,
    "failed": 0,
    "in_progress": 0,
    "output_mode": "individual",
    "zip_download_url": null,
    "items": [
        {
            "source_url": "https://example.com/page-1",
            "status": "Success",
            "download_url": "https://spaces.example.com/...",
            "output_file_size": 184320,
            "duration": "3.12"
        },
        {
            "source_url": "https://example.com/page-2",
            "status": "Success",
            "download_url": "https://spaces.example.com/...",
            "output_file_size": 210944,
            "duration": "4.55"
        },
        {
            "source_url": "https://example.com/page-3",
            "status": "Success",
            "download_url": "https://spaces.example.com/...",
            "output_file_size": 97280,
            "duration": "2.87"
        }
    ]
}

Estado Parcial#

Cuando todas las URLs han finalizado pero algunas fallaron:

{
    "batch_id": "550e8400-e29b-41d4-a716-446655440000",
    "status": "partial",
    "total": 3,
    "completed": 2,
    "failed": 1,
    "in_progress": 0,
    "output_mode": "individual",
    "zip_download_url": null,
    "items": [
        {
            "source_url": "https://example.com/page-1",
            "status": "Success",
            "download_url": "https://spaces.example.com/...",
            "output_file_size": 184320,
            "duration": "3.12"
        },
        {
            "source_url": "https://example.com/page-2",
            "status": "Success",
            "download_url": "https://spaces.example.com/...",
            "output_file_size": 210944,
            "duration": "4.55"
        },
        {
            "source_url": "https://invalid-url.example",
            "status": "Failed",
            "download_url": null,
            "output_file_size": null,
            "duration": "0.87"
        }
    ]
}

Modo ZIP Completado#

En modo ZIP, se proporciona un único zip_download_url para todo el archivo:

{
    "batch_id": "550e8400-e29b-41d4-a716-446655440000",
    "status": "completed",
    "total": 3,
    "completed": 3,
    "failed": 0,
    "in_progress": 0,
    "output_mode": "zip",
    "zip_download_url": "https://spaces.example.com/...",
    "items": [
        {
            "source_url": "https://example.com/page-1",
            "status": "Success",
            "download_url": "https://spaces.example.com/...",
            "output_file_size": 184320,
            "duration": "3.12"
        },
        {
            "source_url": "https://example.com/page-2",
            "status": "Success",
            "download_url": "https://spaces.example.com/...",
            "output_file_size": 210944,
            "duration": "4.55"
        },
        {
            "source_url": "https://example.com/page-3",
            "status": "Success",
            "download_url": "https://spaces.example.com/...",
            "output_file_size": 97280,
            "duration": "2.87"
        }
    ]
}

Valores de Estado del Lote#

Estado Significado
processing Al menos una URL todavía se está convirtiendo.
completed Todas las URLs se convirtieron correctamente.
partial Todas las URLs finalizaron, pero algunas fallaron.
failed Todas las URLs fallaron.

Callbacks de Webhook#

Proporciona un callback_url en la solicitud para recibir una notificación POST automática al completarse. El webhook se envía con Content-Type: application/json y un timeout de 30 segundos. No se realizan reintentos si falla la entrega.

Callback en Modo Individual#

En modo individual, se envía un POST de webhook separado por cada URL a medida que se completa:

{
    "job_id": "12345",
    "status": "success",
    "batch_id": "550e8400-e29b-41d4-a716-446655440000",
    "gcs_uri": "env/files/{project_id}/url-to-pdf/page1_20260405_123456789.pdf",
    "filename": "page1_20260405_123456789.pdf",
    "file_size": 184320
}

Callback en Modo ZIP#

En modo ZIP, se envía un único POST de webhook cuando se completa todo el lote:

{
    "job_id": "550e8400-e29b-41d4-a716-446655440000",
    "status": "success",
    "batch_id": "550e8400-e29b-41d4-a716-446655440000",
    "gcs_uri": "env/files/{project_id}/url-to-pdf/batch_20260405_123456789.zip",
    "filename": "batch_20260405_123456789.zip",
    "file_size": 456789,
    "total_tasks": 3,
    "successful_tasks": 2,
    "failed_tasks": 1,
    "tasks": [
        {"url": "https://example.com/page-1", "status": "success", "filename": "page1.pdf"},
        {"url": "https://example.com/page-2", "status": "success", "filename": "page2.pdf"},
        {"url": "https://invalid.example", "status": "failed", "error": "Page load timeout"}
    ]
}
Diferencia en el comportamiento de las notificaciones: En modo individual, recibes N POSTs de webhook separados (uno por URL) y N emails separados. En modo ZIP, recibes un POST de webhook y un email para todo el lote. Ten esto en cuenta al diseñar tu manejador de webhooks.

Notificaciones por Email#

Se envía un email de finalización a notification_email cuando el lote termina. Si no se proporciona notification_email, el email se envía por defecto al correo del propietario del proyecto.

El email incluye:

  • Estado del trabajo (success/failed) con un banner de color
  • ID del Lote
  • Para trabajos por lotes: una tabla que enumera cada URL, su estado y el nombre del archivo de salida
  • Un enlace para descargar los resultados desde el panel de control

Restricciones por Plan de Suscripción#

Función Founding Indie Studio Enterprise
Procesamiento por lotes No
Modo asíncrono No
Agrupación de salida ZIP No No
Callbacks de webhook No No
HTTP Basic Auth / Cookies / Headers No
Límite de tamaño de lote 0 Según el plan Según el plan Ilimitado
Conversiones mensuales 100 Según el plan Según el plan Ilimitado

Ejemplos de Código#

Python -- Modo Individual con Sondeo#

import requests
import time

# Submit batch
response = requests.post(
    "https://api.enconvert.com/v1/convert/url-to-pdf",
    headers={"X-API-Key": "sk_your_private_key"},
    json={
        "url": [
            "https://example.com/page-1",
            "https://example.com/page-2",
            "https://example.com/page-3"
        ]
    }
)

data = response.json()
batch_id = data["batch_id"]
print(f"Batch started: {batch_id} ({data['url_count']} URLs)")

# Poll for completion
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"):
        for item in status["items"]:
            if item["download_url"]:
                print(f"  {item['source_url']} -> {item['download_url']}")
        break

    time.sleep(5)

Python -- Modo ZIP con Webhook#

import requests

response = requests.post(
    "https://api.enconvert.com/v1/convert/url-to-pdf",
    headers={"X-API-Key": "sk_your_private_key"},
    json={
        "url": [
            "https://example.com/page-1",
            "https://example.com/page-2",
            "https://example.com/page-3"
        ],
        "output_format": True,
        "output_filename": "monthly-reports",
        "callback_url": "https://your-server.com/webhook/enconvert",
        "pdf_options": {
            "page_size": "A4",
            "orientation": "portrait"
        }
    }
)

data = response.json()
print(f"Batch started: {data['batch_id']}")
print(f"URLs: {data['url_count']}, Format: {data['output_format']}")
# Results will be delivered to your webhook URL

Node.js -- Capturas de Pantalla por Lotes#

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/page-1",
            "https://example.com/page-2",
            "https://example.com/page-3"
        ],
        output_format: true,
        output_filename: "screenshots-bundle"
    })
});

const data = await response.json();
console.log(`Batch ${data.batch_id}: ${data.url_count} screenshots queued`);

Respuestas de Error#

Estado Condición
400 Bad Request url está vacío o falta
400 Bad Request output_format=true con una sola URL (requiere varias URLs)
400 Bad Request direct_download=true con varias URLs (no compatible)
400 Bad Request direct_download=true con async_mode=true
400 Bad Request Clave pública intentando usar varias URLs
402 Payment Required El lote excedería la cuota mensual de ops restante
402 Payment Required Se alcanzó el límite de almacenamiento
403 Forbidden El procesamiento asíncrono no está disponible en el plan actual
403 Forbidden El procesamiento por lotes no está disponible (límite de lote es 0)
403 Forbidden El tamaño del lote excede el límite de lote del plan
403 Forbidden La salida ZIP no está disponible en el plan actual
403 Forbidden Los callbacks de webhook no están disponibles en el plan actual
404 Not Found Lote no encontrado (batch_id incorrecto o proyecto incorrecto)

Límites#

Límite Valor
Tamaño de lote Depende del plan (Founding: deshabilitado)
Conversiones mensuales Depende del plan (lote completo verificado previamente)
Timeout de entrega de webhook 30 segundos (sin reintentos)
Máximo de cookies por solicitud 50
Máximo de encabezados personalizados por solicitud 20
Retención de archivos Depende del plan

Preguntas frecuentes#

¿Cómo convierto varias URLs a PDF en una sola solicitud de API?#

Envía un POST a /v1/convert/url-to-pdf con un array de URLs en el parámetro url, autenticado con una clave de API privada (sk_...). La API responde con HTTP 202 y un batch_id, y cada URL se convierte en segundo plano.

¿Puedo obtener todos los resultados de la conversión por lotes en un único archivo ZIP?#

Sí. Establece output_format en true para agrupar todos los resultados exitosos en un archivo ZIP llamado {output_filename}_{timestamp}.zip (o batch_{timestamp}.zip si no se indica un nombre personalizado). La salida ZIP requiere un plan con acceso a salida ZIP (Studio, Production o Enterprise) y varias URLs en la solicitud.

¿Cómo verifico el estado de un trabajo de conversión por lotes?#

Consulta GET /v1/convert/batch/{batch_id} con tu clave de API privada. La respuesta indica processing, completed, partial, o failed, junto con elementos por URL que contienen download_url, output_file_size, y duration.

¿Por qué mi solicitud por lotes devuelve 403 Forbidden?#

Un 403 Forbidden significa que se alcanzó una restricción del plan: el procesamiento asíncrono no está disponible en tu plan, el procesamiento por lotes está deshabilitado (límite de lote es 0), el tamaño del lote excede el límite de tu plan, o una función restringida como la salida ZIP o los callbacks de webhook no está incluida en tu plan.

¿Puedo usar el procesamiento por lotes con una clave de API pública?#

No. El procesamiento por lotes requiere una clave de API privada (sk_...). Las claves públicas están restringidas a solicitudes síncronas de una sola URL, y enviar varias URLs con una clave pública devuelve 400 Bad Request.