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}`);
}