Skip to content

Repository files navigation

gptweb2api

An OpenAI-compatible API gateway for ChatGPT Web with a browserless direct transport — no Chrome, no Playwright, no headless browser farm. One small Go binary serves chat, streaming, images, vision, files, and million-token contexts over plain HTTPS using your own ChatGPT account credentials.

OpenAI SDK/client ──► gptweb2api (:8788) ──► chatgpt.com backend-api
                        ~22 MB RSS            pure HTTP/2 + SSE

Highlights

  • Browserless by default — the gateway performs ChatGPT Web's requirements/preparation round trip in-process (bootstrap proof, SHA3-512 proof-of-work solving, conduit acquisition). Observed footprint: ~22 MB RSS for the whole gateway versus hundreds of MB for a Chrome tab.
  • OpenAI-compatible surface — 39 routes covering chat, responses, images, vision, files, uploads, conversations, stored completions, and batches.
  • Huge contexts — prompts beyond the Web request ceiling are serialized losslessly to JSONL, split into ≤8 MiB part files, uploaded through the Web file-service, and attached to one compact wire message. Qualified live at an estimated 1,000,000 tokens with 64/64 marker recall.
  • Images without a browser — generation, edits, and variations ride the same direct transport; vision and image attachments use image_asset_pointer parts, up to 10 attachments per turn.
  • Automatic fallback — if upstream bot-defense blocks the direct path, an optional local browser relay (Playwright + Chrome) takes over those turns.
  • Production hygiene — request IDs, JSON logs, /metrics, /healthz + /readyz, graceful shutdown, hot-reloadable OAuth credential files with automatic refresh.

Requirements

  • A ChatGPT account with an OAuth credential file. The gateway reads the same auth.json written by OpenAI Codex CLI (~/.codex/auth.json by default) and refreshes tokens automatically.
  • Go 1.24+ to build, or use the Dockerfile.
  • Optional: Node 18+ and Chrome only for the browser-relay fallback.

Quickstart

go build -o gptweb2api ./cmd/gptweb2api
cp .env.example .env          # set GPTWEB_API_KEY to your own secret
./gptweb2api

Any OpenAI SDK works — point base_url at http://127.0.0.1:8788/v1:

from openai import OpenAI
client = OpenAI(base_url="http://127.0.0.1:8788/v1", api_key="your-gptweb-key")
reply = client.chat.completions.create(
    model="auto",
    messages=[{"role": "user", "content": "Hello!"}],
)
print(reply.choices[0].message.content)

Docker

docker build -t gptweb2api .
docker run -p 8788:8788 -v ~/.codex:/root/.codex:ro \
  -e GPTWEB_API_KEY=change-me gptweb2api

API Reference

All routes require Authorization: Bearer $GPTWEB_API_KEY except /healthz. Errors follow the OpenAI error shape ({"error": {"message", "type", "param", "code"}}). Parameters that ChatGPT Web cannot honor (for example max_tokens, temperature) are rejected with unsupported_parameter rather than silently ignored.

System

Route Purpose
GET /healthz Process liveness; no auth
GET /readyz Readiness: auth + requirements provider (+ relay in browser mode)
GET /metrics Prometheus text metrics
curl http://127.0.0.1:8788/healthz
curl http://127.0.0.1:8788/readyz -H "Authorization: Bearer $KEY"
curl http://127.0.0.1:8788/metrics -H "Authorization: Bearer $KEY"

Models

Route Purpose
GET /v1/models Live catalog from the account
GET /v1/models/{model} One catalog entry
curl http://127.0.0.1:8788/v1/models -H "Authorization: Bearer $KEY"
{"object": "list", "data": [
  {"id": "auto", "object": "model", "owned_by": "chatgpt-web"},
  {"id": "gpt-5.6", "object": "model", "owned_by": "chatgpt-web"}
]}

Chat Completions

POST /v1/chat/completions

curl http://127.0.0.1:8788/v1/chat/completions \
  -H "Authorization: Bearer $KEY" -H "Content-Type: application/json" \
  -d '{
    "model": "auto",
    "stream": false,
    "messages": [
      {"role": "system", "content": "You are concise."},
      {"role": "user", "content": "Explain SSE in one paragraph."}
    ]
  }'

Streaming — standard OpenAI data: chunks ending with data: [DONE]:

curl -N http://127.0.0.1:8788/v1/chat/completions \
  -H "Authorization: Bearer $KEY" -H "Content-Type: application/json" \
  -d '{"model":"auto","stream":true,"messages":[{"role":"user","content":"Count to five."}]}'

