codex-backend-sdk
Unofficial Python SDK for building independent harnesses and integrations on top of the backend capabilities available to an authenticated Codex/ChatGPT subscriber.
The package intentionally separates three kinds of API:
| Surface | Purpose | Typical stability |
|---|---|---|
client.* |
OpenAI-shaped inference and media primitives exposed by Codex | Most familiar and typed |
client.codex.* |
Codex-specific account, cloud, search, and Remote Control capabilities | Product-specific |
client.chatgpt.* |
ChatGPT product surfaces observed in the official Desktop client | Broadest, often raw |
Requirements: a ChatGPT Plus, Pro, or Enterprise subscription. Authentication uses the official Codex OAuth client and stores compatible credentials in
~/.codex/auth.json.
Independent project: this library reverse-engineers undocumented backend contracts. It is not affiliated with, endorsed by, or supported by OpenAI. Availability can vary by plan, workspace, rollout, and backend revision.
Install
pip install --upgrade codex-backend-sdk
For an editable checkout:
git clone https://github.com/B4PT0R/codex-backend-sdk.git
cd codex-backend-sdk
pip install -e .
Quickstart
from codex_backend_sdk import OpenAI
client = OpenAI().authenticate()
response = client.responses.create(
model="gpt-5.5",
input="Explain quicksort in one paragraph.",
)
print(response.output_text)
authenticate() reuses existing Codex credentials when possible and otherwise
opens the browser OAuth flow. Most examples below assume an authenticated
client created this way.
Choose the right surface
Direct client: inference and reusable primitives
Start here for the common building blocks of an agent or application:
client.responses # text/reasoning/tool inference, parsing, compaction
client.models # Codex model catalog
client.files # upload files for Apps/MCP parameters
client.images # Codex-backed image generation and editing
client.audio # ChatGPT-backed transcription
client.embeddings # OpenAI embeddings endpoint through OAuth
client.live # OpenAI-shaped GPT-Live creation and sideband attachment
client.realtime # additive low-level Codex Realtime v3 transport
These resources follow openai-python conventions where the backend overlaps
with the public OpenAI API. Backend-specific restrictions remain explicit.
See the compatibility matrix for the audited
signatures, transport adaptations, and intrinsic OAuth/backend boundaries.
client.codex: Codex product capabilities
Use this namespace for capabilities owned by Codex rather than the general Responses API:
client.codex.web_search # structured search/page/weather/etc. commands
client.codex.usage # quota plus detailed usage
client.codex.tasks # Codex cloud tasks and turns
client.codex.environments # cloud environments and machines
client.codex.repositories # repository and branch discovery
client.codex.remote_control # Remote Control server and host discovery
client.codex.config # managed Codex settings/configuration
client.codex.profile # Codex profile
client.codex.memories # account memories and trace summarization
client.codex.worktree_snapshots # cloud-task archive uploads
client.chatgpt: Desktop-observed product APIs
Use this namespace when an integration needs ChatGPT product state or hosted Apps rather than Codex inference:
client.chatgpt.conversations # ChatGPT history and streaming conversation API
client.chatgpt.projects # projects, files, saves, connector scopes
client.chatgpt.files # file library, attachments, downloads, processing
client.chatgpt.search # cross-product indexed search
client.chatgpt.apps # hosted Apps/MCP transports and widgets
client.chatgpt.plugins # plugin catalogs, skills, bundles, sharing
client.chatgpt.connectors # connector discovery, linking, external actions
client.chatgpt.models # ChatGPT and third-party model catalogs
client.chatgpt.voice # voices, dictation metadata, read-aloud speech
client.chatgpt.account # account metadata and explicit preferences
These private product schemas evolve more often. The SDK therefore returns raw dictionaries or raw streams when imposing a stable model would be misleading.
Common workflows
Generate a response
response = client.responses.create(
model="gpt-5.5",
instructions="Be concise.",
input="Summarize the CAP theorem.",
reasoning={"effort": "medium", "summary": "auto"},
)
print(response.output_text)
print(response.reasoning_summary)
The backend always streams internally. Without stream=True, the SDK collects
events and returns a typed Response.
Stream output
stream = client.responses.create(
input="Write a short limerick about Linux.",
stream=True,
)
for event in stream:
if event.type == "response.output_text.delta":
print(event.delta, end="", flush=True)
For long-lived integrations, the alternate WebSocket transport keeps one connection reusable across sequential turns:
with client.responses.websocket.connect() as ws:
for event in ws.create({"model": "gpt-5.5", "input": "Say hello."}):
if event.get("type") == "response.output_text.delta":
print(event.get("delta", ""), end="")
Maintain a multi-turn conversation
The Codex backend does not expose previous_response_id; preserve prior items
in the next request:
history = [{"role": "user", "content": "My name is Alice. Say OK."}]
first = client.responses.create(input=history)
history.extend(first.output)
history.append({"role": "user", "content": "What is my name?"})
print(client.responses.create(input=history).output_text)
Compact a long context
compacted = client.responses.compact(
model="gpt-5.5",
instructions="Preserve task-critical decisions and unresolved work.",
input=history,
)
history = compacted.output
Compaction items are opaque encrypted backend state. Replay them as input; do not parse or modify their contents.
Call a function
import json
tools = [{
"type": "function",
"name": "get_weather",
"description": "Get the current weather for a city.",
"parameters": {
"type": "object",
"properties": {"city": {"type": "string"}},
"required": ["city"],
"additionalProperties": False,
},
}]
first = client.responses.create(
input="What is the weather in Paris?",
tools=tools,
)
call = first.tool_calls[0]
second = client.responses.create(
input=[
call,
{
"type": "function_call_output",
"call_id": call["call_id"],
"output": json.dumps({"temperature": 18, "unit": "celsius"}),
},
],
tools=tools,
)
print(second.output_text)
Parse structured output
from pydantic import BaseModel
class Person(BaseModel):
name: str
age: int
result = client.responses.parse(
input="Extract: Ada is 37 years old.",
text_format=Person,
)
print(result.output_parsed)
Discover available models
for model in client.models.list():
print(model.id, model.context_window, model.supported_reasoning_levels)
model = client.models.retrieve("gpt-5.5")
Model discovery obtains the latest stable Codex CLI version over HTTPS from the public npm registry; Node, npm and the Codex binary are not required. The version is cached for 24 hours per process, shared across clients. The lookup uses a separate request without backend credentials and a three-second timeout. On failure it keeps the last known version (or a bundled verified fallback) and retries no sooner than five minutes later. This only updates the advertised catalog version, not the SDK's implementation of backend features.
To pin the catalog version and skip registry access entirely:
models = client.models.list(extra_query={"client_version": "0.159.2"})
force_refresh=True bypasses the five-minute model catalog cache, not the
24-hour version cache. Requests with explicit options do not replace the default
catalog cache. Other protocol and application version fields are independent.
Upload a file
uploaded = client.files.upload("report.csv")
print(uploaded.uri) # sediment://file_...
The helper creates ChatGPT file metadata, uploads to the backend-issued signed URL, and finalizes the file for Apps/MCP file parameters.
Transcribe audio
with open("meeting.wav", "rb") as audio:
transcript = client.audio.transcriptions.create(
model="gpt-4o-mini-transcribe",
file=("meeting.wav", audio, "audio/wav"),
)
print(transcript.text)
Generate or edit an image
import base64
image = client.images.generate(
model="gpt-image-2",
prompt="A cheerful blue robot holding a red flower",
)
with open("robot.png", "wb") as output:
output.write(base64.b64decode(image.data[0].b64_json))
edited = client.images.edit(
image=open("robot.png", "rb"),
mask=open("mask.png", "rb"),
prompt="Add a small red star in the center",
)
Codex workflows
Structured web search
result = client.codex.web_search.search(
id="search-session-1",
model="gpt-5-search-api",
commands={
"search_query": [{"q": "Python packaging PEP 735"}],
"response_length": "short",
},
)
print(result["output"])
continuation = result.get("encrypted_output")
The same transport supports page operations, image search, finance, weather, sports, and time commands. See the API reference for command validation.
Inspect quota and detailed usage
quota = client.codex.usage()
daily = client.codex.usage_details.daily_token_breakdown()
credits = client.codex.usage_details.credit_events()
plugin_metrics = client.codex.usage_details.plugin_usage_metrics(
start_date="2026-09-01",
end_date="2026-09-18",
)
print(quota.get("rate_limit", {}).get("primary_window"))
Work with Codex cloud tasks
tasks = client.codex.tasks.list(limit=10)
task = client.codex.tasks.retrieve(tasks["items"][0]["id"])
turns = client.codex.tasks.turns.list(task["task"]["id"])
logs = client.codex.tasks.turns.logs(task["task"]["id"], turns[0]["id"])
environments = client.codex.environments.list()
repositories = client.codex.repositories.search(
"codex",
connector_id="github-connector-id",
)
Creation, cancellation, environment updates, cache resets, and pull-request operations are explicit mutations; discovery methods never invoke them.
Run a Remote Control server
server = client.codex.remote_control.enroll(
name="My workstation",
installation_id="stable-installation-id",
os="linux",
arch="x86_64",
app_server_version="0.147.0",
)
pairing = client.codex.remote_control.pairing.start(server, manual_code=True)
print(pairing.manual_pairing_code)
with client.codex.remote_control.connect(
server,
installation_id="stable-installation-id",
server_name="My workstation",
) as connection:
for envelope in connection:
print(envelope)
The transport deliberately leaves protocol-v3 envelopes raw. A bridge must preserve client, stream, sequence, ACK, chunk, and cursor semantics. Never log pairing codes or Remote Control tokens.
ChatGPT product workflows
Search and retrieve ChatGPT history
page = client.chatgpt.conversations.list(limit=20)
matches = client.chatgpt.conversations.search("release notes")
conversation = client.chatgpt.conversations.retrieve("conv_...")
global_matches = client.chatgpt.search.global_search(
"release notes",
sources=("conversation",),
limit=20,
)
ChatGPT conversations are product history, not Codex App Server threads and not Responses API state.
Browse projects and files
projects = client.chatgpt.projects.list_all()
project = client.chatgpt.projects.retrieve(projects[0]["id"])
project_conversations = client.chatgpt.projects.conversations(projects[0]["id"])
library = client.chatgpt.files.list_library_files()
content = client.chatgpt.files.download("file_...")
Download helpers can return bytes, BytesIO, a persisted Path, or the raw
HTTP response. OAuth is retained for ChatGPT URLs and omitted from signed CDN
URLs.
Use hosted Apps/MCP
tools = client.chatgpt.apps.list_tools()
result = client.chatgpt.apps.call_tool("connector_tool_name", {"query": "..."})
with client.chatgpt.apps.connect_hosted_mcp() as mcp:
hosted_tools = mcp.list_tools()["tools"]
resources = mcp.list_resources()["resources"]
Hosted tools may mutate external services. The calling harness must inspect tool annotations and own user confirmation; the SDK never auto-invokes tools.
Discover plugins and skills
plugins = client.chatgpt.plugins.list_all(scope="GLOBAL")
installed = client.chatgpt.plugins.installed_all()
suggested = client.chatgpt.plugins.suggested()
home = client.chatgpt.plugins.home()
skill = client.chatgpt.plugins.skill("plugins~...", "skill-name")
checkout = client.chatgpt.plugins.bundles.extract_plugin(
"plugins~...",
"./plugins/example",
)
Bundle helpers enforce HTTPS, download limits, archive limits, safe relative paths, manifest checks, and atomic extraction. Signed download URLs never receive the user's OAuth bearer.
Discover connectors safely
connectors = client.chatgpt.connectors.directory.list_all()
detail = client.chatgpt.connectors.retrieve(
connectors[0]["id"],
include_actions=True,
)
links = client.chatgpt.connectors.links.list_accessible()
Read-only discovery is separate from
client.chatgpt.connectors.authentication and
client.chatgpt.connectors.external_actions. Linking, email, contacts, and
Google Drive conversion require explicit method calls and caller-owned approval.
Generate subscription-backed speech
speech = client.chatgpt.voice.synthesize_pronunciation(
text="Bonjour Baptiste",
pronunciation_language="fr-FR",
)
speech.write_to_file("bonjour.mp3")
The ChatGPT read-aloud service is smaller than the public speech API: it exposes
language and playback speed, not arbitrary model and voice selection. It can
return a typed in-memory object, bytes, BytesIO, a data URI, or a file path.
Turn-scoped HTTP routing
For an agent turn with several generations (tools or retries), use an isolated Responses resource. Continue sending the complete input as usual:
with client.responses.turn() as turn:
first = turn.create(input=items, prompt_cache_key="conversation-id")
# Append model/tool results to items, then generate again in the same turn.
following = turn.create(input=items, prompt_cache_key="conversation-id")
The resource captures the first x-codex-turn-state from HTTP response headers
or response.metadata events and replays it unchanged within this scope. The
opaque routing token is never added to model input or persisted. Leaving the
scope closes it; a new turn starts without the previous token. Authentication
or cache-affinity changes invalidate it. Separate scopes do not share tokens,
even on the same client. Streaming callers must consume the metadata event for
an SSE-only token to be captured. Compaction remains a separate backend request
and does not inherit the turn token; this scope does not manage WebSockets.
prompt_cache_key also supplies the HTTP session-id affinity header unless
an explicit request/client header overrides it (case-insensitively). These are
routing mechanisms, not a guarantee of a cache hit or reduced quota usage.
response.usage.input_tokens_details.cached_tokens preserves the API's cache
read measurement: an absent or null value is None, while an explicitly reported
zero remains 0. The SDK does not calculate cache percentages or moving averages;
applications should exclude missing measurements from those calculations.
Advanced transports
Realtime voice
live = client.live.create(
session={
"model": "gpt-live-1-codex",
"instructions": "Speak naturally and stay concise.",
"delegation": {"type": "client"},
},
transport={"type": "webrtc", "sdp": offer_sdp},
)
apply_remote_sdp(live.transport.sdp)
with client.live.sideband.connect(session_id=live.session.id) as sideband:
for event in sideband:
print(event.type)
Model identifiers are forwarded unchanged: the backend decides which models
are available to your account, so new Codex models do not require an SDK update.
Earlier call-creation probes accepted gpt-live-1-codex and
gpt-live-1-boulder-alpha; these examples are not an exhaustive catalog.
A gpt-live-1.1 probe on October 22, 2026 also returned an SDP answer and
connected WebRTC, but the server then rejected the voice session with
forbidden: Voice session access denied. Call creation alone does not prove
that an account can receive model responses. client.live follows the current
openai-python creation, response, and sideband idioms while adapting them to
the ChatGPT OAuth call transport.
The earlier low-level surface remains available for backend-specific code:
answer = client.realtime.calls.create_v3(
sdp=offer_sdp,
session={"model": "gpt-live-1-codex"},
)
with client.realtime.sideband.connect(call_id=answer.call_id) as sideband:
sideband.send({"type": "session.close"})
The OAuth backend's event dialect is still experimental and is exposed
forward-compatibly through LiveEvent rather than rewritten into invented
public fields. The public primary client.live.connect() WebSocket is not
available through ChatGPT OAuth; use WebRTC plus the attached sideband.
Embeddings
embedding = client.embeddings.create(
model="text-embedding-3-small",
input="Embed this sentence.",
dimensions=256,
)
print(embedding.data[0].embedding)
Unlike ChatGPT transcription and Codex image generation, embeddings target the OpenAI Platform endpoint and may consume the associated platform quota.
Authentication and lifecycle
Browser OAuth
client = OpenAI().authenticate()
Use authenticate(interactive=False) to require existing usable credentials
without opening a browser, or force=True to request a fresh interactive login.
Device-code OAuth
client = OpenAI().authenticate_device_code(
on_code=lambda code: print(code.verification_url, code.user_code),
allowed_workspace_ids=["optional-workspace-id"],
)
This is suited to headless or remote harnesses that cannot receive a loopback OAuth callback.
Logout versus revocation
client.logout() # local, offline, idempotent
client.revoke() # remote OAuth revocation, then local cleanup
logout() does not invalidate the account session remotely. revoke() is an
explicit network mutation and clears local credentials only after success.
Reliability and safety
- Transient
429,5xx, timeout, and connection failures are retried by default; configuremax_retriesandretry_base_delayonOpenAI(...). - Private backend schemas may change without notice. Typed models are used only where a stable boundary is useful.
- Mutations are explicitly named and separated from discovery where possible.
- Payments, subscriptions, workspace administration, attestation, reporting, telemetry, analytics, and beacons are deliberately not exposed.
- Personal access tokens are outside this OAuth-focused SDK surface.
Documentation
- API reference: complete resource tree, methods, parameters, return values, and important validation behavior.
- Backend protocol notes: wire contracts, payload details, backend limitations, and live observations.
- Endpoint coverage: exposed, live-probed, contract-tested, and excluded surfaces.
- Desktop endpoint inventory: audited source snapshots and comparison with the official Desktop client.
- Changelog: user-visible SDK changes.
Development
uv sync --extra dev
uv run pytest -q
uv run python -m compileall -q codex_backend_sdk
uv build
Live probes are intentionally kept separate from the deterministic test suite: tests must not consume quota, mutate account state, or depend on rollout state.
License
MIT. See LICENSE.
Metadata
Release files for codex-backend-sdk 0.5.7
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | |
|---|---|---|---|---|
| codex_backend_sdk-0.5.7.tar.gz | 195.4 kB | Without provenance | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| codex_backend_sdk-0.5.7-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 281.4 kB
Release files / codex_backend_sdk-0.5.7.tar.gz
| Download URL | codex_backend_sdk-0.5.7.tar.gz |
|---|---|
| Size | 195.4 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
27747ef386ac135b9d8887b5ac5e8fc8ffabaa7b997d582a4a3ebe8fbeea7f3d
|
|
BLAKE2b-256 checksum How to use checksums |
97171e9c4365928e066af09b1b75c6193c43b3b82dd817fb30a9e41773e7c6c6
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.11.7 {"installer":{"name":"uv","version":"0.11.7","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"26.04","id":"resolute","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
|
Release files / codex_backend_sdk-0.5.7-py3-none-any.whl
| Download URL | codex_backend_sdk-0.5.7-py3-none-any.whl |
|---|---|
| Size | 86.1 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
240c75661b1ed469772ca1ce38184c7f8f863f0db57328ebd12ac828e0b2e428
|
|
BLAKE2b-256 checksum How to use checksums |
1842c6d8f8163230fce33b477bc2ebe55e63b3a5e838cea9dd94281df507c682
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.11.7 {"installer":{"name":"uv","version":"0.11.7","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"26.04","id":"resolute","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
|
Security for codex-backend-sdk 0.5.7
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.