Setup and a first call
This is an independent OpenAI-compatible service, not OpenAI's own Codex API. Its single model is open-weight, text only, and handles code well enough for generation, explanation and refactoring tasks you should still review. If you want the background first, the page on the OpenAI Codex API explains how the two relate.
Install the SDK with pip install openai, create a key on the API key page, and run:
# pip install openai
from openai import OpenAI
client = OpenAI(base_url="https://api.getcodexapi.com/v1", api_key="YOUR_KEY")
resp = client.chat.completions.create(
model="uncensored",
messages=[
{"role": "system", "content": "You are a senior Python developer. Reply with code only, no commentary."},
{"role": "user", "content": "Write a function that parses an ISO 8601 date string and returns a datetime."},
],
temperature=0.2,
)
print(resp.choices[0].message.content)The new users' trial credit of $0.50 covers thousands of small calls like this one. Temperature is 0.2 because code benefits from less randomness. If you see a markdown fence around the answer, strip it in code or ask in the system message for raw text. The base URL page, Codex API base URL, lists the exact address again.
Prompting for code that runs
A few habits improve output more than any parameter.
- State the language and version. "Python 3.11, standard library only" removes a lot of guessing.
- Give the signature. Paste the function header and a docstring, and ask the model to fill the body.
- Show one example input and output. It pins down edge cases better than prose.
- Ask for one thing. Generate the function, then ask for tests in a second call.
- Request a diff for edits. Whole-file rewrites burn output tokens, which cost more than input.
The context window is 100,000 tokens, and max_tokens is optional. For a code task you can paste several modules and still have room for the reply. Setting max_tokens is useful only as a safety net against runaway output.
JSON mode for structured results
When code consumes the answer, use JSON mode. It makes the output valid JSON; it does not enforce your schema, so name the keys in the prompt and check them.
import json
from openai import OpenAI
client = OpenAI(base_url="https://api.getcodexapi.com/v1", api_key="YOUR_KEY")
resp = client.chat.completions.create(
model="uncensored",
response_format={"type": "json_object"},
messages=[
{"role": "system", "content": "Return JSON with keys: name, args (list of strings), returns."},
{"role": "user", "content": "Describe the signature of: def clamp(x, lo, hi)."},
],
temperature=0,
)
data = json.loads(resp.choices[0].message.content)
print(data["name"], data["args"])Pair this with a try/except around json.loads and a single retry that sends the parse error back. In practice that handles the rare malformed response. Use temperature 0 so repeated runs match.
Tool calling
With tools you describe functions and the model decides when to call them. You run the function, then send the output back with the tool role. This is the base of every coding agent.
import json
from openai import OpenAI
client = OpenAI(base_url="https://api.getcodexapi.com/v1", api_key="YOUR_KEY")
tools = [{
"type": "function",
"function": {
"name": "run_tests",
"description": "Run the project's test suite and return the output.",
"parameters": {
"type": "object",
"properties": {"path": {"type": "string"}},
"required": ["path"],
},
},
}]
messages = [{"role": "user", "content": "Run the tests in tests/unit and tell me what failed."}]
first = client.chat.completions.create(model="uncensored", messages=messages, tools=tools, tool_choice="auto")
msg = first.choices[0].message
if msg.tool_calls:
call = msg.tool_calls[0]
args = json.loads(call.function.arguments)
output = "2 passed, 1 failed: test_parse_empty" # replace with a real runner
messages.append(msg)
messages.append({"role": "tool", "tool_call_id": call.id, "content": output})
final = client.chat.completions.create(model="uncensored", messages=messages, tools=tools)
print(final.choices[0].message.content)Test generated code like any other contribution: run it, lint it, and keep a small suite of prompts with known-good outputs so you notice when a prompt change makes results worse. Pin seed and temperature while you compare versions.
Notes: keep tool descriptions short and literal, validate arguments before running anything, and never pass model output straight into a shell. For a loop, repeat the call while tool_calls is present and stop after a maximum number of steps. Each step resends the history, so a ten-step run pays for roughly ten times the context.
Streaming and retry on 429
For interactive tools, stream tokens as they arrive. Usage comes at the end of the stream, which makes cost logging easy.
import time
from openai import OpenAI, RateLimitError
client = OpenAI(base_url="https://api.getcodexapi.com/v1", api_key="YOUR_KEY", max_retries=0)
def ask(prompt, tries=4):
for attempt in range(tries):
try:
stream = client.chat.completions.create(
model="uncensored",
messages=[{"role": "user", "content": prompt}],
stream=True,
stream_options={"include_usage": True},
)
text = []
for chunk in stream:
if chunk.choices and chunk.choices[0].delta.content:
piece = chunk.choices[0].delta.content
text.append(piece)
print(piece, end="", flush=True)
if chunk.usage:
print("\ntokens:", chunk.usage.prompt_tokens, chunk.usage.completion_tokens)
return "".join(text)
except RateLimitError:
time.sleep(5 * (attempt + 1))
raise RuntimeError("still rate limited")
ask("Explain Python generators in four sentences.")The limits behind that retry loop: a full key allows 8 concurrent requests and 300 per minute, and a trial key 2 and 60. Over a limit you get HTTP 429 with code rate_limited or concurrency. Temporary overload also gives 429 with a Retry-After: 5 header. The example sets max_retries=0 on the client so your own loop controls the waits. A streamed request holds its concurrency slot until the stream finishes, so always consume or close the stream.
Running many requests at once
Batch jobs such as generating tests for a hundred functions need a concurrency cap. Use a thread pool with at most 8 workers on a full key.
A simple pattern is ThreadPoolExecutor(max_workers=6) around a function that calls ask, leaving two slots free for other work. With the trial key use 2 workers. If you pass a bigger number, the API will answer extra requests with concurrency errors, and your retry loop will spend time waiting instead of working. For async code, use AsyncOpenAI with an asyncio.Semaphore(6) around each call; it gives the same cap without threads. The X-RateLimit-Limit-Requests and X-RateLimit-Limit-Concurrency response headers show the limits for your key.
Cost of coding workloads
The price is $0.15 per 1M input tokens and $0.50 per 1M output tokens. Say you generate unit tests for 200 functions. Each call sends 1,500 input tokens and returns 700 tokens. Input: 200 x 1,500 = 300,000 tokens, or $0.045. Output: 200 x 700 = 140,000 tokens, or $0.07. Total: $0.115.
A refactor of a large file sends 30,000 tokens in and 8,000 out: $0.0045 + $0.004 = $0.0085 per run. Failed or refused requests are not charged, and credit never expires. Top-ups start at $20 and can be paid by card or crypto, with a bonus of 5% from $50, 10% from $100 and 15% from $200. See pricing, and read the docs for the full parameter list.

