Open-source project
imroc/req avatar
imroc/req

imroc/req: a Go HTTP client that impersonates browsers and speaks HTTP/3

Simple Go HTTP client with Black Magic

4,870 stars413 forksGoMIT

At a glance

What is it?
imroc/req wraps net/http with chainable clients, automatic retries, HTTP fingerprint impersonation and an exportable Transport. It suits Go teams that need browser-like requests or quick API tests, and it is a poor fit for anyone who wants zero third-party dependencies.
Who is it for?
Adopt imroc/req if you scrape pages that reject default Go clients, or if you want retries, tracing and HTTP/3 without assembling them yourself. Stay with net/http or a thin wrapper if your dependency budget is tight, since req pulls in quic-go, utls, brotli and x/net.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 20 days ago.
What is it written in?
Mainly Go, according to GitHub's language statistics.

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

Editorial analysis

What imroc/req solves for Go HTTP callers

Plain net/http gives you a client, a request and a response, and nothing else. Retries, decoding, progress reporting and protocol negotiation are left to the caller. imroc/req is a wrapper around that stack which bundles those concerns behind chainable methods. The README describes it as a "Simple Go HTTP client with Black Magic", and the feature list backs the claim: client-level and request-level settings, automatic retry, automatic marshalling and unmarshalling based on Content-Type, Basic, Bearer and Digest authentication, file download with progress callbacks, and middleware at four levels (request, response, client, transport).

The audience is Go engineers who write a lot of outbound HTTP. Two groups get the most from it. The first is anyone doing API testing or scripting, because the package name itself can act as a client and a request, so a GET is one line. The second is anyone hitting sites that fingerprint TLS and HTTP/2 characteristics to block non-browser clients. The repository contains client_impersonate.go and the module depends on github.com/refraction-networking/utls, which is the mechanism behind that feature. If your problem is a 403 from a bot filter, this is the part of the library that matters.

How the client, request and transport layers fit together

The architecture follows a three-level split that the README makes explicit. At the bottom is req.Transport, which the README says is exportable and can replace the Transport of an existing http.Client. It adds HTTP/3, content dumping and middleware on top of what http.Transport does. Above that sits the Client, created with req.C(), which holds defaults shared across requests. On top is the Request, created with client.R(), which carries per-call settings and executes with Get, Post and the rest.

The data flow is conventional until it is not. A request is built, middleware runs, the transport picks a protocol, the response comes back, and decode.go handles turning the body into a Go value. The interesting parts are the branches. client_impersonate.go swaps in a uTLS-based handshake so the TLS ClientHello matches a chosen browser profile. roundtrip_js.go and transport_default_wasm.go exist for WebAssembly builds, and transport_default_other.go covers everything else, which tells you the transport is deliberately platform-aware. parallel_download.go and ratelimit.go are separate files rather than options buried in the client, which suggests they are opt-in features with their own state.

One design decision deserves scrutiny. The README recommends creating an explicit client in production rather than using the global wrapper methods, and the basic usage example leans entirely on the global form. That is a reasonable split for a library that wants a one-line demo, but it means the first code most people copy is the pattern the README later tells them not to use.

Installing imroc/req and making a first real request

The README states that Go 1.24 or later is required, and the module file declares go 1.25.0, so check your toolchain before you start. Installation is a single go get against the v3 module path:

bash
go get github.com/imroc/req/v3

Then import it and send a request. The README's simple GET example creates a client with req.C(), builds a request with client.R(), and calls Get. The response body is printed directly because the library decodes it for you:

go
package main

import (
	"fmt"
	"github.com/imroc/req/v3"
	"log"
)

func main() {
	client := req.C() // Use C() to create a client.
	resp, err := client.R(). // Use R() to create a request.
		Get("https://httpbin.org/uuid")
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println(resp)
}

The README shows the expected output as a JSON object containing a uuid field, printed without an explicit decode call. For development, the README demonstrates req.DevMode(), which turns on debug logging, and req.MustGet, which treats the package name as a request and panics on error instead of returning one. The example output shows the full request and response headers for an HTTP/2 call, followed by the same request forced to HTTP/1.1 with req.EnableForceHTTP1(). That force switch is worth knowing about early, because it is the fastest way to tell whether a failing endpoint is a protocol negotiation problem or something else.

Where imroc/req is the wrong tool

