Status & streaming
SSE event vocabulary, Last-Event-ID resume, and legacy job polling.
Research runs as a single tuned sync flow — you usually just await the call. This page documents two extras: the SSE event vocabulary for live streaming (via stream: true or Accept: text/event-stream), and the legacy GET /v1/research/{job_id} polling endpoint kept for in-flight jobs created before the 2026-05-20 single-flow refactor.
Polling (legacy)
GET /v1/research/{job_id}
Authorization: Bearer sk-brime-...| Status code | Meaning |
|---|---|
200 OK | Terminal state (complete / errored / timeout). |
202 | Still running. |
404 | job_id unknown. |
The SDKs ship a polling helper with exponential backoff:
from brime import Brime
client = Brime()
final = client.research_status(job_id="rsh_...") # single fetch
# or, for long-running legacy jobs, the SDK's polling helper:
final = client.research_status(job_id="rsh_...", wait=True, poll_interval=10, poll_timeout=420)const final = await client.researchStatus({ jobId: "rsh_..." });
// or, for long-running legacy jobs:
const final2 = await client.researchStatus({ jobId: "rsh_...", wait: true, pollInterval: 10, pollTimeout: 420 });The interval grows with 1.5× exponential backoff up to a cap (default 30 s).
State machine
queued ──► running ──► complete
├──► errored
└──► timeout| State | Notes |
|---|---|
queued | Awaiting worker pickup. Usually under 1 s. |
running | Agent loop active; current_round increments. |
complete | Terminal success. answer, sources_count, steps_count populated. |
errored | Terminal failure. error.{code, message} populated. |
timeout | 10-minute alarm hit. Partial answer may be available. |
Streaming (SSE)
GET /v1/research/{job_id}/stream
Authorization: Bearer sk-brime-...
Accept: text/event-streamReturns text/event-stream framing.
Event vocabulary
event | When | data |
|---|---|---|
intent | Round 1 start | {query, follow_up} |
status | Human-readable progress hint | string (e.g., "Round 1: thinking…") |
tool_call | Agent chose to run web_search | {round, queries[]} |
tool_result | SERP + ranking returned | {round, result_count, top_titles[]} |
sources | Source list updated (after every round) | {sources: ResearchSource[]} |
final | Terminal answer (sync flow) | {answer, sources} |
done | Stream end | {} |
complete | Legacy-job terminal success | {answer, sources, steps, credits, latency_ms} |
error | Terminal failure | {code, message} |
timeout | 10-minute alarm hit | {} |
Frame example
event: tool_call
id: 12
data: {"round": 1, "queries": ["ECB October 2025 rate decision"]}
event: tool_result
id: 13
data: {"round": 1, "result_count": 9, "top_titles": ["2025 ECB Rate Decision — Reuters", ...]}
event: complete
id: 47
data: {
"answer": "...",
"sources": [...],
"credits_used": 5,
"latency_ms": 67213
}Resuming with Last-Event-ID
Disconnected mid-stream? Reconnect with the standard SSE header:
GET /v1/research/{job_id}/stream
Last-Event-ID: 13Brime keeps the last 200 events per job. The server replays everything after event 13, then continues live.
The Python and Node.js SDKs accept a last_event_id parameter:
for evt in client.research_stream(
query="...",
last_event_id="13",
):
...for await (const evt of client.researchStream({
query: "...",
last_event_id: "13",
})) {
// ...
}Choosing polling vs streaming
Pick polling when…
- You're running in a queue worker / cron / Lambda
- You want the simplest possible code
- Your client doesn't speak SSE (some HTTP libraries don't)
Pick streaming when…
- You're rendering progress to a user (web UI, terminal)
- You want minimal latency from agent step to display
- You need fine-grained tracing (LangSmith, OTel)
Both consume the same credits — the choice is purely UX.