Hosted relay · version 1

ChatGPT Desktop API

An OpenAI-compatible hosted API backed by the official ChatGPT macOS app on your paired Mac. The primary base URL is https://chatgpt.authapi.dev/v1.

Experimental and unofficial desktop automation. This is not an OpenAI-supported API.

Setup and pairing

  1. Sign in on the website and in the official ChatGPT macOS app.
  2. Open the dashboard, install bridge v11, and leave its LaunchAgent running.
  3. Create a 10-minute dashboard pairing token, run ~/.local/bin/chatgpt-api pair, and paste it at the hidden prompt. Never put the token in command history.
  4. Confirm the device is online and make it the default target.
  5. Create or use your shared authapi.dev cxa_… API key.

The dashboard shows devices, versions, online state, and last-seen time. Make default selects the hosted request target. Revoke disconnects and permanently unpairs a device. chatgpt-api unpair removes the local credential; revoke the dashboard device too if it remains listed. Cancelling a pending pairing token does not revoke an existing device.

Authentication

Every hosted /v1/* request requires a bearer header containing the example key cxa_RE...EY. Keep keys in headers, never URLs.

The website playground instead uses your secure session cookie against /api/chatgpt/*. Session cookies cannot authorize /v1/*, and API keys cannot authorize dashboard/playground session routes.

VALUE='paste-your-key-here'
curl https://chatgpt.authapi.dev/v1/chat/completions \
  -H "Authorization:"" Bearer ${VALUE}" \
  -H 'Content-Type: application/json' \
  -d '{"model":"chatgpt-desktop","messages":[{"role":"user","content":"Reply with: connected"}]}'

OpenAI Python SDK

from openai import OpenAI
client = OpenAI(base_url="https://chatgpt.authapi.dev/v1", api_key="cxa_RE...EY")
result = client.chat.completions.create(
    model="chatgpt-desktop",
    messages=[{"role": "user", "content": "Hello"}],
)
print(result.choices[0].message.content)

Chat completions

POSThttps://chatgpt.authapi.dev/v1/chat/completions

Accepted fields are exactly messages, model, stream, stream_options, and timeout_ms.

FieldContract
messagesRequired non-empty array. Roles: system, developer, user, assistant, tool. Content is a string or text/input_text parts.
modelOptional non-empty string, maximum 256 characters. Use a live model ID; chatgpt-desktop preserves the current selection.
streamBoolean. True returns OpenAI-style SSE ending with data: [DONE].
stream_optionsOnly with stream=true; accepts only boolean include_usage.
timeout_msInteger 1,000–300,000; default 180,000.

Enforced limits: chat request body 1 MiB; image request body 128 KiB; complete JSON response 8 MiB for chat/health/models and 40 MiB for images. Health has a fixed 10-second deadline; models a fixed 35-second deadline; chat defaults to 180 seconds (300-second cap); images default to 300 seconds (600-second cap). One shared maximum of 8 pending/in-flight requests applies per device across all operations.

Temporary Chat is mandatory. The bridge verifies it before submission and checks that no sidebar conversation was stored; unexpected history triggers exact cleanup and a failed-closed request. Usage counts are zero because the desktop UI exposes no authoritative token counts.

VALUE='paste-your-key-here'
curl -N https://chatgpt.authapi.dev/v1/chat/completions \
  -H "Authorization:"" Bearer ${VALUE}" \
  -H 'Content-Type: application/json' \
  -d '{"stream":true,"messages":[{"role":"user","content":"Count to five"}]}'

Image generations

POSThttps://chatgpt.authapi.dev/v1/images/generations
VALUE='paste-your-key-here'
curl https://chatgpt.authapi.dev/v1/images/generations \
  -H "Authorization:"" Bearer ${VALUE}" \
  -H 'Content-Type: application/json' \
  -d '{"model":"gpt-image-2","prompt":"A watercolor fox","n":1,"response_format":"b64_json"}'

Accepted fields are exactly prompt, model, n, response_format, and timeout_ms. Prompt is required and at most 100,000 characters; model is gpt-image-2; n is 1; response_format is b64_json; timeout is 1,000–600,000 ms (default 300,000). The response contains one base64 PNG.

Images require a fresh regular conversation. The bridge deletes that exact conversation and restores Temporary Chat; unverifiable cleanup fails closed.

Models

GEThttps://chatgpt.authapi.dev/v1/models

Returns an OpenAI-style list discovered live from the signed-in desktop app's model picker. Model and generation operations share the serialized desktop queue.

VALUE='paste-your-key-here'
curl https://chatgpt.authapi.dev/v1/models \
  -H "Authorization:"" Bearer ${VALUE}"

Health

GEThttps://chatgpt.authapi.dev/v1/health
{
  "ok": true,
  "bridge": "chatgpt-desktop",
  "version": 11,
  "app": {"reachable": true, "authenticated": true, "temporaryChat": true}
}

False app fields (and an optional sanitized detail) can still return 200 when bridge v11 is online. No default online bridge produces a relay error.

Architecture, privacy, and security

The Mac initiates wss://chatgpt.authapi.dev/api/chatgpt/relay; no inbound port or LAN exposure is needed. Hosted HTTPS requests are authenticated by the cxa_… key, routed to its owner's default online device, transit authapi.dev transiently in memory over TLS, and return over the same outbound WSS path.

Prompts, text, and image bytes therefore pass through authapi.dev. The hosted relay proxies traffic; it does not stay local or travel directly from a browser to the Mac. Payloads are not stored, though normal security metadata and coarse operation/status/timing logs may be retained. Logs exclude credentials and payloads. ChatGPT cookies, OAuth tokens, and passwords never leave the Mac. TLS does not make the service end-to-end encrypted from authapi.dev.

Pairing tokens expire after 10 minutes and creation is limited to 10 per user per hour. At most 10 non-revoked devices may be paired. Expired/consumed pairings older than 24 hours and unreferenced revoked devices older than 30 days are pruned while recent security metadata remains available for audit and abuse response. Device credentials are stored in a private local config. Revoke lost devices and rotate compromised API keys. If a paired Mac is compromised, assume its relay credential and routed payloads are exposed: revoke it, run chatgpt-api unpair, rotate affected keys, review metadata, and pair a clean Mac.

Errors

{"error":{"message":"Human-readable detail","type":"service_unavailable","param":null,"code":"relay_unavailable"}}
StatusMeaning
400Invalid JSON/content type, unsupported field, or invalid parameter.
401Missing, malformed, revoked, or unknown API key.
403Desktop bridge denied or could not authenticate an operation.
404Unknown /v1 route.
413Request exceeds the operation's bounded body limit.
429Relay/device busy; honor Retry-After when present.
502Bridge disconnected, failed, or returned an invalid/oversized response.
503No usable default online bridge.
504Relay or desktop deadline expired.

If SSE fails after headers, the server aborts the transport without inventing [DONE]. Treat a stream without [DONE] as incomplete.

Advanced: local-only interface

The optional unauthenticated bridge interface binds to 127.0.0.1:1456. Keep it on loopback only; never bind or forward it to a LAN or public interface. It exposes the same generation/model paths without requiring a cxa_… key. The OpenAPI global bearer scheme applies to the primary hosted server and is ignored on loopback; supplying a bearer header locally adds no authentication. Its legacy health path is GET http://127.0.0.1:1456/health, not /v1/health, and additionally reports LaunchAgent and outbound relay state.