The dependency list is the first real cost. A go get of req brings in quic-go for HTTP/3, utls for fingerprint impersonation, brotli and klauspost/compress for content encoding, go-querystring, icholy/digest and golang.org/x/net and x/text. If your project is a small CLI or a library that other people import, that is a meaningful expansion of your module graph, and the HTTP/3 and impersonation code is dead weight unless you use it. A team that needs only GET and JSON decoding gets more value from net/http plus encoding/json.

The second limitation is the one the README itself hints at. The global wrapper methods are convenient and the README explicitly says production code should create an explicit client instead. Libraries that encourage a global default client can end up with shared state that is hard to reason about, and req is no exception here; the recommendation exists because the global path uses a default client behind the scenes.

Third, HTTP fingerprint impersonation is inherently a moving target. It depends on uTLS and on browser profiles that the library has to maintain as browsers change. The README links to a documentation page for the feature but does not describe how profiles are updated or how quickly. If your use case depends on a specific browser version's fingerprint, that is a maintenance relationship you are entering, not a one-time configuration.

How imroc/req differs from net/http and resty

The honest comparison is against the standard library, because that is what req wraps. net/http gives you no retry policy, no automatic body decoding, no progress callbacks and no middleware. You can build all of it, and many teams do, but the code is the same every time. req's value proposition is that this code already exists and is exposed through chainable methods.

Against resty, which appears in the search terms people use around this project, the visible difference in this material is scope. req's feature list includes HTTP/3, TLS fingerprint impersonation via uTLS, and an exportable Transport that can be dropped into an existing http.Client. Those three are not standard fare for a convenience wrapper. The README's Exportable bullet is the one that matters most for incremental adoption: you can keep your existing http.Client and swap only the Transport, which means you do not have to rewrite request construction to get HTTP/3 or content dumping.

That exportable transport is also the strongest argument for req over rolling your own. Replacing a Transport is a small diff. Reimplementing retry with backoff, brotli decoding, HTTP/3 negotiation and request dumping is not.

Maintenance, licensing and upgrade cost

The repository is not archived, and the last push was on 2026-09-11. Releases have been frequent: v3.61.0 on 2026-08-13, v3.60.0 on 2026-07-30 and v3.59.0 on 2026-07-02. That cadence means you should expect to move versions rather than pin one for years, and it also means bug fixes arrive quickly.

The licence is MIT, which is permissive and imposes few obligations beyond preserving the copyright notice and licence text. This is not legal advice; check the LICENSE file in the repository and your own organisation's policy before shipping.

The upgrade cost is concentrated in the module path. The import is github.com/imroc/req/v3, so major-version changes arrive as a new path rather than a breaking change inside the same import. Within v3, the API is built from chainable methods, which tends to make additions non-breaking. The riskier surface is the impersonation feature, where the behaviour depends on external browser profiles and on the uTLS version pinned in go.mod. Upgrading req can change which fingerprint you present, and that is the kind of change that shows up as a new 403 rather than a compile error.

Editorial conclusion

Adopt imroc/req if you scrape pages that reject default Go clients, or if you want retries, tracing and HTTP/3 without assembling them yourself. Stay with net/http or a thin wrapper if your dependency budget is tight, since req pulls in quic-go, utls, brotli and x/net. Before committing, check that your toolchain meets the Go 1.24+ requirement stated in the README, and confirm the impersonation profiles you need are listed on req.cool.

Frequently asked questions

What Go version does imroc/req require?

The README states that Go 1.24 or later is required, and the module file declares go 1.25.0. Check your toolchain before running go get github.com/imroc/req/v3.

How do I install imroc/req?

Run go get github.com/imroc/req/v3 and then import github.com/imroc/req/v3 in your code. The README shows this as the only installation step.

Can imroc/req replace the Transport in an existing http.Client?

The README states that req.Transport is exportable and can directly replace the Transport of an http.Client, adding HTTP/3, content dumping and middleware with minimal code change.

Does imroc/req support HTTP/3?

Yes. The feature list includes HTTP/1.1, HTTP/2 and HTTP/3, with automatic detection of the server side and an option to force a specific protocol. The module depends on github.com/quic-go/quic-go for the HTTP/3 path.

Official sources

  1. imroc/req on GitHub
  2. License: MIT
  3. Project website
  4. README
  5. Releases
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/imroc-req.svg)](https://hysenlabs.com/projects/imroc-req)