Skip to main content

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

Version 0.5.8 aligns the SDK with Codex source commit 0eb98b4617: WebSocket interruption, message replay fields, numeric reasoning efforts, and scoped installed-plugin pagination. See the source audit for verified contracts and the separately inventoried, unsupported cloud gRPC transport.

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="")

To stop an active WebSocket response, call ws.interrupt() after receiving response.created, including from another thread. Keep consuming events until both response.interrupt.accepted and the terminal response event have arrived before reusing the connection. Closing an unfinished event iterator closes the connection. Replayed messages retain their IDs, phase, status, encrypted content, and backend metadata.

Custom numeric reasoning budgets are also supported: reasoning={"effort": 4096, "context": "all_turns"}. Numeric strings such as "4096" are encoded as JSON integers, matching Codex; model support is determined by the backend.

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

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; configure max_retries and retry_base_delay on OpenAI(...).
  • 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

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.8

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for codex-backend-sdk 0.5.8
File Size Uploaded Provenance
codex_backend_sdk-0.5.8.tar.gz 201.8 kB Without provenance Details

Built distribution (wheel)

Table of built distributions (wheels) for codex-backend-sdk 0.5.8
File Interpreter ABI Platform
codex_backend_sdk-0.5.8-py3-none-any.whl Python 3 none any Details

Total release size: 289.2 kB

Release files / codex_backend_sdk-0.5.8.tar.gz

Download URL codex_backend_sdk-0.5.8.tar.gz
Size 201.8 kB
Tags Source
SHA-256 checksum
How to use checksums
236e7879acdeb6e940a03f99f52c9a3e49bbdfcf784f32ef6ca8c381a0c22990
BLAKE2b-256 checksum
How to use checksums
ab137e2194b08c3c6f9fdd3728e7403b52102a8afe39be41be3ab83317a3b3f2
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.8-py3-none-any.whl

Download URL codex_backend_sdk-0.5.8-py3-none-any.whl
Size 87.4 kB
Tags Python 3
SHA-256 checksum
How to use checksums
716159646ab749745d311cca23455f410f30ac1d0a70ab59a93debe67f210151
BLAKE2b-256 checksum
How to use checksums
b94d4db8bb6488369ba677c30f881de81efd9c38136c305c18b2b88a7ea951b5
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.8

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.

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page