SOAPNoteAPI
Multiple10 min5 steps

Stream SOAP Notes with SSE

Enable Server-Sent Events to display each SOAP section as it is generated, delivering a real-time experience.

1

Enable streaming

Use the streaming endpoint POST /v1/stream/note instead of /v1/note. The request body is the same — the API responds with text/event-stream instead of JSON.
Terminal
curl -N -X POST https://api.soapnoteapi.com/v1/stream/note \
  -H "Authorization: Bearer snapi_sk_test_your_key_here" \
  -H "Content-Type: application/json" \
  -H "Accept: text/event-stream" \
  -d '{
    "transcript": "Patient is a 38-year-old female presenting for follow-up...",
    "specialty": "nurse_practitioner"
  }'
2

Parse SSE events in Python

Set stream=True on the requests call and iterate over lines. Each SSE event has a data: prefix containing a JSON object with section and content fields.
Python
import os
import json
import requests

api_key = os.environ["SOAPNOTEAPI_KEY"]

response = requests.post(
    "https://api.soapnoteapi.com/v1/stream/note",
    headers={
        "Authorization": f"Bearer {api_key}",
        "Content-Type": "application/json",
        "Accept": "text/event-stream",
    },
    json={
        "transcript": "Patient is a 38-year-old female presenting for follow-up...",
        "specialty": "nurse_practitioner",
    },
    stream=True,
)

for line in response.iter_lines(decode_unicode=True):
    if not line or not line.startswith("data: "):
        continue

    payload = line[len("data: "):]
    if payload == "[DONE]":
        print("\nStream complete.")
        break

    event = json.loads(payload)
    # event: { "section": "subjective", "content": "Patient is a..." }
    print(f"[{event['section']}] {event['content']}", end="", flush=True)
3

Parse SSE events in Node.js

Use the fetch response body as a readable stream. Decode chunks as UTF-8 text and split on newlines to extract SSE data lines.
JavaScript
const apiKey = process.env.SOAPNOTEAPI_KEY;

const response = await fetch("https://api.soapnoteapi.com/v1/stream/note", {
  method: "POST",
  headers: {
    "Authorization": `Bearer ${apiKey}`,
    "Content-Type": "application/json",
    "Accept": "text/event-stream",
  },
  body: JSON.stringify({
    transcript: "Patient is a 38-year-old female presenting for follow-up...",
    specialty: "nurse_practitioner",
  }),
});

const reader = response.body.getReader();
const decoder = new TextDecoder();
let buffer = "";

while (true) {
  const { done, value } = await reader.read();
  if (done) break;

  buffer += decoder.decode(value, { stream: true });
  const lines = buffer.split("\n");
  buffer = lines.pop() ?? "";

  for (const line of lines) {
    if (!line.startsWith("data: ")) continue;

    const payload = line.slice("data: ".length);
    if (payload === "[DONE]") {
      console.log("\nStream complete.");
      break;
    }

    const event = JSON.parse(payload);
    process.stdout.write(`[${event.section}] ${event.content}`);
  }
}
4

Parse SSE events in the browser

In a browser context you can use the Fetch API with a ReadableStream. The pattern is similar to the Node.js version, but you update the DOM instead of writing to stdout.
JavaScript
async function streamNote(transcript, specialty, onChunk) {
  const response = await fetch("https://api.soapnoteapi.com/v1/stream/note", {
    method: "POST",
    headers: {
      "Authorization": `Bearer ${apiKey}`,
      "Content-Type": "application/json",
      "Accept": "text/event-stream",
    },
    body: JSON.stringify({ transcript, specialty }),
  });

  const reader = response.body.getReader();
  const decoder = new TextDecoder();
  let buffer = "";

  while (true) {
    const { done, value } = await reader.read();
    if (done) break;

    buffer += decoder.decode(value, { stream: true });
    const lines = buffer.split("\n");
    buffer = lines.pop() ?? "";

    for (const line of lines) {
      if (!line.startsWith("data: ")) continue;
      const payload = line.slice("data: ".length);
      if (payload === "[DONE]") return;

      const event = JSON.parse(payload);
      onChunk(event.section, event.content);
    }
  }
}

// Usage
streamNote(transcript, "nurse_practitioner", (section, content) => {
  document.getElementById(section).textContent += content;
});
5

Build a real-time UI with React

Combine the streaming pattern with React state to progressively render each SOAP section as it arrives.
JavaScript
import { useState, useCallback } from "react";

function useSoapStream() {
  const [sections, setSections] = useState({
    subjective: "",
    objective: "",
    assessment: "",
    plan: "",
  });
  const [isStreaming, setIsStreaming] = useState(false);

  const generate = useCallback(async (transcript, specialty) => {
    setIsStreaming(true);
    setSections({ subjective: "", objective: "", assessment: "", plan: "" });

    const response = await fetch("https://api.soapnoteapi.com/v1/stream/note", {
      method: "POST",
      headers: {
        "Authorization": `Bearer ${apiKey}`,
        "Content-Type": "application/json",
        "Accept": "text/event-stream",
      },
      body: JSON.stringify({ transcript, specialty }),
    });

    const reader = response.body.getReader();
    const decoder = new TextDecoder();
    let buffer = "";

    while (true) {
      const { done, value } = await reader.read();
      if (done) break;

      buffer += decoder.decode(value, { stream: true });
      const lines = buffer.split("\n");
      buffer = lines.pop() ?? "";

      for (const line of lines) {
        if (!line.startsWith("data: ")) continue;
        const payload = line.slice("data: ".length);
        if (payload === "[DONE]") break;

        const { section, content } = JSON.parse(payload);
        setSections((prev) => ({
          ...prev,
          [section]: prev[section] + content,
        }));
      }
    }

    setIsStreaming(false);
  }, []);

  return { sections, isStreaming, generate };
}

// In your component:
// const { sections, isStreaming, generate } = useSoapStream();
// <p>{sections.subjective}</p>
// <p>{sections.objective}</p>
// ...

Ready to start building?

Get a free API key and $10 in credit — no credit card required.

Get your free API key