# Flask SSE for the Responses API
Flask routes are sync, which is actually convenient: the sync OpenAI client's
`stream=True` iterator works directly in a generator. The traps are context and
worker starvation.
## Route shape
- `def gen():` yields SSE frames: `data: ` + json.dumps({"delta": event.delta}) +
two newlines for each `response.output_text.delta` event, and returns on
`response.completed`.
- Return `Response(stream_with_context(gen()), mimetype="text/event-stream")`.
`stream_with_context` keeps the request context alive while the generator runs;
without it, accessing `request` inside the generator raises outside-context errors.
- Headers: `Cache-Control: no-cache`, `X-Accel-Buffering: no`.
## Worker math (the one people miss)
- With gunicorn sync workers, one open SSE stream holds one worker for the whole
session. Four workers means the fifth concurrent user waits. Use gevent or
eventlet workers for SSE routes, or run a separate gunicorn with more workers
for the streaming blueprint.
- Create the OpenAI client once at app startup, not per request. The sync client
is safe to share across threads; per-request clients just add TLS handshake cost.
## Check before you ship
- Open two browser tabs and confirm both streams progress at once (catches the
single-worker stall).
- Kill a client mid-stream and confirm the generator exits instead of running to
`response.completed` in the background, burning tokens.