Códigos de error de la API de EnConvert#
Esta referencia enumera todos los códigos de estado HTTP y mensajes de error que devuelve la API de EnConvert, desde 200 OK para conversiones síncronas y 202 Accepted para trabajos asíncronos y por lotes, hasta las respuestas de error documentadas a continuación. Cada sección de error enumera las cadenas de mensaje exactas, la condición que activa cada una, y cómo corregir la solicitud. Los cuerpos de error no tienen todos la misma forma: existen seis, y la sección Formato de respuesta de error muestra cada una.
Un trabajo que falla después de haber sido aceptado no es un error HTTP. El 202 se mantiene y el fallo aparece en la carga de estado del trabajo cuando lo consultas, tal como se describe en Trabajos síncronos y asíncronos.
Códigos de estado HTTP#
| Código | Estado | Descripción |
|---|---|---|
200 |
OK | Conversión completada correctamente (modo síncrono). |
202 |
Accepted | El trabajo por lotes o asíncrono fue aceptado para procesamiento en segundo plano. |
400 |
Bad Request | Parámetros inválidos, campos obligatorios faltantes, cuerpo de solicitud malformado o contenido de archivo inválido. |
401 |
Unauthorized | Clave de API o token JWT faltante, inválido o expirado. |
402 |
Payment Required | Se agotó la cuota mensual de ops, no hay periodo de facturación activo, se alcanzó el tope de watchers, se alcanzó el límite de almacenamiento, o un endpoint V2 está desactivado en tu plan. |
403 |
Forbidden | Restricción de tipo de clave, de dominio o de lista de endpoints permitidos, una restricción de función V1 (async, webhooks, salida ZIP, autenticación básica, lotes), o acceso a un recurso de otro proyecto. |
404 |
Not Found | El recurso solicitado (trabajo, lote, operación, archivo, watcher o widget) no existe, o la ruta no corresponde a ningún endpoint. |
405 |
Method Not Allowed | La ruta existe, pero no para el método HTTP que usaste. |
409 |
Conflict | Un job_id proporcionado por el cliente ya está en uso, o se pidió un reintento de webhook sobre un trabajo de ingest que no ha finalizado. |
410 |
Gone | Un artefacto V2 o un archivo comprimido de lote superó la ventana de retención de archivos de tu plan y ya no está en el almacenamiento. |
413 |
Payload Too Large | El archivo subido supera el límite de tamaño de tu plan de suscripción. |
415 |
Unsupported Media Type | La URL de destino devolvió contenido que este conversor no puede renderizar (p. ej., JSON a url-to-pdf). |
422 |
Unprocessable Entity | El cuerpo de la solicitud no pasó la validación de esquema (incluidos campos desconocidos en endpoints V2), o falló una precondición de renderizado, como un wait_for_selector que nunca apareció. |
429 |
Too Many Requests | Se superó un límite de tasa de solicitudes de ventana corta. Este no es el código de cuota; agotar la asignación mensual responde 402. |
500 |
Internal Server Error | Error inesperado durante la conversión (falló nuestro motor). |
502 |
Bad Gateway | No se pudo alcanzar el sitio de destino, falló un proveedor externo (upstream), o el destino sirvió un desafío anti-bot sin contenido de página (/v2/perceive, salvo que se defina allow_degraded). |
503 |
Service Unavailable | Un conversor no está disponible, el grupo de renderizado o la compuerta de admisión de conversiones está a plena capacidad, o una dependencia externa está caída. |
504 |
Gateway Timeout | El sitio de destino tardó demasiado en responder o en terminar de cargar, o la solicitud superó el presupuesto de 300 segundos del gateway. |
Formato de respuesta de error#
Existen seis formas de cuerpo. Cuál recibes depende de dónde ocurrió el fallo, no solo del código de estado, así que comprueba el tipo de detail antes de leerlo.
1. detail de tipo cadena. El caso común, y la única forma que ven la mayoría de las integraciones.
{
"detail": "Authentication required"
}
2. detail de tipo objeto. El 413 por tamaño de archivo del plan. El objeto estructurado es el valor de detail, así que lee body.detail.max_size, no body.max_size. Consulta 413 Payload Too Large.
{
"detail": {
"error": "File too large",
"file_size": 10485760,
"max_size": 5242880,
"tier": "free",
"key_type": "private"
}
}
3. detail de tipo array más errors. La validación de esquema (422) devuelve ambos: detail es la salida sin procesar del validador y errors es un array paralelo de cadenas legibles por personas. Consulta 422 Unprocessable Entity.
4. Envoltorio de conversión tipificado. {"error", "code", "detail"}, con un code legible por máquina. Solo lo emiten los tres endpoints V1 de conversión de URL. Consulta Errores de conversión del navegador.
5. Excepción no controlada. Un 500 que no proviene de un conversor no tiene detail en absoluto:
{
"error": "Internal server error",
"event_id": "a1b2c3d4"
}
6. Tiempo de espera de solicitud del gateway. El propio presupuesto de 300 segundos del gateway produce un 504 sin detail y sin code:
{
"error": "Request timeout"
}
400 Bad Request#
Se devuelve cuando la solicitud contiene parámetros inválidos, campos faltantes o datos malformados.
Validación de entrada#
| Mensaje | Condición |
|---|---|
'url' must be provided |
Falta el campo url o está vacío en los endpoints basados en URL. |
Invalid file format '{ext}' for {endpoint}. Allowed: {list} |
La extensión del archivo subido no coincide con los formatos aceptados por el endpoint. |
File content does not match the '{endpoint}' input type. |
La extensión se aceptó, pero los bytes mágicos del archivo corresponden a otro formato. |
Invalid pdf_options: {error} |
JSON malformado en el campo de formulario pdf_options. |
Validación de lote y modo#
| Mensaje | Condición |
|---|---|
Public keys only support a single URL input |
Una clave pública o del panel intentó enviar varias URLs. |
output_format=True requires multiple URLs |
Se solicitó agrupación en ZIP con una sola URL. |
direct_download not supported for multiple URLs |
direct_download=true con un array de URLs. |
direct_download only works in sync mode |
direct_download=true combinado con async_mode=true. |
Validación de autenticación, cookies y encabezados#
| Mensaje | Condición |
|---|---|
'auth' must be an object with 'username' and 'password' |
El parámetro auth tiene una estructura incorrecta. |
'cookies' must be an array of cookie objects |
cookies no es un array. |
'cookies' array must not exceed 50 entries |
Se proporcionaron más de 50 cookies. |
Cookie at index {i} must be an object |
La entrada de cookie no es un diccionario. |
Cookie at index {i} must have 'name' and 'value' |
A la cookie le faltan campos obligatorios. |
Cookie at index {i} must have 'domain' or 'url' |
A la cookie le faltan tanto domain como url. |
'headers' must be an object of header name/value pairs |
headers no es un diccionario. |
'headers' must not exceed 20 entries |
Se proporcionaron más de 20 encabezados personalizados. |
Header '{name}' cannot be overridden |
Intento de configurar un encabezado bloqueado. El conjunto bloqueado es host, content-length, transfer-encoding, connection, upgrade, te, trailer. |
Header '{name}' value must be a string |
El valor del encabezado no es una cadena de texto. |
Cannot use both 'auth' and an 'Authorization' header. Use 'auth' for HTTP Basic Auth or 'headers' for Bearer/custom auth, not both. |
Se proporcionaron tanto un objeto auth como un encabezado personalizado Authorization. |
Seguridad de URL (SSRF)#
Todos los endpoints basados en URL examinan la url de destino antes de obtenerla. Estos mensajes se devuelven como 400 cuando la URL no es una dirección pública http(s).
| Mensaje | Condición |
|---|---|
Only http:// and https:// URLs are supported. |
La URL usa un esquema distinto de http o https. |
URLs with embedded credentials are not allowed. Use the 'auth' field for HTTP Basic Auth. |
La URL incrusta un nombre de usuario/contraseña (https://user:pass@host/). |
URL has no hostname. |
No se pudo analizar la URL para obtener un host. |
This hostname is not allowed. |
El host es localhost o un nombre de host de metadatos de la nube. |
URLs resolving to private or internal addresses are not allowed. |
La URL es, o se resuelve en, una IP privada, de bucle local (loopback), local de enlace (link-local), reservada o de cualquier otro modo no pública. |
Non-standard IP address notation is not allowed. |
El host usa una notación de IP octal, hexadecimal o de entero empaquetado que podría resolverse de forma ambigua. |
Could not resolve hostname '{hostname}'. |
Falló la resolución DNS del host. |
This URL is blocked by the site's threat policy. |
El host de destino está en la lista de denegación de la política de amenazas, que se comprueba junto con el filtro SSRF. |
Validación de opciones de renderizado#
| Mensaje | Condición |
|---|---|
'wait_for_selector' must be a string |
wait_for_selector no era una cadena. |
'wait_for_selector' is too long (max 1000 chars) |
El selector supera los 1000 caracteres. |
'wait_for_selector_timeout' must be a positive integer (ms) |
El tiempo de espera falta, es cero, negativo o no es un entero. |
'wait_for_selector_timeout' must not exceed 60000 ms |
El tiempo de espera supera el techo de 60 segundos. |
'block_ads' must be a boolean / 'block_media' must be a boolean |
La bandera de bloqueo no era un booleano. |
Errores de sitemap y rastreo#
| Mensaje | Condición |
|---|---|
No URLs found in sitemap: {url} |
El sitemap se analizó pero no contiene URLs. |
Timeout fetching sitemap: {url} |
La obtención del sitemap superó el tiempo de espera de 30 segundos. |
Could not fetch sitemap: {url} returned {status} |
La URL del sitemap devolvió un estado HTTP distinto de 200. |
Invalid XML in sitemap: {url} |
No se pudo analizar el XML del sitemap. |
Unrecognized sitemap format at {url}: root element is <{tag}> |
El elemento raíz del sitemap no es <urlset> ni <sitemapindex>. |
No pages discovered on {base_url} |
El rastreo completo finalizó pero no encontró ninguna página. |
Errores de contenido de conversión#
| Mensaje | Condición |
|---|---|
Invalid JSON: {error} |
El archivo JSON contiene una sintaxis JSON inválida. |
Invalid YAML: {error} |
El archivo YAML contiene una sintaxis YAML inválida. |
Invalid TOML: {error} |
El archivo TOML contiene una sintaxis TOML inválida. |
Invalid HTML encoding (expected UTF-8) |
El archivo HTML no está codificado en UTF-8. |
Invalid Markdown encoding (expected UTF-8) |
El archivo Markdown no está codificado en UTF-8. |
JSON must be an array of objects for CSV conversion |
La entrada de json-to-csv no es un array. |
JSON array is empty |
La entrada de json-to-csv es un array vacío. |
CSV file is empty or has no valid rows |
El archivo CSV no tiene filas de datos. |
XML structure cannot be converted to CSV |
El XML no es tabular (xml-to-csv). |
Turnstile verification failed |
El desafío antibots de Cloudflare Turnstile falló. |
Turnstile token required |
Solicitud del widget sin un token de Turnstile. |
401 Unauthorized#
Se devuelve cuando falta la autenticación o es inválida.
| Mensaje | Condición |
|---|---|
Authentication required |
No se proporcionó clave de API ni token JWT en la solicitud. |
Invalid API Key format |
La clave de API es demasiado corta o no empieza con sk_ o pk_. |
Invalid API Key |
El hash de la clave de API no se encontró en la base de datos. |
API Key revoked |
La clave de API fue desactivada desde el panel. |
Token has expired |
El token de acceso JWT expiró (vida útil de 1 hora). |
Invalid token |
El JWT está malformado, alterado o es inválido de alguna otra forma. |
Refresh token has expired |
El token de actualización expiró (vida útil de 7 días). |
Invalid refresh token |
El token de actualización está malformado o es inválido. |
Invalid token type |
El token se decodificó correctamente, pero no es del tipo esperado (refresh). |
No refresh token |
Se llamó al endpoint de actualización del widget sin la cookie refresh_token. |
Refresh token not found |
El token de actualización presentado no está almacenado en ninguna sesión. |
User not found or invalid |
El token se decodificó, pero su sujeto ya no corresponde a una cuenta utilizable. |
Project not found |
No se pudo analizar el id de proyecto de la clave o del token al comprobar la cuota de ops. |
402 Payment Required#
Se devuelve cuando se supera un límite de uso. La división entre 402 y 403 no es simétrica y suele confundir. Toda condición de cuota responde 402, igual que un endpoint V2 desactivado en tu plan. Las restricciones de funciones V1 responden 403 (async, webhooks, salida ZIP, autenticación básica, lotes). La limitación de tasa es un mecanismo aparte que responde 429, nunca 402.
| Mensaje | Condición |
|---|---|
Monthly operations limit reached ({used}/{limit}). Upgrade your plan to continue. |
El contador mensual unificado de ops alcanzó la asignación del plan. Plan Founding: 500 ops. Cada endpoint consume este único contador. En cualquier plan de pago con excedente activado, las solicitudes continúan a $0.02/op en lugar de fallar. |
This request needs {units} operations but only {remaining} of your {limit} monthly operations remain. Upgrade your plan to continue. |
La solicitud por lotes superaría la cuota mensual de ops restante. Todo el lote se rechaza de antemano. |
No active billing period found for this project. Contact support to restore your subscription. |
El proyecto no tiene periodo de uso y no fue posible aprovisionar uno a partir de su suscripción. La compuerta falla en cerrado en lugar de conceder una operación gratuita. |
Storage limit reached. Delete files or upgrade your storage plan to continue. |
El uso de almacenamiento del proyecto alcanzó la asignación de almacenamiento del plan. |
Active watcher limit reached ({active_count}/{limit}). Delete an existing watcher or upgrade your plan to add more. |
El proyecto ya tiene el número máximo de watchers activos de su plan. Los watchers no consumen ops; este es un tope sobre cuántos existen a la vez. |
{Feature} is not available on your current plan. Upgrade to a V2-inclusive plan to access this endpoint. |
Un endpoint V2 está deshabilitado para el plan. |
Quedarse sin créditos mensuales de IA no produce un 402. La extracción de esquema recurre al resultado heurístico y CSS, y la solicitud sigue teniendo éxito. Las asignaciones, los precios y qué cuenta como una operación están en Límites de tasa y cuotas.
403 Forbidden#
Se devuelve cuando se deniega el acceso debido a restricciones de tipo de clave, dominio, función V1 del plan o endpoint. Las restricciones de endpoints V2 son la excepción: responden 402, no 403.
Restricciones de clave de API y token#
| Mensaje | Condición |
|---|---|
Private API keys cannot be used from browsers |
Se usó una clave privada (sk_...) en una solicitud con un encabezado Origin de navegador. Usa en su lugar una clave pública con JWT. |
Domain {origin} not authorized |
El origen de la solicitud no coincide con ningún dominio de la lista de dominios permitidos de la clave de API. |
Public API keys can only be used to generate JWT tokens or fetch widget branding. Please exchange your public key for a JWT token at /v1/auth/token, then use the token for API calls. |
Se usó una clave pública en una ruta distinta de /auth/token o /auth/branding. Intercámbiala primero por un JWT. |
Endpoint '{path}' not allowed for this API key |
La lista allowed_endpoints de la clave de API no incluye la ruta solicitada. |
Endpoint '{path}' not allowed for this token |
La lista allowed_endpoints del token JWT no incluye la ruta solicitada. |
Token issued for different origin |
El origen de la solicitud no coincide con el origen registrado en el JWT (evita el robo de tokens). |
Parent origin does not match token |
El encabezado X-Parent-Origin no coincide con lo validado al emitir el token. |
Restricciones de funciones del plan#
| Mensaje | Condición |
|---|---|
Async processing is not available on your current plan. Please upgrade to access this feature. |
async_mode=true en un plan sin acceso asíncrono. |
Webhook callbacks is not available on your current plan. Please upgrade to access this feature. |
Se proporcionó callback_url en un plan sin acceso a webhooks. |
ZIP output bundling is not available on your current plan. Please upgrade to access this feature. |
output_format=true en un plan sin acceso a salida ZIP. |
Basic authentication, cookies & custom headers is not available on your current plan. Please upgrade to access this feature. |
Se usó auth, cookies o headers en un plan sin acceso a autenticación básica. |
Batch processing is not available on your current plan. Please upgrade to access this feature. |
Se enviaron varias URLs en un plan con batch_limit de 0. |
Batch size {N} exceeds your plan's limit of {M} URLs per batch. |
El número de URLs supera el límite de tamaño de lote del plan. |
Website crawling is not available on your current plan. Please upgrade to access this feature. |
Se usó el endpoint de captura de sitios web en un plan con crawl_mode "none" (plan Founding). |
Full website crawling requires a Studio plan or higher. Your plan supports sitemap-based crawling only. |
Se solicitó crawl_mode=full en un plan Indie que solo admite rastreo basado en sitemap. |
Restricciones de widget#
| Mensaje | Condición |
|---|---|
Widget API key has been revoked |
La clave de API interna vinculada al widget fue desactivada. |
Domain {origin} is not authorized for this widget |
El dominio de inserción del widget no está en la lista de dominios permitidos del widget. |
Refresh token does not match widget |
El ID de proyecto del token de actualización no coincide con el proyecto del widget. |
Batch status requires a private API key |
Una clave pública o del panel intentó acceder a GET /v1/convert/batch/{batch_id}. |
Access denied |
Intento de acceder a un recurso (estado de trabajo, archivo) que pertenece a otro proyecto. |
Otros dos mensajes 403 no tratan ni de claves ni de planes: Account suspended, que se devuelve en todas las solicitudes cuando la cuenta asociada a la clave o al token está suspendida, y robots.txt disallows fetching this URL (request sent respect_robots=true)., que devuelve perceive cuando pediste cumplimiento de robots y el destino no permite esa ruta.
404 Not Found#
| Mensaje | Condición |
|---|---|
Job not found |
El ID del trabajo de conversión no se encontró en la base de datos (consulta de estado). |
Batch not found |
El ID de lote no tiene filas de actividad coincidentes para este proyecto. |
File not found |
El archivo solicitado no existe en el almacenamiento (endpoint de descarga). |
Widget not found |
El ID de widget no se encontró o el widget fue desactivado. |
Operation not found, Ingest job not found, Watcher not found |
Un id de recurso V2 que no existe, o que pertenece a otro proyecto. La existencia nunca se filtra entre proyectos. |
Not Found |
La ruta no corresponde a ningún endpoint de la API. Revisa la ruta y el prefijo de versión. |
409 Conflict#
| Mensaje | Condición |
|---|---|
job_id already in use |
Un job_id proporcionado por el cliente ya está reclamado por otro proyecto. Elige un id distinto, o deja que la API genere uno. |
A completion webhook is only delivered for completed jobs. |
Se solicitó un reintento de webhook para un trabajo de ingest que no ha alcanzado completed. |
410 Gone#
El artefacto existió, pero superó la ventana de retención de archivos de tu plan y ya no está en el almacenamiento. La retención es por plan; consulta Límites de tasa y cuotas.
| Mensaje | Condición |
|---|---|
The artifact is no longer in storage (it may have passed your plan's file-retention window). Re-run the perceive request to regenerate it. |
Descarga de artefacto en GET /v2/perceive/{operation_id}. |
The batch archive is no longer in storage (it may have passed your plan's file-retention window). |
Descarga de ZIP en GET /v2/perceive/batch/{job_id}. |
Trata el 410 como definitivo para ese objeto. Volver a ejecutar la solicitud produce un artefacto nuevo; reintentar la descarga, no.
413 Payload Too Large#
Se devuelve cuando el archivo subido supera el tamaño máximo de archivo del plan.
detail, no un objeto de nivel superior. Lee body.detail.max_size.
{
"detail": {
"error": "File too large",
"file_size": 10485760,
"max_size": 5242880,
"tier": "free",
"key_type": "private"
}
}
| Campo | Descripción |
|---|---|
error |
Siempre "File too large". |
file_size |
El tamaño del archivo subido en bytes. |
max_size |
El tamaño máximo de archivo permitido para tu plan, en bytes. |
tier |
El slug de tu plan de suscripción (por ejemplo, "free", "starter", "pro"), con "free" como valor de reserva cuando no se resuelve ningún plan. Los slugs son identificadores estables de la API; los nombres comerciales son Founding (free), Indie (starter), Studio (pro) y Production (business). |
key_type |
El tipo de clave de API usada: "private", "public" o "unknown". |
El límite se comprueba contra el número exacto de bytes de la parte subida, antes de que empiece cualquier trabajo de conversión. Un archivo cuyo tamaño sea exactamente max_size se acepta; solo se rechaza uno mayor. El encabezado Content-Length es un mecanismo de reserva para puntos de llamada antiguos que no entregan su objeto de subida a la comprobación.
POST /v2/ingest/files no usa esta forma. Responde 413 con un detail de cadena simple: File '{filename}' exceeds the {max_size}-byte limit.
Los techos por plan están en Límites de tasa y cuotas, y las rutas de subida a las que esto se aplica están en Ingesta de archivos.
Errores de conversión del navegador (415 / 422 / 502 / 504)#
Las conversiones de URL distinguen un fallo en el sitio de destino o en la entrada (un 4xx, 502 o 504 sobre el que puedes actuar) de un fallo en nuestro motor (un 500). Los tres endpoints V1 de conversión de URL (url-to-pdf, url-to-screenshot, url-to-markdown) devuelven estos fallos tipificados con un code legible por máquina junto a detail:
{
"error": "Gateway Timeout",
"code": "upstream_timeout",
"detail": "The target site took too long to respond while rendering PDF for https://example.com."
}
| Código | Campo code |
Condición |
|---|---|---|
415 |
unsupported_content_type |
El destino devolvió contenido que el conversor no puede renderizar, por ejemplo application/json enviado a url-to-pdf o url-to-screenshot. Usa url-to-markdown para JSON. |
422 |
selector_not_found |
Un wait_for_selector proporcionado por el llamante nunca apareció dentro de wait_for_selector_timeout. |
502 |
upstream_unreachable |
No se pudo alcanzar el sitio de destino (fallo de DNS o de conexión). |
502 |
empty_render |
La navegación finalizó pero la página no produjo contenido capturable. |
504 |
upstream_timeout |
El sitio de destino tardó demasiado en responder o en terminar de cargar. |
Esos cinco son todo el vocabulario. Ninguna otra familia de endpoints emite un code, V2 incluido: un fallo de V2 vuelve como una cadena detail simple. La clase base del envoltorio define un sexto slug, conversion_error, pero nada lo lanza, así que nunca te llega. Ramifica sobre los cinco anteriores y trata cualquier otro valor como desconocido.
Un 504 también puede llegar en dos formas sin tipificar: {"error": "Request timeout"} cuando la solicitud supera el presupuesto de 300 segundos del gateway, y un detail de cadena simple con el mensaje de tiempo de espera cuando una conversión de documento (LibreOffice) se agota. Ninguna de las dos lleva un code.
422 Unprocessable Entity#
Los fallos de validación de esquema devuelven dos arrays paralelos. detail es la salida sin procesar del validador, que es lo que reasignas a los campos del formulario. errors es una cadena legible por personas por cada problema, que es lo que muestras al usuario.
{
"detail": [
{
"loc": ["body", "max_pages"],
"msg": "Input should be a valid integer, unable to parse string as an integer",
"type": "int_parsing"
}
],
"errors": [
"body.max_pages: Input should be a valid integer, unable to parse string as an integer (you sent 'ten')"
]
}
Vale la pena tratar tres valores de type por su nombre:
type |
Significado |
|---|---|
extra_forbidden |
Campo desconocido. Los esquemas de solicitud V2 rechazan las claves desconocidas en lugar de ignorarlas, así que un parámetro mal escrito es un 422 que nombra el campo, y no una opción descartada en silencio. |
missing |
No se envió un campo obligatorio. |
json_invalid |
El cuerpo de la solicitud no era JSON válido. |
POST /v2/perceive/batch devuelve un 422 cuyo detail es una lista simple de objetos {"loc", "msg"}, sin clave type y sin array errors de nivel superior. Los analizadores que asumen que errors siempre está presente fallarán ahí.
Un 422 con el código selector_not_found es otra cosa: una precondición de renderizado que falló, tratada en Errores de conversión del navegador.
429 Too Many Requests#
La limitación de tasa es un control de equidad de ventana corta e independiente de la cuota mensual de ops. Agotar la cuota responde 402; solo el limitador de tasa responde 429.
| Mensaje | Condición |
|---|---|
Rate limit exceeded. Please slow down and retry shortly. |
Se superó una ventana de tasa de solicitudes del proyecto. Los cubos son por proyecto y están separados por tipo de clave, así que el tráfico público y el privado no comparten uno. |
Un 429 incluye cuatro encabezados:
| Encabezado | Significado |
|---|---|
RateLimit-Limit |
Solicitudes permitidas en la ventana que se superó. |
RateLimit-Remaining |
Solicitudes restantes en esa ventana, 0 en un rechazo. |
RateLimit-Reset |
Segundos hasta que la ventana se reinicia. |
Retry-After |
El mismo valor que RateLimit-Reset. Espera ese tiempo antes de reintentar. |
Estos encabezados aparecen solo en el 429. Las respuestas correctas no llevan encabezados de límite de tasa ni de ops restantes, así que no puedes leer tu presupuesto restante en una respuesta; revisa el uso en el panel. Consulta Límites de tasa y cuotas.
500 Internal Server Error#
| Mensaje | Condición |
|---|---|
Conversion failed: {error} |
Un error inesperado durante una conversión de archivo subido. Las conversiones de URL no usan este mensaje: aparecen como el envoltorio tipificado anterior, o como el cuerpo genérico siguiente. |
Perception failed. Reference operation_id '{id}' when contacting support. |
Un fallo inesperado dentro de una ejecución de /v2/perceive. Los demás endpoints V2 tienen equivalentes, como Distillation failed. Reference operation_id ... y Could not start ingest job. Reference job_id .... Cita el id cuando contactes con soporte. |
Todo lo que falla fuera de un conversor nunca te llega como texto. Vuelve sin ningún detail:
{
"error": "Internal server error",
"event_id": "a1b2c3d4"
}
Un caso sorprende a mucha gente: un cuerpo JSON malformado enviado a un endpoint V1 de URL (url-to-pdf, url-to-screenshot, url-to-markdown, website-to-pdf, website-to-screenshot) devuelve este 500 en lugar de un 422, porque esos endpoints leen el cuerpo sin procesar. El mismo cuerpo malformado en un endpoint V2 devuelve un 422 con type: json_invalid.
Si encuentras errores 500 persistentes, es probable que el problema esté en el archivo o la URL de entrada. Prueba con una entrada diferente para aislar el problema.
503 Service Unavailable#
| Mensaje | Condición |
|---|---|
Converter not available: {endpoint} |
El conversor solicitado no está registrado o no se está ejecutando. |
Converter not available |
El conversor basado en URL para el endpoint solicitado no está disponible. |
The conversion service is at capacity. Please retry shortly. |
El grupo de renderizado del navegador no tiene ninguna ranura libre. Se envía con Retry-After: 30. |
Server is at capacity. Please retry shortly. |
La compuerta de admisión de conversiones de CPU está llena: hay demasiadas conversiones de archivos, o demasiados bytes, en curso. Se envía con Retry-After: 10. |
Search is temporarily unavailable. Please try again later. |
El proveedor de búsqueda externo (upstream) detrás de lookup no está disponible o está mal configurado. |
Turnstile verification unavailable |
El servicio de verificación de Cloudflare Turnstile no está disponible. |
Existen dos compuertas de capacidad distintas y piden esperas diferentes, así que lee Retry-After en lugar de suponer una. Por lo demás, estos errores son transitorios: reintenta después de una breve espera.
Errores de los endpoints V2#
Los endpoints de inteligencia web V2 reutilizan los códigos de estado anteriores, con algunas condiciones específicas de V2 que vale la pena mencionar.
Cuota y plan (402 / 403)#
Las operaciones V2 se miden contra la misma cuota mensual unificada de ops que las conversiones V1: una op por unidad de trabajo. Cualquier plan de pago con excedente activado ($0.02/op) permite superar la asignación; si no, el tope es estricto.
| Código | Condición |
|---|---|
402 |
Se agotó la cuota mensual de ops. Todos los endpoints V2 (perceive, discover, lookup, distill, ingest) facturan este único contador junto con las conversiones V1. |
402 |
Se alcanzó el límite de watchers activos (max_watchers) (watch). Los watchers son un tope separado y nunca consumen ops. |
402 |
El endpoint está desactivado para el plan. Las restricciones V2 responden 402, a diferencia de las restricciones de funciones V1, que responden 403. |
403 |
El endpoint no está en la lista allowed_endpoints de la clave de API. |
Dos comportamientos de V2 deliberadamente no son errores. Agotar el saldo de créditos de IA no hace fallar la solicitud: la extracción de esquema recurre al resultado heurístico y CSS. Y una ejecución de distill con varias URLs que cruza el límite de ops a mitad de camino tampoco devuelve 402: se detiene ahí, devuelve las URLs que terminó, y añade una advertencia que indica cuántas se omitieron.
Validación (422)#
Todos los esquemas de solicitud V2 rechazan las claves desconocidas, así que un parámetro mal escrito es un 422 que nombra el campo. La forma del cuerpo se describe en 422 Unprocessable Entity.
| Endpoint | Condición |
|---|---|
| perceive | Se envió proxy_url, geolocation o action_chain; reservado para una versión posterior. |
| distill | No se proporcionó ni schema ni prompt (enviar ambos está bien, gana schema); no se proporcionó ninguno o se proporcionaron ambos de urls y discover_from; un campo CSS inválido, un tipo de campo no admitido, o una expresión regular que arriesga un backtracking catastrófico. |
| watch | frequency_minutes por debajo del mínimo horario de 60 minutos; un cuerpo PATCH vacío. |
| ingest | El mode no coincide con la fuente (modo urls sin urls, o sitemap/crawl sin una url semilla). |
Proveedor de búsqueda (502 / 503)#
El endpoint lookup depende de un proveedor de búsqueda externo (upstream). El texto de error sin procesar del proveedor nunca llega al cliente.
| Código | Mensaje | Condición |
|---|---|---|
502 |
The search provider returned an error. Please try again. |
El proveedor devolvió una respuesta de error o un fallo de transporte no reintentable. |
503 |
Search is temporarily unavailable. Please try again later. |
El proveedor está mal configurado (falta la clave) o no está disponible temporalmente. Reintenta más tarde. |
No encontrado (404)#
GET y DELETE sobre un operation_id, job_id o watcher_id de V2 que no existe, o que pertenece a otro proyecto, devuelven 404. La existencia nunca se filtra entre proyectos.
Aceptado (202)#
Ingest siempre es asíncrono: POST /v2/ingest responde 202 con un job_id que consultas mediante polling. Los lotes de perceive con más de 10 URLs responden 202 con estado queued. Un lote de 10 o menos normalmente responde en línea, pero si supera la ventana en línea degrada a 202 con estado processing y una advertencia, así que gestiona el 202 con cualquier tamaño de lote.
Un 202 también significa que los fallos posteriores no son errores HTTP. Consulta el trabajo y lee su carga de estado, tal como se describe en Trabajos síncronos y asíncronos.
Solución de problemas#
Problemas de autenticación#
- ¿Recibes un 401? Verifica que tu clave de API sea válida y esté activa en el panel. Si usas JWT, asegúrate de que el token no haya expirado (vida útil de 1 hora).
- ¿Recibes un 403 sobre uso desde el navegador? Estás usando una clave privada (
sk_...) desde código del lado del cliente. Cambia a una clave pública con JWT para solicitudes desde el navegador. - ¿Recibes un 403 sobre dominio? Agrega tu dominio a la lista de dominios permitidos de la clave de API en el panel.
Problemas de conversión#
- ¿Recibes un 400 sobre formato de archivo? Asegúrate de que la extensión del archivo subido coincida con el endpoint (por ejemplo,
.jsonpara json-to-xml,.docxpara doc-to-pdf). - ¿Recibes un 413? Tu archivo supera el límite de tamaño del plan. Lee
detail.max_sizede la respuesta, luego revisa el tamaño máximo de archivo de tu plan o actualízalo. - ¿Recibes un 402? Alcanzaste tu cuota mensual de ops, el tope de watchers o el límite de almacenamiento, o el proyecto no tiene periodo de facturación activo. Revisa el uso en el panel y consulta Límites de tasa y cuotas.
Problemas de acceso a funciones#
- ¿Recibes un 403 sobre funciones del plan? La función V1 que intentas usar (async, lotes, webhooks, salida ZIP, autenticación básica) requiere un nivel de plan superior. Consulta la tabla de habilitación de funciones.
- ¿Recibes un 402 en un endpoint V2 que no tiene que ver con la cuota? Las restricciones de endpoints V2 responden
402, no403. El mensaje dice... is not available on your current plan. Upgrade to a V2-inclusive plan to access this endpoint.
Preguntas frecuentes#
¿Por qué la API devuelve 402 Payment Required en una conversión de archivo?#
Un 402 significa que se agotó un límite de uso: tu cuota mensual unificada de ops (500 ops en el plan Founding), el tope de watchers activos, o la asignación de almacenamiento de tu proyecto. También cubre dos casos ajenos a la cuota: un proyecto sin periodo de facturación activo, y un endpoint V2 desactivado en tu plan. Las solicitudes por lotes que superarían la cuota mensual de ops restante se rechazan de antemano con un 402 para todo el lote. Las conversiones V1 y las operaciones V2 consumen la misma cuota; cualquier plan de pago con excedente activado ($0.02/op) permite superarla. La limitación de tasa es un mecanismo distinto y responde 429.
¿Cómo soluciono un error 401 Unauthorized de la API de conversión?#
Verifica que la clave de API esté presente, comience con sk_ o pk_, y siga activa en el panel; las claves revocadas devuelven API Key revoked. Si te autenticas con un JWT, ten en cuenta que los tokens de acceso expiran después de 1 hora (Token has expired) y los tokens de actualización después de 7 días.
¿Por qué recibo 413 Payload Too Large al subir un archivo?#
El archivo subido supera el tamaño máximo de archivo de tu plan, medido a partir del número exacto de bytes de la parte subida antes de que empiece cualquier trabajo de conversión. Un archivo justo en el límite se acepta. El cuerpo del 413 anida un objeto estructurado bajo detail, con file_size, max_size (ambos en bytes), tier y key_type, así que léelo como detail.max_size y no como un campo de nivel superior.
¿Puedo usar una clave de API privada desde JavaScript en el navegador?#
No: una clave privada (sk_...) usada en una solicitud con un encabezado Origin de navegador devuelve 403 Private API keys cannot be used from browsers. Intercambia una clave pública por un JWT en /v1/auth/token y usa ese token para las llamadas a la API desde el navegador.
¿Es permanente un error 503 Service Unavailable de la API?#
No, los errores 503 como Converter not available: {endpoint} o Turnstile verification unavailable suelen ser transitorios. Reintenta la solicitud después de una breve espera.