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.
X-API-Key: sk_...). Las claves públicas están restringidas a solicitudes síncronas de una sola URL.
Cómo Funciona#
- Envía una solicitud con un array de URLs en el parámetro
urla/v1/convert/url-to-pdfo/v1/convert/url-to-screenshot. - La API valida el lote según los límites de tu plan y devuelve HTTP 202 con un
batch_id. - 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.
- Sigue el progreso mediante
GET /v1/convert/batch/{batch_id}, o recibe una notificación por webhook o email al completarse. - 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"
}
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"}
]
}
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 | Sí | Sí | Sí |
| Modo asíncrono | No | Sí | Sí | Sí |
| Agrupación de salida ZIP | No | No | Sí | Sí |
| Callbacks de webhook | No | No | Sí | Sí |
| HTTP Basic Auth / Cookies / Headers | No | Sí | Sí | Sí |
| 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.