Guias

Ciclo de Vida do Job em Batch

Sync vs async, estados do job, resultados parciais e webhooks

Os endpoints de batch (/batch/distill, /batch/extract) são assíncronos. Um job percorre uma pequena máquina de estados; entender os estados ajuda você a escolher entre polling e webhooks, e como tratar falhas parciais.

Sync vs async

Sync (/distill, /extract)Async (/batch/distill, /batch/extract)
URLs por requisição1até 100
RespostaResultado completo no corpo HTTPJob ID; resultados buscados separadamente
LatênciaUma requisição, uma esperaSubmeter → polling ou webhook → buscar
Melhor paraUX em tempo real, ferramentas para agentes, consultas pontuaisJobs agendados, ingestão RAG, monitoramento
Modo de falhaUma URL ruim falha a chamada inteiraUma URL ruim falha a sua linha, o job continua
Custo de concorrênciaUm slot por chamadaUm slot para o batch inteiro

Em caso de dúvida: uma URL → sync; muitas URLs ou sem pressa → async.

Estados do job

StatusSignificado
PENDINGJob aceito, na fila
PROCESSINGPelo menos uma URL está sendo processada
COMPLETEDTodas as URLs atingiram um estado terminal (sucesso ou falha)
FAILEDErro fatal em nível de job (raro — geralmente uma URL falha, não o job inteiro)
CANCELLEDCancelamento iniciado pelo usuário via DELETE

Uma falha de URL não falha o job. Cada item em results[] carrega seu próprio status: PENDING, PROCESSING, SUCCEEDED ou FAILED. O job vai para COMPLETED assim que toda linha atinge um estado terminal.

Polling vs webhooks

Tamanho do jobRecomendadoPor quê
< 10 URLsPolling a cada 5–10 sO overhead de webhook não compensa o setup
10–100 URLsWebhookPolling queima ~60 round-trips em um job de 5 minutos
> 100 URLs (vários batches)WebhookCada batch dispara uma vez na conclusão

Veja Webhooks para formato do payload, verificação de assinatura (HMAC-SHA256) e comportamento de retry.

Resultados parciais

GET /batch/distill/{id} funciona enquanto o job ainda está PROCESSING — você obtém o que já terminou. Útil para dashboards que streamam linhas conforme completam.

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)

Cancelamento

DELETE /batch/distill/{id} (ou /batch/extract/{id}) só funciona em jobs PENDING ou PROCESSING. Uma vez que o job atinge um estado terminal, ele permanece lá. URLs já processadas em um job cancelado continuam sendo cobradas; URLs em andamento que ainda não terminaram, não.