Guías

Ciclo de vida del job en lote

Sync vs async, estados del job, resultados parciales y Webhooks

Los endpoints de batch (/batch/distill, /batch/extract) son asíncronos. Un job atraviesa una pequeña máquina de estados; entender esos estados te ayuda a decidir entre polling y Webhooks, y a manejar fallos parciales.

Sync vs async

Sync (/distill, /extract)Async (/batch/distill, /batch/extract)
URLs por solicitud1hasta 100
RespuestaResultado completo en el cuerpo HTTPJob ID; resultados se consultan aparte
LatenciaUna solicitud, una esperaEnviar → polling o Webhook → recoger
Mejor paraUX en tiempo real, herramientas para agentes, búsquedas ad-hocJobs programados, ingestión RAG, monitorización
Modo de falloUna URL mala falla la llamadaUna URL mala falla su fila, el job continúa
Coste de concurrenciaUn slot por llamadaUn slot para todo el batch

Ante la duda: una sola URL → sync; muchas URLs o sin prisa → async.

Estados del job

StatusSignificado
PENDINGJob aceptado, en cola
PROCESSINGAl menos una URL se está procesando
COMPLETEDTodas las URLs alcanzaron un estado terminal (éxito o fallo)
FAILEDError fatal a nivel de job (raro — normalmente falla una URL, no el job entero)
CANCELLEDCancelación iniciada por el usuario vía DELETE

El fallo de una URL no hace fallar el job. Cada elemento en results[] lleva su propio status: PENDING, PROCESSING, SUCCEEDED o FAILED. El job pasa a COMPLETED cuando cada fila alcanza un estado terminal.

Polling vs Webhooks

Tamaño del jobRecomendadoPor qué
< 10 URLsPolling cada 5–10 sEl overhead del Webhook no compensa el cableado
10–100 URLsWebhookEl polling consume ~60 round-trips en un job de 5 minutos
> 100 URLs (varios batches)WebhookCada batch dispara una vez al completarse

Ver Webhooks para la forma del payload, verificación de firma (HMAC-SHA256) y comportamiento de reintento.

Resultados parciales

GET /batch/distill/{id} funciona mientras el job sigue en PROCESSING — obtienes lo que haya terminado hasta ahora. Útil para paneles que transmiten filas conforme se completan.

import httpx, time

API = "https://openapi.thunderbit.com/openapi/v1"
H = {"Authorization": "Bearer YOUR_API_KEY"}

job = httpx.post(f"{API}/batch/distill", headers=H,
                 json={"urls": urls}).json()
batch_id = job["data"]["id"]

while True:
    body = httpx.get(f"{API}/batch/distill/{batch_id}", headers=H).json()["data"]
    fresh = [r for r in body["results"] if r["status"] == "SUCCEEDED"]
    yield_to_dashboard(fresh)
    if body["status"] in ("COMPLETED", "FAILED", "CANCELLED"):
        break
    time.sleep(5)

Cancelación

DELETE /batch/distill/{id} (o /batch/extract/{id}) solo funciona en jobs en PENDING o PROCESSING. Una vez el job alcanza un estado terminal, ahí se queda. Las URLs ya procesadas en un job cancelado siguen siendo facturables; las URLs en vuelo que no habían terminado, no.