brime

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 codeMeaning
200 OKTerminal state (complete / errored / timeout).
202Still running.
404job_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
StateNotes
queuedAwaiting worker pickup. Usually under 1 s.
runningAgent loop active; current_round increments.
completeTerminal success. answer, sources_count, steps_count populated.
erroredTerminal failure. error.{code, message} populated.
timeout10-minute alarm hit. Partial answer may be available.

Streaming (SSE)

GET /v1/research/{job_id}/stream
Authorization: Bearer sk-brime-...
Accept: text/event-stream

Returns text/event-stream framing.

Event vocabulary

eventWhendata
intentRound 1 start{query, follow_up}
statusHuman-readable progress hintstring (e.g., "Round 1: thinking…")
tool_callAgent chose to run web_search{round, queries[]}
tool_resultSERP + ranking returned{round, result_count, top_titles[]}
sourcesSource list updated (after every round){sources: ResearchSource[]}
finalTerminal answer (sync flow){answer, sources}
doneStream end{}
completeLegacy-job terminal success{answer, sources, steps, credits, latency_ms}
errorTerminal failure{code, message}
timeout10-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: 13

Brime 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

check

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)
bolt

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.