With images (data URLs or public HTTPS URLs), up to 10 attachments:

{
  "model": "auto",
  "messages": [{
    "role": "user",
    "content": [
      {"type": "text", "text": "What is in this picture?"},
      {"type": "image_url", "image_url": {"url": "data:image/png;base64,..."}}
    ]
  }]
}

With documents (text-like MIME types ride the file-service automatically):

{
  "model": "auto",
  "messages": [{
    "role": "user",
    "content": [
      {"type": "text", "text": "Summarize this file."},
      {"type": "file", "file": {"filename": "notes.txt", "file_data": "data:text/plain;base64,..."}}
    ]
  }]
}

store: true persists the completion locally for later retrieval.

Stored completions

Route Purpose
GET /v1/chat/completions List stored completions
POST /v1/chat/completions/{completion_id} Update stored metadata
GET /v1/chat/completions/{completion_id} Retrieve one
DELETE /v1/chat/completions/{completion_id} Delete one
GET /v1/chat/completions/{completion_id}/messages Stored messages
curl http://127.0.0.1:8788/v1/chat/completions -H "Authorization: Bearer $KEY"
curl http://127.0.0.1:8788/v1/chat/completions/{completion_id}/messages -H "Authorization: Bearer $KEY"

Legacy Completions

POST /v1/completions

curl http://127.0.0.1:8788/v1/completions \
  -H "Authorization: Bearer $KEY" -H "Content-Type: application/json" \
  -d '{"model":"auto","prompt":"Say hi in three words"}'

Responses

POST /v1/responses

Stateful continuation via previous_response_id; text, image, and local input_file inputs; streaming supported.

curl http://127.0.0.1:8788/v1/responses \
  -H "Authorization: Bearer $KEY" -H "Content-Type: application/json" \
  -d '{"model":"auto","input":"Remember the codeword: LANTERN-7"}'

# continue the same thread
curl http://127.0.0.1:8788/v1/responses \
  -H "Authorization: Bearer $KEY" -H "Content-Type: application/json" \
  -d '{"model":"auto","previous_response_id":"resp_...","input":"What was the codeword?"}'

Input with a stored file:

{
  "model": "auto",
  "input": [{
    "role": "user",
    "content": [
      {"type": "input_text", "text": "Summarize"},
      {"type": "input_file", "file_id": "file_..."}
    ]
  }]
}

Stored responses

Route Purpose
GET /v1/responses/{response_id} Retrieve
DELETE /v1/responses/{response_id} Delete
GET /v1/responses/{response_id}/input_items Input items
GET /v1/responses/{response_id}/input_items/{item_id} One input item

Images

POST /v1/images/generations

curl http://127.0.0.1:8788/v1/images/generations \
  -H "Authorization: Bearer $KEY" -H "Content-Type: application/json" \
  -d '{"model":"auto","prompt":"a red circle on white background, flat vector","n":1}'
{"created": 1787..., "data": [{"b64_json": "iVBORw0KGgo..."}]}

POST /v1/images/edits (multipart)

curl http://127.0.0.1:8788/v1/images/edits \
  -H "Authorization: Bearer $KEY" \
  -F "prompt=Change the circle color from red to blue" \
  -F "image=@input.png;type=image/png"

Up to 10 input images, 20 MiB total. PNG/JPEG/GIF/WebP.

POST /v1/images/variations (multipart)

curl http://127.0.0.1:8788/v1/images/variations \
  -H "Authorization: Bearer $KEY" \
  -F "image=@input.png;type=image/png"

Vision

POST /v1/vision/analyze (multipart)

Convenience endpoint: one or more images plus a prompt, no data URLs needed.

curl http://127.0.0.1:8788/v1/vision/analyze \
  -H "Authorization: Bearer $KEY" \
  -F "prompt=What is in this image?" \
  -F "image=@photo.jpg;type=image/jpeg"

Files

Route Purpose
POST /v1/files Upload (multipart: purpose, file)
GET /v1/files List
GET /v1/files/{file_id} Metadata
GET /v1/files/{file_id}/content Download bytes
DELETE /v1/files/{file_id} Delete
curl http://127.0.0.1:8788/v1/files \
  -H "Authorization: Bearer $KEY" \
  -F "purpose=assistants" -F "file=@notes.txt;type=text/plain"

curl http://127.0.0.1:8788/v1/files/{file_id}/content \
  -H "Authorization: Bearer $KEY" -o notes.txt

Local storage only (.gptweb2api-files/) — files referenced by Responses input_file and large-context conversion.

Uploads (chunked, OpenAI Uploads API shape)

