n4nAI

Python requests vs the OpenAI SDK: raw REST tradeoffs

A pragmatic engineer's comparison of python requests vs openai sdk for LLM API calls: capabilities, latency, cost, ergonomics, and which to use when building.

n4n Team4 min read887 words

Audio narration

Coming soon — every post will get a voice note here.

The debate over python requests vs openai sdk isn’t about which library is better—it’s about control versus convenience when shipping LLM features. If you’re hitting a single OpenAI endpoint, the SDK removes boilerplate; if you’re routing across many providers or need raw HTTP behavior, raw REST earns its keep.

At a glance

Dimension python requests openai SDK
Capabilities Any HTTP verb, any endpoint, full header control OpenAI-typed models, assistants, streaming helpers
Cost model No added cost; you parse usage yourself No added cost; usage surfaced in response objects
Latency Minimal import/runtime overhead Slightly larger client, built-in retry/backoff
Ergonomics Manual JSON, auth, error handling Native objects, IDE autocomplete, async support
Ecosystem Universal, dependency-light OpenAI-first, rapid support for new API surfaces
Limits You implement rate limits and retries SDK handles some backoff, but not cross-provider

Capabilities

Raw REST with requests

The requests library gives you a bare HTTP client. You construct the URL, set headers, serialize the body, and decode the response. This works against any OpenAI-compatible server, not just OpenAI’s own.

import requests

resp = requests.post(
    "https://api.openai.com/v1/chat/completions",
    headers={"Authorization": f"Bearer {API_KEY}", "Content-Type": "application/json"},
    json={"model": "gpt-4o-mini", "messages": [{"role": "user", "content": "hi"}]}
)
data = resp.json()
print(data["choices"][0]["message"]["content"])

You can swap the base URL to any compatible gateway, add custom proxy headers, or embed the call inside a larger pipeline without pulling in OpenAI-specific abstractions.

OpenAI SDK

The official SDK wraps the same REST surface with Python objects. It knows about chat.completions, embeddings, audio, and newer surfaces like assistants. You get typed responses and method signatures.

from openai import OpenAI

client = OpenAI(api_key=API_KEY)
completion = client.chat.completions.create(
    model="gpt-4o-mini",
    messages=[{"role": "user", "content": "hi"}]
)
print(completion.choices[0].message.content)

The SDK also exposes async clients, websocket-style streaming, and automatic pagination for list endpoints. If you only touch OpenAI, it is the path of least resistance.

Price and cost model

Token pricing is set by the API provider, not the client. Whether you use requests or the SDK, a gpt-4o-mini call costs the same per token. The difference is visibility.

With requests, the usage block sits in the JSON response:

usage = data["usage"]
# {"prompt_tokens": 5, "completion_tokens": 12, "total_tokens": 17}

With the SDK, that same data is an attribute: completion.usage.total_tokens. Neither library bills you; they just expose what the server returns.

When you route through a gateway that aggregates providers, metering becomes a server-side concern. An OpenRouter-class gateway like n4n.ai provides per-token usage metering on a single OpenAI-compatible endpoint, so either client sees the same JSON usage block regardless of which backend model served the request.

Latency and throughput

Per-call latency splits into two parts: client overhead and network round-trip. requests adds almost nothing—it’s a thin wrapper over urllib3. The SDK instantiates a larger client object and includes retry logic that can delay the first byte on transient errors.

In high-throughput batch jobs, connection pooling matters more than client weight. Both support session reuse; with requests you explicitly create a Session, while the SDK manages a client-side connection pool internally.

Streaming is where the SDK shines for ergonomics but not raw speed:

# SDK streaming
stream = client.chat.completions.create(model="gpt-4o-mini", messages=[...], stream=True)
for chunk in stream:
    print(chunk.choices[0].delta.content or "", end="")
# requests streaming
with requests.post(url, headers=headers, json=payload, stream=True) as r:
    for line in r.iter_lines():
        if line and line.startswith(b"data:"):
            print(line[5:].decode())

The requests version forces you to parse Server-Sent Events yourself. The SDK hides that, at the cost of an extra dependency and some internal buffering.

Ergonomics

This is the SDK’s strongest argument. Autocomplete on completion.choices[0].message.content beats remembering nested dictionary keys. The SDK also handles authentication via environment variables (OPENAI_API_KEY), raises typed exceptions (openai.RateLimitError), and provides an async interface for asyncio services.

Raw requests makes you write error handling:

if resp.status_code != 200:
    raise RuntimeError(f"{resp.status_code}: {resp.text}")

You also manage timeouts manually—a common production outage cause. The SDK sets sane defaults and lets you override timeout and max_retries at the client level.

That said, requests has zero opinions. If you need to inject a custom x-request-id, route via a sidecar proxy, or test against a recorded cassette, you control every byte. The SDK can be subclassed, but you fight its defaults.

Ecosystem and limits

The OpenAI SDK tracks the vendor’s API closely. When OpenAI launches a new beta endpoint, the SDK usually ships support within days. The community builds extensions (LangChain, Haystack) that assume the SDK types.

requests is frozen in its mature state. It works everywhere Python runs, including minimal Lambda layers where every megabyte counts. But you must hand-roll support for new features like function-calling tool schemas or structured outputs—copying the JSON shape from docs.

Limits are mostly self-imposed. The SDK won’t help you route across Anthropic and OpenAI in one fallback chain; you’d need a gateway or your own logic. With requests, that’s just another if statement. Rate limiting is yours to implement either way unless the SDK’s retry catches a 429.

Which to choose

Use the OpenAI SDK when:

  • You integrate only with OpenAI (or a single compatible endpoint) and want fastest iteration.
  • You rely on typed responses, IDE support, and built-in async.
  • Your team is non-expert in HTTP minutiae and shouldn’t hand-write auth headers.
  • You need quick access to newest OpenAI-specific surfaces (assistants, fine-tuning jobs).

Use python requests (or httpx) when:

  • You route across multiple providers behind one OpenAI-compatible base URL and need to switch URLs at runtime.
  • You run in constrained environments (serverless, edge) where dependency size and cold-start time matter.
  • You require fine-grained control over headers, proxies, mocking, or SSE parsing.
  • You already have a HTTP layer and don’t want another vendor-coupled abstraction.

Hybrid pattern: Many production systems use the SDK for dev speed but drop to requests for a critical path that calls a gateway with automatic fallback when a provider is rate-limited or degraded. The gateway’s single endpoint means the SDK works unchanged, while requests scripts can probe health without importing the heavier package.

Pick the client that matches your deployment shape, not the one with the nicer README.

Tagspythonrequestsopenai-sdkcomparison

Written by

n4n Team

The team building n4n — a single OpenAI-compatible API in front of 240+ models, with automatic fallback, load balancing and pay-per-token metering.

More from n4n Team →

All python raw rest calls (requests/httpx) posts →