ChatGPT-Web2API

mcp
Security Audit
Warn
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.

SUMMARY

OpenAI-compatible API proxy for ChatGPT web — expose ChatGPT as an MCP server, REST API, and OpenAI SDK endpoint via CDP-driven browser automation

README.md

Project Banner

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.

CI
Release
License: MIT
Python 3.11+
Tests


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 on
429 + 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: a
CallToolResult 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 a
doctor 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, one
CDPDriver, 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 and
tab_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 on
degraded (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 owned
CDPDriver/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_instructions are xfail (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:

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 (requires tab_mode: "owned"); see docs/deployment.md → "Parallel mode (one Chrome, many tabs)".
  • Memory writes — ChatGPT's /backend-api/memories is 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)

No results found