Skip to content
Documentation/Guides & tools

Read a verification stream

Keep complete events across network chunks and detect interrupted responses.

Markdown

Read event types

The response uses text/event-stream, with a JSON object in each data: frame. Events can include citation, groups, verdict, decomposition, sub-claim-start, sub-claim-result, metadata, and result. Treat result as the final structured verification payload.

A separate usage event carries operation_id and usage.credits. done marks successful completion. error reports a verification failure; accounting_error reports a billing failure. Do not turn an interrupted stream into a successful result.

Keep partial frames

A network chunk can end in the middle of a UTF-8 character, a JSON value, or a frame separator. Retain incomplete data until the next read. This example accepts the API's LF and CRLF framing, fails on error events, and limits buffered data to 8 MiB. Increase that client limit only when your expected results require it.

javascript
// This function is also published verbatim in the streaming guide.
async function* readEvents(response) {
  if (!response.ok)
    throw new Error(`HTTP ${response.status}: ${await response.text()}`);
  if (!response.body) throw new Error("Response body is unavailable");
  const reader = response.body.getReader();
  const decoder = new TextDecoder();
  let buffer = "";
  let completed = false;
  try {
    while (true) {
      const { value, done } = await reader.read();
      buffer += decoder.decode(value, { stream: !done });
      if (buffer.length > 8 * 1024 * 1024)
        throw new Error("SSE frame exceeds the client limit");
      let boundary;
      while ((boundary = /\r?\n\r?\n/.exec(buffer))) {
        const frame = buffer.slice(0, boundary.index);
        buffer = buffer.slice(boundary.index + boundary[0].length);
        const data = frame
          .split(/\r?\n/)
          .filter((line) => line.startsWith("data:"))
          .map((line) => line.slice(5).replace(/^ /, ""))
          .join("\n");
        if (!data) continue;
        const event = JSON.parse(data);
        if (event.type === "error" || event.type === "accounting_error") {
          throw new Error(event.message || event.type);
        }
        yield event;
        if (event.type === "done") {
          completed = true;
          return;
        }
      }
      if (done) break;
    }
    if (!completed) throw new Error("Stream ended without a done event");
  } finally {
    await reader.cancel();
    reader.releaseLock();
  }
}

Consume the stream

Call this from your server with a key in its environment. Set a timeout appropriate to your request.

javascript
const response = await fetch("https://api.webcite.co/api/v1/verify/stream", {
  method: "POST",
  headers: {
    "x-api-key": process.env.WEBCITE_API_KEY,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({ claim: "The Eiffel Tower is 330 meters tall" }),
  signal: AbortSignal.timeout(300_000),
});
for await (const event of readEvents(response)) {
  console.log(event);
}