EU GPT logo
EU GPT

Public preview — This API is in public preview. Endpoints, schemas, and limits may change before general availability.

API

Handling streams

Parse SSE without the SDK — for custom clients, edge functions, and language bindings that don't exist yet.

If the official OpenAI SDK fits your stack, use it — it handles SSE parsing, reconnection edge cases, and typed events for free. This guide is for the cases where you need to roll your own: a Go service, a Cloudflare Worker, a Rust client, a server forwarding the stream to a browser.

The wire format#

event: message
data: {"type":"response.created","sequence_number":0, …}

event: message
data: {"type":"response.output_text.delta","sequence_number":1,"delta":"He"}

event: message
data: {"type":"response.output_text.delta","sequence_number":2,"delta":"llo"}

event: message
data: {"type":"response.completed","sequence_number":3, …}

Each event is one or more lines, separated by \n. Events are separated from each other by a blank line (\n\n). The data: line is JSON; everything else is metadata you can ignore.

Browser: fetch + ReadableStream#

The browser’s fetch returns a ReadableStream for text/event-stream responses. The EventSource API does not work here because it cannot send a body or headers — use fetch instead.

const response = await fetch("/v1/responses", {
  method: "POST",
  headers: {
    "Authorization": `Bearer ${apiKey}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({ input: "Tell me a story." }),
});

if (!response.ok) {
  throw new Error(`HTTP ${response.status}`);
}

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

while (true) {
  const { value, done } = await reader.read();
  if (done) break;
  buffer += decoder.decode(value, { stream: true });

  // SSE events are separated by blank lines.
  let idx;
  while ((idx = buffer.indexOf("\n\n")) !== -1) {
    const raw = buffer.slice(0, idx);
    buffer = buffer.slice(idx + 2);
    handleEvent(raw);
  }
}

function handleEvent(raw) {
  // Each event has lines like "event: message" and "data: {...}".
  for (const line of raw.split("\n")) {
    if (line.startsWith("data: ")) {
      const event = JSON.parse(line.slice(6));
      if (event.type === "response.output_text.delta") {
        renderDelta(event.delta);
      } else if (event.type === "response.completed") {
        renderDone();
      } else if (event.type === "error") {
        renderError(event.message);
      }
    }
  }
}

Node / server-side#

import { fetch } from "undici"; // or built-in node fetch on 18+

const response = await fetch("https://chat.eugpt.ai/v1/responses", {
  method: "POST",
  headers: {
    "Authorization": `Bearer ${process.env.EUGPT_API_KEY}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({ input: "Hi", stream: true }),
});

const decoder = new TextDecoder();
let buffer = "";

for await (const chunk of response.body) {
  buffer += decoder.decode(chunk, { stream: true });
  let idx;
  while ((idx = buffer.indexOf("\n\n")) !== -1) {
    const raw = buffer.slice(0, idx);
    buffer = buffer.slice(idx + 2);
    for (const line of raw.split("\n")) {
      if (line.startsWith("data: ")) {
        const event = JSON.parse(line.slice(6));
        // handle event
      }
    }
  }
}

Python without the SDK#

import json
import os
import httpx

def stream_response(input_text: str):
    with httpx.stream(
        "POST",
        "https://chat.eugpt.ai/v1/responses",
        headers={
            "Authorization": f"Bearer {os.environ['EUGPT_API_KEY']}",
            "Content-Type": "application/json",
        },
        json={"input": input_text, "stream": True},
        timeout=httpx.Timeout(connect=10, read=120, write=10, pool=10),
    ) as response:
        response.raise_for_status()
        buffer = ""
        for chunk in response.iter_text():
            buffer += chunk
            while "\n\n" in buffer:
                raw, buffer = buffer.split("\n\n", 1)
                for line in raw.splitlines():
                    if line.startswith("data: "):
                        yield json.loads(line[6:])

Use it as:

for event in stream_response("Tell me a haiku."):
    if event["type"] == "response.output_text.delta":
        print(event["delta"], end="", flush=True)

Forwarding to a browser#

If your server proxies EU GPT to a browser, the cleanest pattern is to not parse — just pipe the bytes through. The browser handles SSE parsing itself.

// Cloudflare Worker / edge function
const upstream = await fetch("https://chat.eugpt.ai/v1/responses", {
  method: "POST",
  headers: {
    "Authorization": `Bearer ${env.EUGPT_API_KEY}`,
    "Content-Type": "application/json",
  },
  body: await request.text(),
});

return new Response(upstream.body, {
  headers: {
    "Content-Type": "text/event-stream",
    "Cache-Control": "no-cache",
    "Connection": "keep-alive",
  },
});

Do not strip or rewrite events — the OpenAI SDK on the browser side expects the canonical shape.

Handling mid-stream errors#

A stream may close with an error event instead of response.completed. Always check the last event you received. A stream that ends after a delta but without a terminal event indicates a network failure, not a successful response — treat it as a retry candidate.

let terminal = null;
for await (const event of events) {
  if (event.type === "response.completed" || event.type === "error") {
    terminal = event;
  }
  // ... handle other events
}

if (!terminal) {
  throw new Error("Stream closed without terminal event");
}
if (terminal.type === "error") {
  throw new Error(`Stream errored: ${terminal.code} ${terminal.message}`);
}