n4nAI

Testing streaming LLM responses in Insomnia

Learn how to test streaming responses in Insomnia for LLM APIs using SSE, with step-by-step request setup, validation scripts, and CLI automation.

n4n Team3 min read708 words

Audio narration

Coming soon — every post will get a voice note here.

When you need to test streaming responses insomnia is a practical choice because it renders Server-Sent Events as they arrive and lets you script assertions against the aggregated payload. This guide walks through configuring an OpenAI-compatible chat completion request with stream: true, validating the SSE chunks inside Insomnia’s test runner, and running the same checks from the Inso CLI so they fit a CI pipeline.

Prerequisites

You need Insomnia (desktop or CLI via Inso) 2023.5 or later, a valid API key for an OpenAI-compatible gateway, and a workspace with at least one environment. No local server is required. If you haven’t already, create an environment named Default with base_url and api_key variables:

{
  "base_url": "https://api.openai.com/v1",
  "api_key": "sk-..."
}

Swap base_url to your own gateway. For example, n4n.ai exposes a single OpenAI-compatible endpoint that fronts 240+ models and applies automatic fallback when a provider is rate-limited or degraded, so you can keep one base URL and change only the model field.

Step 1: Configure the streaming request

Create a new POST request named “Stream Chat”. Set the URL to {{ base_url }}/chat/completions. Add headers:

{
  "Authorization": "Bearer {{ api_key }}",
  "Content-Type": "application/json"
}

Use this body to force token streaming:

{
  "model": "gpt-4o-mini",
  "messages": [
    {"role": "user", "content": "Count to three slowly."}
  ],
  "stream": true,
  "temperature": 0
}

Save. Insomnia tags the response as “Event Stream” once it sees text/event-stream. If you test streaming responses insomnia against multiple models, replace the hard-coded model with {{ model }} and define that variable per environment.

Step 2: Send and watch the stream

Click Send. The response pane opens a live view. Each data: frame appends as it arrives. A correct stream looks like:

data: {"choices":[{"delta":{"role":"assistant"}}]}

data: {"choices":[{"delta":{"content":"One"}}]}

data: {"choices":[{"delta":{"content":" Two"}}]}

data: [DONE]

Check the Headers subtab: Content-Type must be text/event-stream, and Transfer-Encoding should be chunked. If you receive a single JSON object with choices[0].message.content, the server ignored stream. To test streaming responses insomnia reliably, confirm manually that chunks appear incrementally and the [DONE] frame closes the connection.

Step 3: Write a basic test suite

Insomnia’s Tests tab runs Chai assertions after the response finishes. The global response object holds the raw stream text. Start with smoke checks:

const raw = response.body.toString();
expect(raw, 'must be event-stream').to.include('data:');
expect(raw, 'must terminate').to.include('[DONE]');
expect(response.status, 'status 200').to.equal(200);

Run the request. The sidebar shows pass/fail. A green check means the stream opened, emitted events, and closed properly.

Step 4: Parse SSE frames and assert on deltas

The raw body concatenates frames with blank lines. Split on \n\n or \r\n\r\n, strip the data: prefix, and skip [DONE]:

const raw = response.body.toString();
const frames = raw
  .split(/\r?\n\r?\n/)
  .map(s => s.trim())
  .filter(s => s.startsWith('data:'))
  .map(s => s.replace(/^data:\s*/, ''))
  .filter(s => s !== '[DONE]');

expect(frames.length, 'at least one delta').to.be.greaterThan(0);

const parsed = frames.map(f => JSON.parse(f));
const text = parsed
  .map(j => j.choices?.[0]?.delta?.content || '')
  .join('');

expect(text, 'reply mentions numbers').to.match(/One|Two|Three/i);

This catches providers that silently batch the whole reply into one frame (still technically SSE but defeats the purpose). When you test streaming responses insomnia with this parser, you enforce true incremental delivery.

Step 5: Verify usage metering and routing hints

Add stream_options to request body to get token counts in the final frame:

{
  "model": "gpt-4o-mini",
  "messages": [{"role": "user", "content": "Hi"}],
  "stream": true,
  "stream_options": {"include_usage": true}
}

Assert the usage object exists:

const last = parsed[parsed.length - 1];
expect(last, 'last frame has usage').to.have.property('usage');
expect(last.usage.completion_tokens).to.be.a('number');

Gateways may also echo cache directives. n4n.ai honors client routing directives and forwards provider cache-control hints, so checking x-cache is meaningful there. In Insomnia tests:

const cache = response.headers['x-cache'];
if (cache) {
  expect(cache, 'cache hint valid').to.match(/HIT|MISS|SKIP/);
}

Step 6: Automate with Inso CLI

GUI runs don’t guard main. Install Inso and run the suite headlessly:

npm install -g inso
inso run test "Stream Chat" --env "Default"

Inso executes the same JavaScript against the streamed body. Drop it into CI:

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - run: npx inso run test "Stream Chat" --env "CI"

A non-streaming mock or a broken proxy fails the [DONE] assertion, blocking merge.

Step 7: Handle fallback and partial streams

Networks drop. Write a test that fails on incomplete streams:

const done = frames.some(f => f === '[DONE]');
expect(done, 'stream closed cleanly').to.equal(true);

If your gateway does automatic fallback mid-stream, the chunk sequence may resume from a backup provider. Your parser already tolerates arbitrary frame counts. Keep the system_fingerprint stable across frames when present:

const fps = parsed.map(j => j.system_fingerprint).filter(Boolean);
if (fps.length) {
  expect(new Set(fps).size, 'single fingerprint').to.equal(1);
}

To test streaming responses insomnia across models, loop environments:

for m in gpt-4o-mini claude-3-haiku; do
  inso run test "Stream Chat" --env "$m"
done

Step 8: Debug timing and ordering

Insomnia’s Timeline tab stamps each chunk. If you suspect reordering, log delta indices:

parsed.forEach((j, i) => {
  if (j.choices?.[0]?.delta?.content) {
    console.log(i, j.choices[0].delta.content);
  }
});

Run from Inso with --verbose to see console output. This surfaces proxies that buffer SSE.

Troubleshooting

  • No incremental render: Ensure stream: true is at top level, not inside messages.
  • Tests can’t see response.body: Insomnia provides it only after completion; streamed bodies are fully buffered for scripts.
  • CORS errors: Insomnia bypasses browser CORS; if you see them, it’s a proxy issue, not the client.

Verify success

Your verification loop is complete when:

  1. The Insomnia Event Stream pane fills chunk by chunk, ending with data: [DONE].
  2. The Tests tab is green for frame count, content regex, usage, and termination.
  3. inso run test exits zero in terminal and CI.
  4. Simulating a kill (e.g., tcpkill) makes the incomplete-stream test fail.

Following these steps lets you test streaming responses insomnia without writing a custom WebSocket or fetch client for every regression. The SSE contract is simple; the valuable checks are delta granularity, clean termination, and stable metadata across fallback.

Tagsinsomniastreamingtestingsse

Written by

n4n Team

The team building n4n — a single OpenAI-compatible API in front of 240+ models, with automatic fallback, load balancing and pay-per-token metering.

More from n4n Team →

All postman & insomnia llm api testing posts →