Route Purpose
POST /v1/uploads Create an upload session (multipart: purpose, filename, mime_type)
POST /v1/uploads/{upload_id}/parts Append a chunk (multipart: data)
POST /v1/uploads/{upload_id}/complete Finish (JSON: part_ids, optional md5)
POST /v1/uploads/{upload_id}/cancel Abort

Conversations

Route Purpose
POST /v1/conversations Create
POST /v1/conversations/{conversation_id} Update metadata
GET /v1/conversations/{conversation_id} Retrieve
DELETE /v1/conversations/{conversation_id} Delete
POST /v1/conversations/{conversation_id}/items Append items
GET /v1/conversations/{conversation_id}/items List items
GET /v1/conversations/{conversation_id}/items/{item_id} One item
DELETE /v1/conversations/{conversation_id}/items/{item_id} Remove item
curl http://127.0.0.1:8788/v1/conversations \
  -H "Authorization: Bearer $KEY" -H "Content-Type: application/json" -d '{}'

curl http://127.0.0.1:8788/v1/conversations/{conversation_id}/items \
  -H "Authorization: Bearer $KEY" -H "Content-Type: application/json" \
  -d '{"items":[{"type":"message","role":"user","content":[{"type":"input_text","text":"hi"}]}]}'

Batches

JSONL batches over the local Files store, processed sequentially.

Route Purpose
POST /v1/batches Create (JSON: input_file_id, endpoint, completion_window)
GET /v1/batches List
GET /v1/batches/{batch_id} Status
POST /v1/batches/{batch_id}/cancel Cancel
curl http://127.0.0.1:8788/v1/batches \
  -H "Authorization: Bearer $KEY" -H "Content-Type: application/json" \
  -d '{"input_file_id":"file_...","endpoint":"/v1/chat/completions","completion_window":"24h"}'

Configuration

All configuration comes from environment variables (.env is loaded automatically; see .env.example for the full annotated list).

Variable Default Meaning
GPTWEB_LISTEN 127.0.0.1:8787 Listen address
GPTWEB_API_KEY Bearer key clients must present
GPTWEB_OAUTH_TOKEN_FILE OAuth credential JSON (auth.json)
GPTWEB_TRANSPORT_MODE auto auto / direct / browser
GPTWEB_STREAM_MAX_HANDOFFS 64 Resumed-segment budget per turn
GPTWEB_TIMEOUT_SECONDS 180 Upstream connect/header timeout
GPTWEB_UPSTREAM_MAX_RETRIES 2 Transient 502/503/504 retries
GPTWEB_BROWSER_RELAY_URL Enable the browser-relay fallback
GPTWEB_FIDELITY_MODE api api adds a stateless output-only baseline prompt
GPTWEB_LANGUAGE / GPTWEB_TIMEZONE en-US / UTC Web client locale hints

How it works

A conversation turn performs the same short-lived integrity dance a real Web client performs — requirements fetch, proof-of-work solve, conduit preparation — and then streams the same v1 JSON-patch SSE the browser receives, including resumed segments. Long answers are segmented upstream; the gateway reconciles replays against a turn-wide emission ledger so output is delivered exactly once, in order, without truncation.

Oversized prompts travel as file attachments; generated images and uploads come back through the file-service. Full protocol notes, live qualification numbers, and debugging tools:

Debugging tools

Tool Purpose
cmd/gptwebprobe Sanitized live checks: transport, file upload, gateway end-to-end
cmd/gptweb2api-smoke Credential-safe operator smoke client
cmd/gptweb2api-context-smoke Deterministic large-context marker-recall qualification
GPTWEB_DEBUG_WIRE=1 Per-turn integrity and stream diagnostics

Project layout

cmd/gptweb2api                gateway entry point
cmd/gptweb2api-smoke          operator smoke client
cmd/gptweb2api-context-smoke  large-context qualification CLI
cmd/gptwebprobe               live transport probe
internal/chatgpt              Web transport: sentinel, SSE, uploads, images
internal/openai               OpenAI-compatible HTTP surface and stores
internal/config               environment configuration
tools/browser-relay           optional Playwright/Chrome fallback relay
docs/                         protocol notes, status, compatibility

Disclaimer

This project talks to ChatGPT Web's private, undocumented interface using your own account and is not affiliated with or endorsed by OpenAI. The private interface can change at any time; nothing here bypasses account limits or grants access you do not already have. Use it for personal automation at your own risk and in accordance with the terms that apply to your account.

License

MIT

About

No description, website, or topics provided.

Resources

Stars

4 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages