Rendering a request¶
Sync or async¶
Every supported request object goes through one of two calls:
to_curl()when the body is already in memory — the client-side request types, and the server-side ones whose framework buffers the body before your code runs (django,flask/werkzeug,tornado);to_curl_async()when the body has to be awaited —starlette/fastapi,aiohttp.web.Requestandaiohttp.ClientRequest.
httpx.Request and httpx2.Request work with both. Supported request objects lists which call fits which type.
import httpx
from curlify3 import to_curl, to_curl_async
req = httpx.Request("POST", "https://httpbin.org/post", json={"hello": "world"})
print(to_curl(req))
# curl -X POST -H 'host: httpbin.org' -H 'content-type: application/json' -d '{"hello":"world"}' https://httpbin.org/post
print(await to_curl_async(req)) # the same command, from async code
Passing an object to the wrong call raises UnsupportedRequestError, and the message says which call takes it:
unknown request object: starlette.requests.Request, it is accepted by to_curl_async(), use that instead
A request that has already been sent is usually still reachable: response.request for requests, niquests and httpx.
Readable output¶
pretty=True puts every option on its own line:
import requests
from curlify3 import to_curl
req = requests.Request("POST", "https://httpbin.org/post", json={"date": "2026-08-10"}).prepare()
print(to_curl(req, pretty=True))
# curl https://httpbin.org/post \
# -X POST \
# -H 'content-type: application/json' \
# -d '{"date": "2026-08-10"}'
The url moves to the first line, where curl reads it as well as at the end — the layout Chrome DevTools’ “Copy as cURL” uses. A request with no options stays on one line.
long_options=True spells the options out, --header instead of -H. The two are independent:
print(to_curl(req, pretty=True, long_options=True))
# curl https://httpbin.org/post \
# --request POST \
# --header 'content-type: application/json' \
# --data '{"date": "2026-08-10"}'
pretty=True is not available with shell="powershell", see Windows PowerShell.
What the command contains¶
The command carries what the request object holds, in this order: --http2 for httpx2, -g when the url needs globbing off, the method, the cookies as -b, the headers as -H, the body, and the url last (first with pretty=True).
Header names are lowercased. A repeated header is joined with
,, repeatedCookieheaders with;.Content-Lengthis left out:curlcomputes it from the body it sends.The method is left out for a GET without a body, which is what
curlsends by default. A GET with a body is spelled-X GET, since a body alone makescurlsend POST, and a HEAD is spelled-I.A body sent without a
Content-Typegetscontent-type: text/plain, socurldoes not add its own form-encoded one — except where the client itself sendsapplication/x-www-form-urlencoded, which the command then carries.
Payload handling covers how each kind of body is rendered, and Supported request objects what each library adds of its own.