Library / SDK
twisted/treq avatar
twisted/treq

treq: a requests-style HTTP client that does not block the Twisted reactor

Python requests like API built on top of Twisted's HTTP client.

603 stars139 forksPythonNOASSERTION

At a glance

What is it?
treq gives Twisted applications a requests-like HTTP API on top of Twisted's Agent, with a coroutine-first interface. The trade is deliberate: you get the familiar call shape and lose the blocking semantics that make requests easy to reason about in a script.
Who is it for?
Adopt treq when your application is already built on Twisted and you want HTTP calls that fit the reactor instead of blocking it, and skip it if you are writing a script or a service that has no event loop, because treq buys you nothing over requests there and costs you an async rewrite.
Can I use it commercially?
Check first. The repository uses a licence we do not classify automatically, so read its LICENSE file before any commercial use.
Is it still maintained?
Yes. The repository last received commits 65 days ago.
What is it written in?
Mainly Python, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 22, 2026, and from our analysis. They are not legal advice.

Editorial analysis

What treq is, and what it is not

treq is described as an HTTP library inspired by requests but written on top of Twisted's Agents. The README puts it plainly: it provides a simple, higher level API for making HTTP requests when using Twisted.

That word when does a lot of work. treq is not a drop-in replacement for requests in the general case. It is the HTTP client for code that is already inside a Twisted reactor, and its entire purpose is to make outbound HTTP calls that do not freeze the event loop.

The homepage is https://treq.readthedocs.org/, the default branch is trunk, the primary language is Python, and the project is hosted under the twisted organisation, with David Reid listed as author and Tom Most as maintainer. The repository root also holds a SECURITY.md, a CONTRIBUTING.rst, a changelog.d/ directory, tox.ini and a .coveragerc, which is the shape of a long-running project with real release discipline rather than a weekend library.

The design brief is worth restating because it explains every API decision: familiar call shapes, non-blocking behaviour. Everything else is in service of those two.

The example shows why the API is await-based

The README's example is a complete interaction, and it is short enough to take apart line by line.

python
    >>> import treq

    >>> async def main(reactor):
    ...     response = await treq.get("https://github.com")
    ...     print(response.code)
    ...     body = await response.text()
    ...     print("<!DOCTYPE html>" in body)

    >>> from twisted.internet.task import react
    >>> react(main)
    200
    True

Three things stand out.

First, the function is a coroutine. The body awaits treq.get, so the HTTP request yields to the reactor instead of blocking the thread. That is the entire difference from requests in a line.

Second, the response is not the body. treq.get returns a response object with the code, and the body has to be awaited separately through response.text(). This is the Deferred model: the headers arrive first, and the body streams after, and a caller who does not consume the body has to deal with the connection lifecycle itself. In requests, the body has already arrived by the time you look at the object.

Third, the entry point is twisted.internet.task.react, which takes the coroutine function and runs it against a reactor. The reactor argument is in the signature because react passes it in.

The output shows the shape of the result: 200 for the status code, and True for the membership test on the decoded body.

Dependency choices reveal what treq had to solve

The dependency list in pyproject.toml is short and each entry answers a question.

Twisted[tls] at 22.10.0 or newer is the base, and the tls extra is there because a comment points at a specific Twisted issue number, so TLS support is not optional in practice for an HTTP client.

hyperlink at 21.0.0 or newer handles URL parsing and manipulation, which is not a trivial thing to do correctly across the URI, URL and IRI specifications, and it is the same library Twisted itself uses.

incremental at 24.7.2 or newer is Twisted's own version management scheme, and it appears twice: once as a build requirement and once as a runtime dependency. That is a signal about how the project is versioned, since incremental is what lets a Twisted project release a new point release without a full version bump of the framework.

attrs and typing_extensions handle the data classes and the typing backports, and multipart handles multipart form encoding, which is a feature requests has and which treq would not have been acceptable without.

The optional dependency groups are worth a look too. The dev group includes httpbin at 0.10.2 and werkzeug at 3.1.8, which together provide a local HTTP server to test against, and it pins both to exact versions. The docs group is sphinx with the read the docs theme. The project classifiers list Python 3.9 through 3.14, CPython and PyPy, and requires-python is 3.9.0 or newer.

Calendar versioning and how releases are cut

The versioning scheme is stated on a badge in the README: calver, YY.MM.MICRO, following the calver.org specification. The release names in the repository follow the same pattern, with treq-26.7.0, treq-25.5.0 and treq-24.9.1 as the recent ones, all in July 2026.

This is a meaningful choice for a library that other people's services depend on. A calendar version carries the release date in the number itself, so a version conflict tells you not just that the versions differ but roughly how far apart they are, and the micro component increments for a patch without a date change.

