ChatGPT-Web2API
Health Warn
- License — License: MIT
- Description — Repository has a description
- Active repo — Last push 0 days ago
- Low visibility — Only 5 GitHub stars
Code Warn
- network request — Outbound network request in scripts/a2_capture_real_fixture.py
- network request — Outbound network request in scripts/a2_investigation_spike.py
- network request — Outbound network request in scripts/a2_phase0_5_and_1_combined.py
- network request — Outbound network request in scripts/a2_phase1_id_survival.py
- network request — Outbound network request in scripts/a2_stress4_clean.py
Permissions Pass
- Permissions — No dangerous permissions requested
No AI report is available for this listing yet.
OpenAI-compatible API proxy for ChatGPT web — expose ChatGPT as an MCP server, REST API, and OpenAI SDK endpoint via CDP-driven browser automation

ChatGPT-Web2API
Turn ChatGPT into an API. No API key. No token extraction. No sentinel solving.
One command starts a Chrome browser, logs into ChatGPT, and exposes an OpenAI-compatible API + MCP server.
Why This Exists
You have a ChatGPT Plus subscription but want to use it programmatically:
- Build AI agents that chat with ChatGPT via MCP (Model Context Protocol)
- Use the OpenAI Python SDK against your ChatGPT account — no separate API key
- Access ChatGPT Projects for persistent memory and custom instructions
- Manage memories, conversations, and projects from code
Other reverse proxies require token extraction, sentinel challenge solving, or cookie management. This one drives a real Chrome browser — anti-bot challenges (Turnstile, PoW) are handled automatically.
Demo
$ pip install chatgpt-web2api
$ chatgpt-web2api
✓ Chrome launched on port 9222
✓ Navigating to chatgpt.com...
✓ Ready on http://localhost:8080
$ curl -s http://localhost:8080/v1/chat/completions \
-H "Content-Type: application/json" \
-d '{"model":"auto","messages":[{"role":"user","content":"What is 8+7?"}]}'
{"choices":[{"message":{"content":"8 + 7 = 15"},"finish_reason":"stop"}]}
# OpenAI Python SDK — drop-in replacement
from openai import OpenAI
client = OpenAI(base_url="http://localhost:8080/v1", api_key="not-needed")
print(client.chat.completions.create(
model="auto",
messages=[{"role": "user", "content": "What is 8+7?"}]
).choices[0].message.content)
# → "8 + 7 = 15"
How It Works
┌──────────────┐ OpenAI API ┌──────────────┐ CDP ┌──────────────┐
│ Your code │ ──────────────► │ API Server │ ────────► │ Chrome │
│ SDK / curl │ ◄────────────── │ or MCP │ ◄──────── │ chatgpt.com │
│ MCP client │ JSON / SSE │ Server │ events │ (logged in) │
└──────────────┘ └──────────────┘ └──────────────┘
The proxy types messages, clicks send, and reads responses — exactly like a human. The browser handles all anti-bot challenges transparently.
Quick Start
pip install chatgpt-web2api
# Start — opens Chrome, waits for login on first run
chatgpt-web2api
# Then use it:
curl http://localhost:8080/v1/chat/completions \
-H "Content-Type: application/json" \
-d '{"model":"auto","messages":[{"role":"user","content":"What is 2+2?"}]}'
# → {"choices":[{"message":{"content":"4"}}]}
That's it. One install, one command, one endpoint.
What You Get
🌐 OpenAI-Compatible REST API
Drop-in replacement for api.openai.com:
from openai import OpenAI
client = OpenAI(base_url="http://localhost:8080/v1", api_key="not-needed")
response = client.chat.completions.create(
model="auto",
messages=[{"role": "user", "content": "Hello!"}],
stream=True,
)
for chunk in response:
print(chunk.choices[0].delta.content or "", end="")
⏱️ Rate Limits & Agent Retry
ChatGPT occasionally shows a "Too many requests" pop-up when conversations are
accessed rapidly. ChatGPT-Web2API turns this from a silent failure into a
practical, agent-friendly signal — so autonomous workflows back off and
recover instead of breaking.
Transparent by default. When the pop-up appears, the server dismisses it
(clicks "Got it") and retries your request with exponential backoff — up to
3 attempts. A transient limit is invisible: your agent gets a normal
successful response and never knows it happened.
A clean pause signal when it persists. If the limit survives all retries,
the server surfaces a standard OpenAI 429 that every major agent framework
already knows how to handle — with zero client-side integration:
HTTP/1.1 429 Too Many Requests
Retry-After: 60
{"error": {"type": "rate_limit_exceeded", "code": "rate_limit_exceeded", ...}}
The OpenAI SDK, LangChain, LlamaIndex, and similar frameworks auto-retry on429 + Retry-After by default. The wait (Retry-After) is parsed from the
pop-up text when ChatGPT gives an exact number, otherwise a conservative 60s.
MCP clients (Claude Desktop, Cursor) get the same signal semantically: aCallToolResult with isError=True and a machine-readable structuredContent
payload an agent can read to decide "pause, then retry this tool":
{"rate_limited": true, "retry_after": 60, "error": "rate_limit_exceeded"}
Streaming caveat: a 429 can be sent before streaming begins (a pre-flight
check), but a limit that appears mid-stream (after the 200 is committed) is
surfaced as an inline[Error: rate_limit_exceeded — retry in Ns]chunk,
since the HTTP status can no longer change. Non-streaming requests are
unaffected.
🩺 Troubleshooting API/UI Drift
ChatGPT changes its web API over time, which can silently break driver functions
(the unit-test mocks can't detect this). ChatGPT-Web2API ships a reactive
diagnostic that captures evidence at the moment a function breaks, plus adoctor command that auto-discovers the breakage and prints the evidence for
fast repair.
Enable capture (off by default) so breakage in the wild is recorded:
W2A_DIAGNOSE=1 chatgpt-web2api start
When a function returns a broken shape, a redacted artifact is written to~/.chatgpt-web2api/diagnostics/<function>-<timestamp>-<seq>.json with the
exact request, live response, expected-vs-actual mismatch (secrets stripped).
Auto-discover + diagnose:
chatgpt-web2api doctor # which functions have captured breakage?
chatgpt-web2api doctor create_project# print the captured evidence for one
chatgpt-web2api doctor --verify get_models # re-run a read function live to test a fix
doctor (no args) auto-discovers which functions are broken — no human
needs to name them. It then prints the evidence an AI coding agent (or you)
reads to propose the corrected payload/selector/parse. --verify confirms a
read-path fix against the live account before you commit it. No AI is bundled —
the project captures deterministic evidence; the fix is human-applied.
🤖 MCP Server (16 Tools for AI Agents)
Expose ChatGPT to Claude Desktop, Cursor, Craft Agent, or any MCP client. Two
transports are supported — SSE is recommended for persistent clients like
ZCode; stdio is kept as a compatibility/dev mode.
SSE transport (persistent server; single active session until B1)
SSE is useful for one long-lived MCP server, but current master does not yet
provide per-client tab/session isolation inside one SSE process. Multiple MCP
clients may attach to the same SSE endpoint, but they share one MCP server, oneCDPDriver, one Chrome tab, and one browser conversation surface. Running
parallel agent sessions through the same SSE server can cause cross-session
interference: one session may navigate, continue, or mutate the browser state
expected by another.
Until the B1 MCP session-affine tab pool is implemented and enabled, use SSE
for a single active agent session at a time. For parallel agent sessions
today, use separate MCP processes with parallel_tabs=true andtab_mode=owned, and ensure the Chrome-owning chatgpt-web2api REST process
is started with the same parallel config or is not serving REST traffic on
that CDP port. Otherwise, wait for B1.
Start it (in a dedicated terminal — Chrome must already be running with an
authenticated session, i.e. chatgpt-web2api started first):
chatgpt-web2api-mcp --transport sse --port 8090
Then add the client config:
{
"mcpServers": {
"chatgpt-web2api-sse": {
"type": "sse",
"url": "http://localhost:8090/sse"
}
}
}
Every client session that attaches to that URL currently shares the single MCP
server and Chrome connection. This avoids per-session process growth, but it is
not safe for parallel independent agent sessions until B1's session-affine
driver/tab pool is available and explicitly enabled.
Multi-session warning: A2 prevents stale-return by anchoring responses to
the submitted turn, but it does not isolate multiple MCP SSE clients from each
other. Shared SSE remains a single browser-control surface today.
Alternative: stdio transport (per-session, dev/debug)
Stdio spawns one MCP child process per client session, so it is not
recommended for many concurrent ZCode sessions. It is useful for single-client
setups, local development, and debugging (logs go straight to stderr):
{
"mcpServers": {
"chatgpt": {
"command": "chatgpt-web2api-mcp",
"args": ["--cdp-port", "9222"]
}
}
}
16 tools give the agent full control:
| Tool | What it does |
|---|---|
chat_completion |
Send message, get response (multi-turn, streaming) |
list_models |
17 live model slugs (GPT-5.5, 5.4 Thinking, etc.) |
list_projects |
ChatGPT projects with persistent memory |
create_project |
Create isolated workspace with dedicated memory |
delete_project |
Permanently remove a project (gated: W2A_ENABLE_DESTRUCTIVE) |
list_conversations |
Recent chats with pagination |
get_conversation |
Full message history |
archive_conversation |
Archive/unarchive (reversible) |
delete_conversation |
Delete permanently |
list_memories |
Facts ChatGPT remembers (41 found in testing) |
create_memory |
Tell ChatGPT to remember something |
delete_memory |
Remove a memory |
list_gpts |
Discover Custom GPTs |
chat_with_gpt |
Chat with a specific Custom GPT |
update_project_instructions |
Change project system prompt |
list_project_files |
Files in a project's knowledge base |
Every tool includes rich descriptions with domain knowledge, Pydantic-validated input, structured output schemas, and proper ToolAnnotations — agents understand how and when to use each one without prompting.
🔒 MCP Tool Access Control
The MCP server has no authentication of its own — any client that can reach it can call its tools. Since several tools mutate or delete data on your real ChatGPT account, tools are gated by risk tier so the dangerous ones are never exposed by accident:
| Tier | Tools | Exposed when |
|---|---|---|
| Safe (always visible) | chat_completion, chat_with_gpt, list_models, list_projects, list_conversations, get_conversation, list_memories, list_gpts, list_project_files |
Out of the box |
| Write (account mutation) | create_project, update_project_instructions, create_memory, archive_conversation |
W2A_ENABLE_WRITE=1 |
| Destructive (irreversible) | delete_conversation, delete_memory |
W2A_ENABLE_DESTRUCTIVE=1 |
# Default: safe reads + core chat only — nothing can mutate your account
chatgpt-web2api-mcp
# Enable project/memory/archive creation
W2A_ENABLE_WRITE=1 chatgpt-web2api-mcp
# Full surface (also exposes deletes)
W2A_ENABLE_WRITE=1 W2A_ENABLE_DESTRUCTIVE=1 chatgpt-web2api-mcp
Gated tools are hidden from list_tools and refused at call time — a client that calls a gated tool by name gets an error result rather than silent execution. Binding the SSE transport to a non-loopback address with no api_keys configured logs a prominent warning.
🔌 ZCode Hook: One-Line Bootstrap with ensure
Instead of manually starting REST and SSE in separate terminals, run a single
command that reconciles both and exits — ideal for a ZCode session-start hook:
chatgpt-web2api ensure
ensure is point-in-time reconciliation, not a supervisor: it checks REST
(/health) and SSE, starts whichever is missing, verifies SSE with a real MCP
handshake, and exits 0 when both are ready (nonzero with a diagnostic if not).
If a service dies later, re-run ensure from the next hook/session.
# Reconcile REST on 8080 + SSE on 8090 (defaults)
chatgpt-web2api ensure
# Custom ports / config (propagated to the child processes)
chatgpt-web2api ensure --rest-port 8081 --mcp-sse-port 8091 --config ~/.my-config.json
Degraded-REST policy: ensure does not restart REST immediately ondegraded (Chrome alive, driver disconnected) — it polls for up to 20s first,
since that may be a transient CDP reconnect. Only missing or broken trigger
an immediate (re)start. This avoids a destructive Chrome bounce on transients.
🔧 Three Interfaces, One Chrome Session
# Terminal 1: Start Chrome + API
chatgpt-web2api
# Terminal 2: MCP server (SSE recommended for ZCode; stdio also supported)
chatgpt-web2api-mcp --transport sse --port 8090
# or: chatgpt-web2api-mcp # stdio (one MCP child per client)
# Terminal 3: Any OpenAI SDK, curl, or HTTP client
curl http://localhost:8080/v1/chat/completions ...
What Makes This Different
| ChatGPT-Web2API | chat2api | ChatGPTReversed | Official API | |
|---|---|---|---|---|
| Anti-bot handling | ✅ Automatic (CDP) | ❌ Manual (PoW/Turnstile) | ❌ Manual | N/A |
| Token extraction | ❌ None needed | ✅ Required | ✅ Required | N/A |
| Project memory | ✅ Full CRUD | ❌ | ❌ | ❌ |
| MCP server | ✅ 16 tools | ❌ | ❌ | ❌ |
| ChatGPT memories | ✅ List/create/delete | ❌ | ❌ | ❌ |
| Custom GPTs | ✅ Chat with any GPT | ❌ | ❌ | ❌ |
| OpenAI SDK compat | ✅ Drop-in | ✅ | ❌ | ✅ (native) |
| Streaming | ✅ SSE | ✅ | ❌ | ✅ |
| Multi-turn | ✅ Auto-continue | ✅ | ❌ | ✅ |
| Installation | pip install |
Docker | npm | pip install openai |
| Cost | Free (uses your subscription) | Free | Free | Pay-per-token |
Key insight: Other proxies fight the anti-bot system. This one uses the browser as the solution — Chrome handles Turnstile, PoW, and session management automatically.
MCP Tools in Action
An AI agent connected via MCP can:
# Create an isolated project with dedicated memory
create_project(name="Python Async Specialist", memory_scope="project_v2")
# Chat within the project (persistent memory across conversations)
chat_completion(message="What are Python coroutines?", project_id="g-p-abc123")
# Manage memories
list_memories() # → 41 facts ChatGPT remembers
create_memory(content="Always use type hints in Python code")
delete_memory(memory_id="abc-123")
# List and manage conversations
list_conversations(limit=10)
archive_conversation(conversation_id="xyz", archive=True)
# Chat with a Custom GPT
chat_with_gpt(gpt_id="g-hkJGhxxx", message="Analyze this data")
Configuration
CLI Flags
| Flag | Default | Description |
|---|---|---|
--port |
8080 | API server port |
--cdp-port |
9222 | Chrome debugging port |
--headless |
false | Run Chrome headless (may trigger detection) |
--log-level |
INFO | DEBUG, INFO, WARNING, ERROR |
Config File (config.json)
{
"port": 8080,
"host": "127.0.0.1",
"cdp_port": 9222,
"headless": false,
"default_model": "auto",
"default_project_id": null,
"tab_mode": "owned",
"parallel_tabs": false,
"api_keys": [],
"request_timeout": 120
}
See config.example.json for all available keys.
MCP Session Pool (B1 — experimental, default off)
When mcp_session_pool_enabled=true, the MCP SSE server does not connect to
Chrome at startup. Instead, each MCP client session gets its own ownedCDPDriver/Chrome tab on first request (lazy materialization). Different
sessions get different tabs, capped by pool size. This enables parallel
multi-session use of one shared SSE endpoint.
Requires parallel_tabs=true + tab_mode=owned (validated at load).
{
"parallel_tabs": true,
"tab_mode": "owned",
"mcp_session_pool_enabled": true,
"mcp_session_pool_size": 2
}
Or via environment:
W2A_PARALLEL_TABS=1 \
W2A_MCP_SESSION_POOL_ENABLED=1 \
W2A_MCP_SESSION_POOL_SIZE=2 \
chatgpt-web2api-mcp --transport sse --port 8090
Pool defaults are conservative (size=2, TTL=30min). Raise the size only after
the account-level burst canary shows no token-rotation errors or hard account
challenges from genuinely simultaneous multi-tab use.
Environment Variables
W2A_PORT=8080 W2A_CDP_PORT=9222 chatgpt-web2api
See .env.example for all available variables.
Tested Models
Live models from a ChatGPT Plus account (June 2025):
| Slug | Type | Best For |
|---|---|---|
auto |
Reasoning | General use (default) |
gpt-5-5 |
Reasoning, 34K context | Complex analysis |
gpt-5-4-thinking |
Extended reasoning | Step-by-step logic |
gpt-5-3-mini |
Fast, 34K context | Simple tasks, speed |
gpt-5-mini |
Fast, 8K context | Quick answers |
Use list_models to get the current catalog.
Performance
| Operation | Time |
|---|---|
| Simple question ("2+2?") | 7s |
| Complex reasoning | 15–36s |
| Multi-turn follow-up | 3–6s faster (auto-continue) |
| Model listing | <1s |
| Memory listing | <1s |
Requirements
- Python 3.11+
- Chrome or Chromium installed
- ChatGPT Plus subscription
Project Structure
src/chatgpt_web2api/
├── __main__.py CLI entrypoint (chatgpt-web2api)
├── config.py Configuration from file/env/CLI
├── chrome.py Chrome subprocess lifecycle
├── cdp_driver.py CDP primitives (24 methods)
├── api_server.py OpenAI-compatible HTTP server
├── mcp_server.py MCP server (16 tools, resources, prompts)
└── service.py Orchestrator: Chrome → CDP → API/MCP
Testing
The suite has two tiers:
Unit tests (default, CI) — fast, no browser, fully mocked. Run with pytest.
These cover structure, business logic, MCP protocol-level behavior, and access gating.
pytest -v # all unit tests (CI runs: pytest -m "not e2e")
End-to-end tests (opt-in) — drive a real ChatGPT account via Chrome CDP.
These are the only tests that exercise the driver↔ChatGPT boundary, which is
exactly where latent bugs hide (the E2E suite has already found four). They
mutate the account, so they are deselected by default and never run in CI.
# 1. Launch Chrome with CDP (reuses your logged-in profile)
# "chrome.exe" --remote-debugging-port=9222 \
# --user-data-dir=~/.chatgpt-web2api/chrome-profile https://chatgpt.com/
# 2. Run the E2E suite
W2A_E2E_RUN=1 pytest -m e2e -v
E2E safety model:
- Snapshot-before / diff-after for every destructive op — no test ever
deletes state it didn't create in the same session. - A session registry + guaranteed-cleanup finalizer removes anything a test
created, even on crash. - Pacing (
W2A_E2E_PACE, default 12s) between tests to avoid ChatGPT's
"Too many requests" rate limit. - Reads are zero-risk; chat/memory operations create throwaway state that's
cleaned up;create_project/update_project_instructionsarexfail(broken
against the current live API — see CHANGELOG).
If not logged in, the suite opens a headed window and waits up to 10 minutes
for you to log in.
Documentation
📋 Full documentation index — start here if you're unsure which doc to read.
Key docs:
- Deployment Guide — pip install, Docker, cookie injection, multi-instance, sharing
- OS Supervision Guide — systemd / launchd / Task Scheduler / NSSM for always-on deployments
- Production Runbook — health interpretation, failure modes, breaker states, auth recovery, safe restart
- API Reference — OpenAI-compatible REST endpoints + MCP server surface
- Architecture — codebase structure and the CDPDriver hub-and-spoke module layout
- Protocol Reference — captured ChatGPT web API endpoints
- Roadmap — phases 1–6, what landed, what's deferred
- Contributing — how to contribute
- Changelog — version history
Limitations
- Single browser session — one Chrome profile = one ChatGPT account (scale with nginx round-robin)
- No headless — headless Chrome triggers ChatGPT's bot detection; use VNC on servers
- Cookie expiry — auth cookies expire ~2 weeks; re-login needed
- Serial requests — one chat at a time through the browser by default (concurrent reads are fine). For per-tab parallelism on one shared Chrome, set
parallel_tabs: true(requirestab_mode: "owned"); see docs/deployment.md → "Parallel mode (one Chrome, many tabs)". - Memory writes — ChatGPT's
/backend-api/memoriesis read-only; creating memories works via chat interface - No image input — text only (CDP file upload not yet implemented)
Roadmap
- Image/file upload to conversations via CDP drag-and-drop
- Headless mode with anti-detection patches
- Concurrent chat pooling across multiple Chrome instances (per-tab parallelism on one Chrome landed via
parallel_tabs; the cross-instance pool/router is still future work) - Web search mode (trigger ChatGPT's built-in search)
- DALL-E image generation via ChatGPT
- Canvas/code execution support
- Retry logic with exponential backoff for Chrome failures
License
MIT © Elephant Rock Lab
Reviews (0)
Sign in to leave a review.
Leave a reviewNo results found