Calling the python requests gpt-4o rest api is the lowest-overhead way to integrate OpenAI’s flagship model into a backend service without dragging in a heavy SDK. You get full control over headers, timeouts, and retries, and you can point the same code at any OpenAI-compatible gateway.
Step 1: Get an API key and configure your environment
Create an API key in the OpenAI dashboard under “API keys”. Export it as an environment variable so it never touches your source tree:
export OPENAI_API_KEY="sk-..."
In Python, read it and fail fast if absent:
import os
API_KEY = os.environ.get("OPENAI_API_KEY")
if not API_KEY:
raise RuntimeError("OPENAI_API_KEY environment variable is not set")
If you run this in a container, inject the secret via the orchestrator’s secret store, not a baked image layer.
Step 2: Understand the endpoint and request shape
The chat completions endpoint is https://api.openai.com/v1/chat/completions. It accepts a JSON body with model, messages, and optional sampling parameters. The python requests gpt-4o rest api interaction is a single HTTP POST.
Headers must carry the bearer token and declare JSON:
import requests
URL = "https://api.openai.com/v1/chat/completions"
HEADERS = {
"Authorization": f"Bearer {API_KEY}",
"Content-Type": "application/json",
}
Request payload reference
A minimal valid body looks like this:
{
"model": "gpt-4o",
"messages": [
{"role": "system", "content": "You are a concise backend engineer."},
{"role": "user", "content": "Explain idempotency keys in one sentence."}
],
"temperature": 0.1,
"max_tokens": 120
}
temperature controls randomness; max_tokens caps the generated reply length, not the context window. GPT-4o supports a 128k token context as documented by OpenAI.
Step 3: Send a minimal chat completion
Build the payload in Python and post it. Always set an explicit timeout—the default blocks forever, which will quietly stall your worker pool during provider brownouts.
payload = {
"model": "gpt-4o",
"messages": [
{"role": "system", "content": "You are a terse senior engineer."},
{"role": "user", "content": "Write a one-line Python function to flatten a list."},
],
"temperature": 0.2,
"max_tokens": 200,
}
resp = requests.post(URL, headers=HEADERS, json=payload, timeout=30)
Using json=payload lets requests serialize and set Content-Type automatically. If you pre-serialize with json.dumps, you must keep the header manual.
Step 4: Parse the response
A 200 response returns a JSON object with a choices array. Extract the assistant message and the finish reason:
resp.raise_for_status()
data = resp.json()
content = data["choices"][0]["message"]["content"]
finish_reason = data["choices"][0]["finish_reason"]
print(content)
print("finish_reason:", finish_reason)
Capture token usage for metering:
usage = data.get("usage", {})
print(f"prompt_tokens={usage.get('prompt_tokens')} completion_tokens={usage.get('completion_tokens')}")
If you later route through a gateway, the usage shape stays identical.
Step 5: Handle errors and rate limits
OpenAI returns 429 on rate limits and 5xx on upstream failures. Wrap the call in a retry loop with exponential backoff; never retry 4xx except 429.
import time
def call_gpt4o(payload, max_retries=3):
for attempt in range(max_retries):
try:
r = requests.post(URL, headers=HEADERS, json=payload, timeout=30)
if r.status_code == 429:
time.sleep(2 ** attempt)
continue
r.raise_for_status()
return r.json()
except requests.HTTPError as e:
if r.status_code >= 500:
time.sleep(2 ** attempt)
continue
raise
raise RuntimeError("Exhausted retries calling GPT-4o")
Use a Session for throughput
If your service makes many calls, reuse a requests.Session to keep TCP/TLS connections alive:
session = requests.Session()
session.headers.update(HEADERS)
def call_gpt4o_session(payload):
r = session.post(URL, json=payload, timeout=30)
r.raise_for_status()
return r.json()
This cuts handshake overhead significantly under load.
Step 6: Stream tokens for responsive UX
Non-streaming calls block until the full completion is ready. For chat interfaces, enable streaming. Set "stream": True and iterate the response as Server-Sent Events.
payload["stream"] = True
with requests.post(URL, headers=HEADERS, json=payload, stream=True, timeout=30) as r:
r.raise_for_status()
for line in r.iter_lines():
if not line:
continue
if line.startswith(b"data: "):
chunk = line[len(b"data: "):]
if chunk == b"[DONE]":
break
delta = json.loads(chunk)
token = delta["choices"][0]["delta"].get("content", "")
print(token, end="", flush=True)
requests has no SSE parser, so you decode lines manually. For async services, httpx.AsyncClient offers the same REST contract with native streaming.
Step 7: Verify the integration end to end
Write a script that asserts a successful round trip. Success means HTTP 200, a non-empty message, and a sane finish_reason.
def test_call():
p = {
"model": "gpt-4o",
"messages": [{"role": "user", "content": "Say 'pong'."}],
"max_tokens": 10,
}
data = call_gpt4o(p)
text = data["choices"][0]["message"]["content"].strip().lower()
assert text == "pong", f"unexpected reply: {text}"
assert data["choices"][0]["finish_reason"] in ("stop", "length")
print("OK")
test_call()
Run python main.py. If it prints OK, your python requests gpt-4o rest api wiring is correct.
You can also verify from the shell before writing Python:
curl -s https://api.openai.com/v1/chat/completions \
-H "Authorization: Bearer $OPENAI_API_KEY" \
-H "Content-Type: application/json" \
-d '{"model":"gpt-4o","messages":[{"role":"user","content":"Say pong"}],"max_tokens":5}' | head
In CI, mock the endpoint with responses or respx to avoid spending tokens on every run.
Step 8: Swap to an OpenAI-compatible gateway
If you want automatic fallback when a provider is rate-limited or degraded, or access to 240+ models behind one endpoint, an OpenRouter-class gateway like n4n.ai exposes the same /v1/chat/completions shape. Change only the base URL and key:
URL = "https://api.n4n.ai/v1/chat/completions"
HEADERS = {"Authorization": f"Bearer {GATEWAY_KEY}", "Content-Type": "application/json"}
The payload stays byte-for-byte compatible because the API is OpenAI-compatible. You can forward provider cache-control hints via extra headers if the gateway honors them.
Gotchas that will bite you
- Missing
Content-Type:requestssets it withjson=, but manualdata=json.dumps(...)without the header yields a 400. - Silent truncation:
max_tokenslimits the reply only. Set it high enough for your task. - Model string casing:
gpt-4ois lowercase with a hyphen.GPT-4Oorgpt-4Ofail. - TLS interception: never set
verify=Falsein production to bypass a proxy. Install the corporate CA bundle correctly. - Timeouts on stream: pass
timeoutto the post, but also handle slow token delivery with a read timeout if your client supports it.
Why raw requests instead of the SDK
The official openai package abstracts auth, retries, and streaming behind a client. That is convenient until you need to inspect exact bytes for a proxy debug or inject custom metrics. The raw python requests gpt-4o rest api path shows every header and body, and avoids SDK version churn when the vendor changes method signatures.
Keep the functions above in a client.py module and import call_gpt4o where needed. That single seam lets you later add caching, structured logging, or provider routing without touching call sites.