← Blog

Claude Code Streaming Output: Capturing It in Scripts

2026-09-14 · 5 min read · SubToAPI Team

When people search "claude code streaming output" they're usually hitting one of two walls: either the terminal output from Claude Code scrolls by too fast to read or log properly, or they're trying to pipe Claude Code into a script and getting garbled text instead of structured data. Both problems have the same root cause — Claude Code's default streaming behavior is built for a human watching a terminal, not for a program consuming output programmatically.

This article covers how streaming actually works in Claude Code, how to capture it cleanly for scripts and CI pipelines, and what to do when you need streaming output inside your own product rather than a terminal.

How Claude Code Streams by Default

In interactive mode, Claude Code prints tokens to stdout as they arrive from the model. This is what gives you the "typing" effect — text appears incrementally instead of waiting for the full response. It's useful for reading along, but it's a raw text stream mixed with terminal control characters (for things like the spinner, tool-call indicators, and diff rendering). If you redirect that output to a file with >, you'll often get a mess of ANSI escape codes instead of clean text.

That's expected — the interactive UI was never meant to be machine-readable.

Capturing Streaming Output Cleanly

For scripting, use Claude Code's non-interactive --print mode combined with an explicit output format:

claude --print --output-format stream-json "Refactor this function for readability" < input.js

stream-json emits newline-delimited JSON events instead of raw terminal text. Each line is a discrete event — message start, content deltas, tool calls, tool results, and message completion. This is the format to reach for whenever you want to:

A minimal consumer in Node.js might look like this:

import { spawn } from "child_process";
import readline from "readline";

const proc = spawn("claude", [
  "--print",
  "--output-format", "stream-json",
  "Summarize the diff in this repo",
]);

const rl = readline.createInterface({ input: proc.stdout });

rl.on("line", (line) => {
  if (!line.trim()) return;
  const event = JSON.parse(line);
  if (event.type === "content_block_delta") {
    process.stdout.write(event.delta.text ?? "");
  }
});

This gives you the same incremental output the terminal shows, but as structured events you can filter, log, or forward — without ANSI escape codes getting in the way.

Why stream-json Beats Parsing Raw Text

Trying to regex your way through Claude Code's plain-text streaming output is fragile. Tool-call markers, thinking blocks, and final answers all share the same stdout stream, and formatting can change between versions. stream-json avoids that by giving each event a type field you can branch on:

If your script only cares about the final text, buffer the deltas and discard everything else. If you're building a monitoring dashboard for CI runs, log every event with a timestamp — you'll get a full trace of what the agent did and when.

Backpressure and Timeouts

Long-running Claude Code sessions (large refactors, multi-file edits) can stream for minutes. Two practical issues come up:

  1. Silent stalls. If no events arrive for a while, don't assume the process hung — some tool calls (running tests, installing dependencies) legitimately take time. Set a generous idle timeout rather than a fixed total timeout.
  2. Stdout buffering. If you pipe through another program (claude ... | tee log.txt), make sure that program doesn't buffer full lines before flushing, or you'll lose the "streaming" benefit entirely and just get a big dump at the end.

When You Need Streaming Output in a Product, Not a Terminal

Capturing Claude Code's stream for scripts and CI is one use case. It's a different problem when you're building an actual product feature — a chat UI, a support tool, an internal app — where you need reliable, low-latency streaming responses served over HTTP to a frontend, with authentication, usage tracking, and team access baked in.

Claude Code's CLI streaming isn't designed for that. For product-facing streaming, you want a proper HTTP API with server-sent events, which is what SubToAPI provides on top of your existing Claude access. Instead of spawning a CLI process and parsing stdout, you call a REST endpoint and consume SSE chunks directly:

curl https://api.subtoapi.app/v1/messages \
  -H "Authorization: Bearer $SUBTOAPI_KEY" \
  -H "content-type: application/json" \
  -d '{
    "model": "claude-sonnet-4",
    "stream": true,
    "messages": [{"role": "user", "content": "Explain event loop backpressure"}]
  }'

Or from JavaScript, reading the stream incrementally in a browser or server context:

const res = await fetch("https://api.subtoapi.app/v1/messages", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.SUBTOAPI_KEY}`,
    "content-type": "application/json",
  },
  body: JSON.stringify({
    model: "claude-sonnet-4",
    stream: true,
    messages: [{ role: "user", content: "Draft a release note" }],
  }),
});

const reader = res.body.getReader();
const decoder = new TextDecoder();

while (true) {
  const { done, value } = await reader.read();
  if (done) break;
  process.stdout.write(decoder.decode(value));
}

This gets you the same token-by-token responsiveness you're used to from Claude Code's terminal, but with application API keys (sub_live_...), per-request usage metadata, and multi-seat team access instead of a local CLI session. See the streaming docs and messages reference for event formats, or the quickstart to get an API key. Plans start at Solo for solo builders and scale up through Team and Scale tiers — full details on pricing.

Questions

Does Claude Code stream output by default? Yes, in interactive terminal mode Claude Code prints tokens as they arrive. In --print non-interactive mode you get the full response at once unless you also set --output-format stream-json.

How do I get structured streaming output instead of raw text? Use claude --print --output-format stream-json, which emits newline-delimited JSON events you can parse per line instead of dealing with raw terminal text and ANSI codes.

Can I use Claude Code's streaming for a production app? Not directly — it's a CLI tool. For product-facing streaming with authentication and usage tracking, use an HTTP API like SubToAPI that serves SSE streams over standard REST endpoints.

Turn your Claude access into an HTTPS API

SubToAPI gives you application API keys, streaming, tool use and usage insights on top of your existing Claude access — set up in minutes.

Start free  Read the quickstart →