Every part of a cURL command maps cleanly onto Python's requests library: -X sets the method (requests.post), -H becomes headers={}, a JSON -d payload becomes json={}, form data becomes data={}, -u becomes auth=(), and query params become params={}. Fastest way: paste it into an online cURL → Python converter. To do it by hand, use the flag-by-flag table below — I ran both the cURL command and the generated Python against a live echo server and they produced identical results.
You copy a working curl command from an API's docs (or from your browser's "Copy as cURL"), and now you need it as Python. The good news: the translation is almost mechanical, because every cURL flag has a direct equivalent in the requests library. Once you've seen the mapping, you can convert any command in seconds.
Below is the complete flag-by-flag map, the two most common real cases (a JSON POST and a GET with params), and — because this is meant to be trustworthy — proof that the converted Python behaves exactly like the original cURL, tested against a live server.
The flag-by-flag map (cURL → requests)
This table covers virtually every cURL command you'll meet. Learn it once and the conversion becomes automatic:
| cURL flag | requests equivalent | What it does |
|---|---|---|
-X POST | requests.post(...) | HTTP method |
-H "K: V" | headers={"K": "V"} | Request header |
-d '{"a":1}' (JSON) | json={"a": 1} | JSON body (sets Content-Type) |
-d "a=b&c=d" | data={"a": "b", "c": "d"} | Form-encoded body |
-G -d "q=x" | params={"q": "x"} | Query-string params |
-u user:pass | auth=("user", "pass") | Basic auth |
-b "k=v" | cookies={"k": "v"} | Cookies |
-F "file=@f.png" | files={"file": open("f.png","rb")} | Multipart upload |
-A "agent" | headers={"User-Agent": "agent"} | User agent |
-L | allow_redirects=True (default) | Follow redirects |
-k / --insecure | verify=False | Skip TLS verification |
The single most common mistake: using data= for a JSON body. Use `json=` — it serializes the dict *and* sets Content-Type: application/json for you. Use data= only for form-encoded (application/x-www-form-urlencoded) bodies.
Example 1 — a JSON POST with headers (tested)
Here's a typical authenticated API call in cURL:
curl -X POST https://postman-echo.com/post \-H "Content-Type: application/json" \-H "Authorization: Bearer tok_123" \-d '{"name":"mithun","plan":"pro"}'
And the direct requests translation — -X POST → requests.post, the two -H flags → headers={}, the JSON -d → json={}:
import requestsr = requests.post("https://postman-echo.com/post",headers={"Content-Type": "application/json","Authorization": "Bearer tok_123",},json={"name": "mithun", "plan": "pro"},)print(r.status_code, r.json()["json"])
I ran both against the live echo server. They returned the same thing — the server saw an identical body and auth header either way:
# curl -> body: {'name': 'mithun', 'plan': 'pro'} | auth: Bearer tok_123# requests -> 200 {'name': 'mithun', 'plan': 'pro'} | auth: Bearer tok_123
Example 2 — a GET with query params
Query strings are where hand-conversion trips people up. Don't paste ?q=python&page=2 into the URL — pass a params dict and let requests build the query string (and URL-encode it) for you:
# curl "https://postman-echo.com/get?q=python&page=2" -A "my-app/1.0"import requestsr = requests.get("https://postman-echo.com/get",params={"q": "python", "page": "2"},headers={"User-Agent": "my-app/1.0"},)print(r.json()["args"]) # -> {'q': 'python', 'page': '2'}
Tested: the server echoed {'q': 'python', 'page': '2'} and the custom User-Agent came through — exactly as the cURL version. Using params also means special characters get encoded correctly, which is easy to get wrong by hand.
The fast way: an online converter
When you just want the Python and don't want to think about flags, paste the whole command into the free cURL to Python converter. It parses the command — method, headers, body, auth, params — and returns ready-to-run requests code. It runs in your browser, so the command (which often contains tokens) never leaves your machine.
Whichever method you use, watch for secrets: cURL commands copied from docs or DevTools frequently contain real API keys, cookies, or bearer tokens. Don't paste those into random web tools, and don't commit the converted code with the token inline — load it from an environment variable instead. (See how to protect API keys in Python.)
Gotchas worth knowing
- JSON vs form:
-d '{...}'is *usually* JSON → usejson=. But-d "a=b&c=d"is form data → usedata=. cURL doesn't distinguish; you have to. - `requests` follows redirects by default — cURL does not unless you pass
-L. If you're matching cURL exactly, addallow_redirects=False. - Cookies and sessions: for multiple calls that share cookies/auth, use a
requests.Session()instead of repeating headers on every call. - File uploads:
-F "file=@photo.png"becomesfiles={"file": open("photo.png", "rb")}— and don't also setContent-Typeby hand;requestssets the multipart boundary for you. - `--insecure`/`-k` maps to
verify=False, but only use it for local testing — it disables TLS verification.
Convert your cURL command — free
Paste a cURL command and get ready-to-run Python requests code. In your browser, nothing uploaded, no signup.
Open the cURL → Python toolFree tools mentioned here
Frequently asked questions
How do I convert a cURL command to Python?
Map each cURL flag to its requests equivalent: -X sets the method (requests.post), -H becomes headers={}, a JSON -d body becomes json={}, form data becomes data={}, -u becomes auth=(), and query params become params={}. Or paste the command into an online cURL-to-Python converter to get ready-to-run requests code instantly.
Should I use json= or data= in requests?
Use json= for a JSON body — it serializes the dict and automatically sets Content-Type: application/json. Use data= only for form-encoded bodies (a=b&c=d) or raw strings. Using data= with a Python dict sends form-encoded data, not JSON, which is the most common conversion bug.
How do I convert curl query parameters to requests?
Pass a params dict to requests rather than putting the query string in the URL: requests.get(url, params={'q': 'python', 'page': '2'}). requests builds and URL-encodes the query string for you, which avoids encoding mistakes you'd make appending ?q=...&page=... by hand.
Does requests follow redirects like curl?
By default requests follows redirects automatically, whereas curl only follows them when you pass -L. If you need to match a plain curl command exactly (no -L), set allow_redirects=False; if the curl had -L, the default requests behavior already matches.
Is it safe to use an online curl-to-python converter?
It's fine for commands without secrets. But cURL commands copied from API docs or browser DevTools often contain real tokens, cookies, or API keys — prefer a converter that runs client-side (in your browser) so the command isn't uploaded, and never commit the generated code with the token inline; load it from an environment variable instead.