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 solicitud | 1 | hasta 100 |
| Respuesta | Resultado completo en el cuerpo HTTP | Job ID; resultados se consultan aparte |
| Latencia | Una solicitud, una espera | Enviar → polling o Webhook → recoger |
| Mejor para | UX en tiempo real, herramientas para agentes, búsquedas ad-hoc | Jobs programados, ingestión RAG, monitorización |
| Modo de fallo | Una URL mala falla la llamada | Una URL mala falla su fila, el job continúa |
| Coste de concurrencia | Un slot por llamada | Un slot para todo el batch |
Ante la duda: una sola URL → sync; muchas URLs o sin prisa → async.
Estados del job
| Status | Significado |
|---|---|
PENDING | Job aceptado, en cola |
PROCESSING | Al menos una URL se está procesando |
COMPLETED | Todas las URLs alcanzaron un estado terminal (éxito o fallo) |
FAILED | Error fatal a nivel de job (raro — normalmente falla una URL, no el job entero) |
CANCELLED | Cancelació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 job | Recomendado | Por qué |
|---|---|---|
| < 10 URLs | Polling cada 5–10 s | El overhead del Webhook no compensa el cableado |
| 10–100 URLs | Webhook | El polling consume ~60 round-trips en un job de 5 minutos |
| > 100 URLs (varios batches) | Webhook | Cada 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.