The project version is dynamic in pyproject.toml, which means it is not written in the file by hand, and the build requirement on incremental is what generates it. Combined with the changelog.d/ directory in the repository root, that is a towncrier-style workflow where each pull request adds a news fragment and the release is assembled from them, rather than a maintainer editing a changelog by hand.

CHANGELOG.rst is the published history and the changelog.d/ directory holds the pending fragments. The project is not archived, the last push was on 2026-08-19, and the release names show two point releases cut close together in July 2026, which is what a project with a steady trickle of fixes looks like.

The cost of the async model, stated plainly

The main limitation is not a bug but a consequence, and it is worth being direct about it before anyone adopts the library.

With requests, the pattern is synchronous and the whole program is easier to read: the request happens, you have the body, you process it. With treq, the request returns a Deferred, the body is a separate await, and the body may not be fully read unless you consume it. Code that has to run under a reactor, with a timeout on a slow endpoint, with a retry, and with several requests in flight, is where the value shows. Code that fetches one URL and exits is where the value is negative, because you now have an event loop to run and a coroutine to write for what used to be two lines.

There is a second, subtler issue that catches people. Because the body is streamed, a response you do not fully consume can hold the connection open. In requests the same problem exists but is hidden by the fact that the body is already in memory. treq makes it visible, which is better, but it means the calling code has to be written with connection lifetimes in mind, and the documentation has to be read carefully on that point.

A third constraint is the Twisted version floor. Twisted[tls] 22.10.0 or newer is required, so treq cannot be dropped into an environment pinned to an older Twisted, and because treq shares the reactor with the rest of your application, an upgrade of Twisted is an upgrade of treq's assumptions too. The reverse also holds: the version of Twisted you run is a version of the HTTP client you run.

Finally, treq is a client only. The repository is scoped to outbound HTTP, so anything requiring an inbound server, a proxy, or a protocol other than HTTP and HTTPS is out of scope by design.

How treq compares with requests and with the raw Agent

The comparison that matters is against two alternatives, and treq sits between them.

Against requests, the difference in approach is the execution model. requests is blocking, and it works by running its own event loop under the hood in a worker thread when you use it from an async context. treq uses the reactor you already have. If your application is Twisted, treq avoids a second event loop, avoids a thread per request, and lets the request participate in the same cancellation, timeout and scheduling story as everything else in the process. If your application is not Twisted, treq offers nothing here.

Against using Twisted's Agent directly, the difference is ergonomics. Agent is the low level transport, and it gives you a Deferred of a response, with the body as a separate stream and the same lifecycle concerns. treq is the layer above it that gives you the request-like call shape, the response object with a code, the text() and content() helpers, and the multipart support, so that the common case is a few lines rather than a state machine.

That is the same trade a lot of libraries make, and it cuts both ways. treq removes the boilerplate and also removes your ability to do something the library does not model, at which point you drop down to Agent anyway. The rule of thumb is that if you find yourself wanting to reach past treq for most calls, you would have been better off with Agent directly.

Editorial conclusion

Adopt treq when your application is already built on Twisted and you want HTTP calls that fit the reactor instead of blocking it, and skip it if you are writing a script or a service that has no event loop, because treq buys you nothing over requests there and costs you an async rewrite. Before you commit, check that the Twisted version in your environment is at least 22.10.0, since that is the declared floor, and note the last push was on 2026-08-19 with release 26.7.0 in July 2026.

Frequently asked questions

What is treq and how is it different from requests?

treq is a high-level Twisted HTTP client inspired by requests but built on Twisted's Agent. The difference in execution model is that requests blocks while treq returns a Deferred, so the response body has to be awaited separately and the request never blocks the reactor.

How do I make a request with treq?

Call treq.get inside a coroutine and await it, then await response.text() to read the body. The entry point in the documentation example is twisted.internet.task.react, which runs the coroutine function against a reactor.

What versions of Python and Twisted does treq support?

requires-python is 3.9.0 or newer, with classifiers for Python 3.9 through 3.14 on both CPython and PyPy. The runtime dependency is Twisted[tls] at 22.10.0 or newer, so the Twisted version floor is 22.10.0.

How is treq versioned and how are releases made?

treq follows calendar versioning in the form YY.MM.MICRO, with recent releases such as treq-26.7.0, treq-25.5.0 and treq-24.9.1. The version is dynamic in pyproject.toml and generated through the incremental build requirement, with a changelog.d/ directory holding pending news fragments.

Does treq support multipart form data?

Yes. The dependency list includes multipart alongside Twisted with TLS, hyperlink, incremental, attrs and typing_extensions, so form encoding of the kind requests supports is available.

Official sources

  1. Issues
  2. Project website
  3. README
  4. Releases
  5. twisted/treq on GitHub
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/twisted-treq.svg)](https://hysenlabs.com/projects/twisted-treq)

Was this useful?