yeah nice engineering clowns
The problem with httpx as a dependency is that it's currently working towards a 1.0 release which will be full of breaking changes.
The httpx2 project is essentially a fork that promises not to break the existing API, which makes it a more stable dependency to build against.
I wrote a pretty long comment about my concerns for the breaking 1.0 version last year - https://github.com/encode/httpx/discussions/3344#discussionc... - in that comment I recommended the HTTPX project release their 1.0 as a package called httpx2 instead, but a year later we now have an httpx2 (released by a different maintainer) that keeps the old API.
But what are the upsides of this change?
When I first read the title, I assumed this was some fancy new protocol that extends HTTP or something. But it's just about an SDK switching one transitive dependency with a fork? And arguably one that should just have its functionality be part of stdlib?
I (ok, Qwen 3.7 27B) wrote some benchmarking code to compare throughput, and niquests seems to be substantially more performant for traffic that doesn't need to traverse the public web, e.g. intra cluster service to service communication:
https://gist.github.com/jtbaker/61061d27949ef48ac31e85ff28c2...
uv run scripts/bench_http.py --count 1000 Benchmarking 1000 requests to http://localhost:8000/health (concurrency=16, timeout=30.0s) Date: 2026-08-28T10:18:34.480111
Running httpx... Running httpx2... Running niquests...
Metric httpx httpx2 niquests ------------------------------------------------------- mean 17.8ms 4.6ms 3.3ms min 3.2ms 1.5ms 1.2ms max 632.4ms 11.1ms 10.8ms p50 15.9ms 4.2ms 3.0ms p95 32.4ms 7.4ms 5.6ms p99 52.1ms 8.5ms 8.4ms total 1.15s 315.5ms 239.7ms throughput 869.2 req/s 3170.0 req/s 4171.1 req/s
Winner: niquests (4.8x faster by wall clock)
But yes, it doesn't support HTTP3. Here's the related issue: https://github.com/pydantic/httpx2/issues/92
Not that there is anything wrong with QUIC, at least its built by someone who knows what they are doing, unlike HTTP2
The httpx maintainer closed off access to issues and discussions on the repo, has been ignoring PRs, and hasn’t updated it in a half a year.
I don’t think there is any reason to consider httpx as a viable project any more. The Pydantic httpx fork has taken its place.
wtf is going on
But seriously, migrating between different libs should be ~free for both Anthropic and OpenAI.
I mean, obviously Python 2.7 is newer than Python 3.1
I remember swearing I would never migrate to python3 but here we are, I migrated without even feeling it or realizing when (But I do remember django played a big role).
> I've closed off access to issues and discussions.
> I don't want to continue allowing an online environment with such an absurdly skewed gender representation. I find it intensely unwelcoming, and it's not reflective of the type of working environments I value.
https://www.reddit.com/r/Python/comments/1rl5kuq/anyone_know...
[1] https://www.python-httpx.org/advanced/transports/#wsgi-trans...
Unfortunately that 1.0 work is happening in a private repository.
I mean really, closing repos? excluding maintainers? committing only on private repos?
This type of behavior should be moderated and banned from any FOSS activity to avoid exactly this situation of becoming a liability.
I've never seen a open source project page without a github link or a git clone ref like this https://www.encode.io/hx/ it is so ridiculous, wasn't that stuff on github? and isn't it anyway there due to over 1k forks?
what's the advantage of having this behavior? I see it only damaging towards the person, the project and the community.
Happens with the Linux kernel as well.
Minors and hotfixes allow you to release a new version for an older release (like if you released 3.7 but you found a bug present in 3.4, you can release 3.4.1 which would be newer than 3.7), making it possible to have multiple supported versions at the same time.
You shouldn’t assume anything about relative date of 2.7 and 3.1.
We started this fork because there was no activity on HTTPX, a very popular Python HTTP library.
A few weeks later, Pydantic started their own fork called HTTPX2. We decided to embrace this and support HTTPX2. We're upstreaming our fixes to HTTPX2 and in our opinion it should be the "blessed" fork. Pydantic can make this more successful than we ever can.
See also https://tildeweb.nl/~michiel/httpx2.html
Thanks for all your support!
Sander & Michiel
Unless OpenAI deprecates the HTTP endpoint, you can pretty much ignore the whole SDK packages.
Nobody is going to get fired for using http1.1
if you move to http3 and a bunch of clients cant use your site, then you're gonna get yelled at.
That’s even more reason to consider it non-viable.
There is no reason to encourage or support this type of behavior in a project. It is their right to do with it as they please, but I’m not interested in a project that functions like this. The forks are continuing to operate like true community projects.
I mean really, closing repos? excluding maintainers? committing only on private repos?
This type of behavior should be moderated and banned from any FOSS activity to avoid exactly this situation of becoming a liability.
I've never seen a open source project page without a github link or a git clone ref like this https://www.encode.io/hx/ it is so ridiculous, wasn't that stuff on github? and isn't it anyway there due to over 1k forks?
what's the advantage of having this behaviour? I see it only damaging towards the person, the project and the community.
The OpenAI Python SDK now uses HTTPX2 for its
synchronous and asynchronous HTTP clients. HTTPX2 is installed automatically
with openai; the previous httpx package is not. This guide explains what
changes for applications that interact with the SDK's HTTP layer.
If you construct an OpenAI or
AsyncOpenAI client without providing http_client, your existing API calls,
parsed response models, streaming APIs, authentication, retries, and numeric
timeouts continue to work:
from openai import OpenAI
client = OpenAI(timeout=30.0)
response = client.responses.create(model="gpt-5.5", input="Hello")
No HTTPX2 extra or separate installation is required:
pip install openai
If your application imported httpx only because an earlier SDK installed it
transitively, add your own httpx dependency or migrate those imports to
httpx2. Installing the SDK no longer installs httpx for you.
HTTPX2 changes the default TLS trust store, including for applications that
use the SDK's default HTTP client. HTTPX previously verified certificates
against the CA bundle provided by certifi. HTTPX2 instead uses the
operating-system trust store, and the SDK no longer installs certifi.
This can break certificate verification in minimal container images without
system CA certificates, environments using corporate TLS-inspecting proxies,
and deployments that relied on a custom or modified certifi bundle. Install
the required CA certificates in the operating-system trust store, or configure
an explicit certificate bundle:
export SSL_CERT_FILE=/path/to/ca-bundle.pem
Alternatively, configure a directory of trusted CA certificates:
export SSL_CERT_DIR=/path/to/ca-directory
These environment variables are honored when trust_env=True, which is the
default. To control trust explicitly on a custom client, pass an
ssl.SSLContext through verify:
import ssl
from openai import OpenAI, DefaultHttpx2Client
ssl_context = ssl.create_default_context(cafile="/path/to/ca-bundle.pem")
client = OpenAI(http_client=DefaultHttpx2Client(verify=ssl_context))
Use DefaultAsyncHttpx2Client(verify=ssl_context) for the equivalent async
configuration. The SDK's aiohttp transport uses the same HTTPX2 TLS settings.
Use HTTPX2 clients and HTTPX2 configuration objects. The SDK provides helpers that preserve its recommended timeout, connection-pool, and redirect defaults:
import httpx2
from openai import OpenAI, AsyncOpenAI, DefaultHttpx2Client, DefaultAsyncHttpx2Client
proxy_client = OpenAI(http_client=DefaultHttpx2Client(proxy="http://proxy.example.com:8080"))
transport_client = OpenAI(
http_client=DefaultHttpx2Client(
transport=httpx2.HTTPTransport(local_address="0.0.0.0"),
timeout=httpx2.Timeout(30.0, connect=5.0),
)
)
async_client = AsyncOpenAI(http_client=DefaultAsyncHttpx2Client(timeout=httpx2.Timeout(30.0)))
Directly constructed httpx2.Client and httpx2.AsyncClient instances are
also supported. When you construct a client directly, its own HTTPX2 defaults
apply unless you configure them yourself.
The existing DefaultHttpxClient and DefaultAsyncHttpxClient names continue
to work, but now construct HTTPX2 clients. Prefer DefaultHttpx2Client and
DefaultAsyncHttpx2Client when making the HTTP client family explicit.
Module-level configuration follows the same rule:
import openai
openai.http_client = openai.DefaultHttpx2Client()
Replace HTTPX-specific objects with the corresponding HTTPX2 objects:
| Previous object | HTTPX2 object |
|---|---|
httpx.Client |
httpx2.Client |
httpx.AsyncClient |
httpx2.AsyncClient |
httpx.Timeout |
httpx2.Timeout |
httpx.URL |
httpx2.URL |
httpx.Limits |
httpx2.Limits |
httpx.HTTPTransport |
httpx2.HTTPTransport |
httpx.AsyncHTTPTransport |
httpx2.AsyncHTTPTransport |
httpx.MockTransport |
httpx2.MockTransport |
For example, a granular SDK timeout becomes:
import httpx2
from openai import OpenAI
client = OpenAI(timeout=httpx2.Timeout(60.0, connect=5.0, read=20.0))
Numeric timeout values do not change. Existing string URLs do not change. Custom transport subclasses, mounted transports, proxy integrations, and connection-pool instrumentation must target HTTPX2's transport interfaces.
Authentication handlers and hooks receive HTTPX2 request and response objects. Update custom auth classes and annotations accordingly:
import httpx2
from openai import OpenAI, DefaultHttpx2Client
def log_request(request: httpx2.Request) -> None:
print(request.method, request.url)
client = OpenAI(http_client=DefaultHttpx2Client(event_hooks={"request": [log_request]}))
If you subclass an HTTP authentication or transport interface, subclass the
matching httpx2 class. Third-party instrumentation, tracing middleware, and
auth integrations must explicitly support HTTPX2.
Parsed SDK response models are unchanged. When using a native HTTPX2 client, transport-facing objects belong to HTTPX2:
import httpx2
from openai import OpenAI
client = OpenAI()
response = client.models.with_raw_response.list()
assert isinstance(response.http_response, httpx2.Response)
assert isinstance(response.http_request, httpx2.Request)
With a native client, use cast_to=httpx2.Response when requesting an unparsed
HTTP response. Streaming response wrappers also expose HTTPX2 response objects.
Application code should usually catch SDK exceptions such as
openai.APITimeoutError and openai.APIConnectionError; with a native client,
an exception's underlying transport cause is an HTTPX2 exception.
These type guarantees apply only to native HTTPX2 clients. An injected legacy
HTTPX client produces httpx.Request, httpx.Response, and HTTPX transport
exceptions instead, even if cast_to=httpx2.Response is supplied.
The supported aiohttp extra uses an HTTPX2-native transport. It does not
install legacy HTTPX or the external httpx-aiohttp adapter:
pip install 'openai[aiohttp]'
from openai import AsyncOpenAI, DefaultAioHttpClient
client = AsyncOpenAI(http_client=DefaultAioHttpClient())
DefaultAioHttpClient() is an httpx2.AsyncClient. Applications using this
helper do not need to construct or import the transport directly.
Mocks must intercept HTTPX2 requests and return HTTPX2 responses. For example:
import httpx2
from openai import OpenAI
def handler(request: httpx2.Request) -> httpx2.Response:
return httpx2.Response(
200,
request=request,
json={"object": "list", "data": []},
)
client = OpenAI(http_client=httpx2.Client(transport=httpx2.MockTransport(handler)))
assert client.models.list().data == []
If your test suite uses RESPX, update to an HTTPX2-compatible RESPX version or fork. A RESPX version that patches only legacy HTTPX cannot intercept the SDK's default HTTPX2 client. If you cannot migrate that integration immediately, the temporary legacy-client escape hatch below lets existing HTTPX-only RESPX setups continue to work while you migrate.
Applications that depend on an HTTPX-only transport, integration, or mocking library can explicitly install legacy HTTPX and inject a legacy client:
pip install openai httpx
Legacy HTTPX support is runtime-only. The SDK's public type annotations
accept HTTPX2 clients, so passing a legacy client directly fails static type
checking in mypy, Pyright, and similar tools. Use cast(Any, ...) or a
targeted type-ignore when deliberately choosing this compatibility path:
from typing import Any, cast
import httpx
from openai import OpenAI
client = OpenAI(http_client=cast(Any, httpx.Client()))
The asynchronous form requires the same workaround:
from typing import Any, cast
import httpx
from openai import AsyncOpenAI
client = AsyncOpenAI(http_client=cast(Any, httpx.AsyncClient()))
Legacy clients preserve the HTTPX request, response, and exception families.
Request raw responses as httpx.Response, using the same type-checking
workaround for the legacy response class:
from typing import Any, cast
import httpx
from openai import OpenAI
client = OpenAI(http_client=cast(Any, httpx.Client()))
response = client.get("/models", cast_to=cast(Any, httpx.Response))
assert isinstance(response, httpx.Response)
Passing cast_to=httpx2.Response does not convert a legacy HTTPX response into
an HTTPX2 response. Install and maintain the legacy dependency yourself.
Legacy HTTPX support is provided as a migration aid and may be discontinued.
If you must retain an existing httpx-aiohttp integration, install it
explicitly and inject its legacy client:
pip install openai httpx-aiohttp
from typing import Any, cast
from httpx_aiohttp import HttpxAiohttpClient
from openai import AsyncOpenAI
client = AsyncOpenAI(http_client=cast(Any, HttpxAiohttpClient()))
This path is covered by dedicated compatibility tests, including a real
request through the aiohttp transport, but remains a temporary escape hatch.
Prefer openai[aiohttp] and DefaultAioHttpClient() for new code.
They also effectively killed off MkDocs: https://github.com/mkdocs/mkdocs/discussions/3677
He doesn't owe those developers anything, of course, but likewise they do not owe him loyalty and thus since an open fork exists, his project will die.
Killing the project is also his right, but people also have a right to be upset by his passive aggressive behavior. Just announce that you're shutting down the project and move on, don't waste our time.
Users of the project can (and have been) moving to forks that are operated as open, community projects in the continued spirit of the project’s original intention.
For what it’s worth, the maintainer has stated their reasons and they are not related to AI or scraper bots.
I have no idea what that means other this person might not be the easier to work with, or get along with.
Do they?