To convert a cURL command to Python, preserve its HTTP method, URL, query string, headers, cookies, authentication, body, file uploads, redirects, TLS settings and timeout. For ordinary requests, Python’s requests library maps these parts directly: use params= for query parameters, headers= for headers, cookies= for cookies, data= for form or raw data, json= for JSON objects, files= for multipart uploads and auth= for Basic authentication.
Install Requests and read the cURL command completely
The examples below use Requests 2.34.2 documentation, which identifies Python 3.10+ as officially supported. Check the current project documentation if your environment uses a newer release. Install it with:
python -m pip install requests
Do not translate only the URL. Before writing Python, identify every meaningful cURL option:
- Method: GET, POST, PUT, PATCH, DELETE or another method.
- URL and query parameters, including repeated parameters.
- Headers such as
Authorization,AcceptandContent-Type. - Cookies, form fields, JSON, raw bytes or uploaded files.
- Authentication, redirects, proxies, compression, TLS verification and certificates.
- Output behavior: whether cURL writes a response body to a file, prints headers or follows redirects.
Shell quoting and variable expansion also matter. A quoted JSON string, an environment variable and a local file reference each require a different Python representation.
#1 Best Overall
Basic GET: the direct conversion
cURL:
curl https://api.example.com/users
Python:
import requests
response = requests.get("https://api.example.com/users", timeout=30)
response.raise_for_status()
print(response.text)
raise_for_status() makes HTTP 4xx and 5xx responses visible. Without it, a request can complete at the transport level while the server has rejected it.
Save binary output instead of printing it
import requests
response = requests.get("https://example.com/archive.zip", timeout=60)
response.raise_for_status()
with open("archive.zip", "wb") as output:
output.write(response.content)
Use response.content for bytes and response.text for decoded text. For a JSON response, call response.json() only after checking the status.
Map methods, parameters and headers
| cURL intent | Requests equivalent |
|---|---|
| GET | requests.get(url, ...) |
| POST, PUT, PATCH or DELETE | The matching convenience method, or requests.request("METHOD", url, ...) |
?page=2&limit=20 |
params={"page": 2, "limit": 20} |
-H "Name: value" |
headers={"Name": "value"} |
-b "session=abc" |
cookies={"session": "abc"} |
-u user:password |
auth=("user", "password") |
Query parameters
Keep the base URL separate from parameters so Requests performs URL encoding:
import requests
params = {"q": "red shoes", "page": 2, "tag": ["sale", "new"]}
response = requests.get(
"https://api.example.com/search",
params=params,
timeout=30,
)
response.raise_for_status()
print(response.url)
print(response.json())
Print response.url while verifying a conversion. It shows the encoded URL that was actually sent. For repeated keys, a list value is appropriate; confirm that the destination API accepts the resulting representation.
Free tools Windows power users keep installed
One-click scans. No signup required.
Headers
import requests
headers = {
"Accept": "application/json",
"Authorization": "Bearer YOUR_TOKEN",
}
response = requests.get(
"https://api.example.com/profile",
headers=headers,
timeout=30,
)
response.raise_for_status()
Do not hard-code real credentials in source control. Read secrets from environment variables or your secret manager. Header names are case-insensitive, but values and spacing are not always interchangeable.
POST bodies: form data, JSON and raw content
Form-encoded fields
For cURL form fields such as -d "name=Sam" -d "role=admin", use data=:
Rank #2
import requests
response = requests.post(
"https://api.example.com/users",
data={"name": "Sam", "role": "admin"},
timeout=30,
)
response.raise_for_status()
print(response.json())
JSON objects
Use json= when the request body is a Python dictionary or list that should be encoded as JSON:
import requests
payload = {"name": "Sam", "roles": ["admin"]}
response = requests.post(
"https://api.example.com/users",
json=payload,
timeout=30,
)
response.raise_for_status()
print(response.json())
Requests’ json= argument encodes the object and sets the appropriate JSON content type. Passing a serialized string through data= does not automatically add Content-Type: application/json. Also, json= is ignored when data= or files= is supplied; do not combine them expecting two bodies.
Do these 3 things before closing this tab:
1Clear out junk files and repair common Windows errors2Scan for outdated or missing drivers - takes under a minute3Repair Windows errors before they cause bigger problemsRaw or pre-serialized data
If the cURL command sends exact bytes, preserve them explicitly and copy any required content type:
import requests
raw_body = '{"name":"Sam"}'
response = requests.post(
"https://api.example.com/users",
data=raw_body,
headers={"Content-Type": "application/json"},
timeout=30,
)
response.raise_for_status()
This is useful when the server requires a specific serialization, signature or byte sequence.
Multipart uploads and file fields
Translate cURL’s multipart options (for example, -F "[email protected]") with files=. Let Requests generate the multipart boundary; manually setting it is a common source of broken uploads.
import requests
with open("report.pdf", "rb") as document:
response = requests.post(
"https://api.example.com/upload",
files={"file": document},
data={"description": "Monthly report"},
timeout=120,
)
response.raise_for_status()
print(response.json())
You can provide a tuple containing filename, file object or bytes, content type and per-part headers:
files = {
"file": (
"report.pdf",
open("report.pdf", "rb"),
"application/pdf",
{"Expires": "0"},
)
}
Close file handles with a context manager when the request finishes.
Authentication, cookies and sessions
Basic authentication
import requests
response = requests.get(
"https://api.example.com/private",
auth=("username", "password"),
timeout=30,
)
response.raise_for_status()
Requests can also consult a netrc file when explicit authentication is not supplied. If the cURL command constructs an unusual authentication header, reproduce that header exactly instead of assuming Basic authentication.
Bearer tokens and API keys
import os
import requests
token = os.environ["API_TOKEN"]
response = requests.get(
"https://api.example.com/data",
headers={"Authorization": f"Bearer {token}"},
timeout=30,
)
response.raise_for_status()
Cookies and persistent sessions
import requests
with requests.Session() as session:
session.headers.update({"Accept": "application/json"})
session.cookies.update({"session": "abc123"})
first = session.get("https://api.example.com/login-check", timeout=30)
first.raise_for_status()
second = session.get("https://api.example.com/account", timeout=30)
second.raise_for_status()
print(second.json())
A session reuses connections and carries cookies between requests, which is closer to a sequence of cURL commands that shares a cookie jar.
Redirects, TLS, proxies and other cURL flags
Common flags do not always have identical defaults. Review the original command rather than assuming a convenience method is equivalent.
- Redirects: Requests follows redirects by default for most methods. Set
allow_redirects=Falsewhen the cURL command does not follow them, or inspectresponse.historyto audit the chain. - TLS verification: Keep certificate verification enabled. Use
verify="/path/to/ca-bundle.pem"for a custom CA. Disabling verification withverify=Falseweakens transport security and should not be a routine conversion. - Client certificates: Use
cert="/path/client.pem"or a(certificate, key)tuple where the cURL command supplies a client certificate. - Proxies: Configure the
proxies=argument or the relevant environment variables, matching the cURL runtime environment. - Compression and streaming: Requests handles normal response decompression. For large downloads, use
stream=Trueand iterate over chunks rather than loading the entire body into memory. - Output and headers: cURL options that print response headers, discard the body or write to a file require corresponding Python handling; they are not part of the HTTP request itself.
cURL does not forward Authorization and Cookie headers to a different origin after a redirect by default. Check the destination and Requests’ redirect behavior before assuming credentials follow every hop.
One reusable conversion pattern
For a request whose method and options may vary, use requests.request():
import requests
response = requests.request(
method="PATCH",
url="https://api.example.com/items/42",
params={"notify": "true"},
headers={
"Accept": "application/json",
"Authorization": "Bearer YOUR_TOKEN",
},
json={"status": "ready"},
timeout=(10, 60),
allow_redirects=False,
)
print("status:", response.status_code)
response.raise_for_status()
print(response.json())
The two-value timeout sets separate connection and read limits. Choose values appropriate for the endpoint instead of allowing a request to wait indefinitely.
Verify that the Python request still means the same thing
- Compare the HTTP method and final URL, including encoded query parameters.
- Compare outgoing headers, but remove secrets before logging.
- Confirm whether the body is form encoded, JSON, multipart or raw bytes.
- Check cookies, authentication, redirect policy, proxy settings and TLS verification.
- Run against a safe test endpoint or environment, then inspect
response.status_code, selected response headers and the body. - Use
response.json()only when the endpoint returns JSON; valid JSON can still be an error response, so status checking remains separate.
Troubleshooting conversion failures
415 Unsupported Media Type
The server expected a different body encoding or content type. Replace data= with json= for a JSON object, or set the exact content type when sending raw data.
Outdated Drivers Are Slowing You Down
One free scan finds every outdated or missing driver and matches the right update for your exact hardware.Free scan · exact hardware matchPC Slower Than It Used to Be?
A free scan shows the junk files, broken settings and background clutter dragging Windows down - then fixes them in one click.Free scan · Windows 10 & 11422 or 400 validation errors
Inspect the serialized body and parameter names. A cURL command using repeated -d fields, nested JSON or array parameters may not map to the dictionary shape you chose.
401 or 403 responses
Check the authentication scheme, token prefix, cookies, header spelling and redirect destination. Do not assume a token sent to one origin is valid after a cross-origin redirect.
Multipart upload rejected
Use files=, keep the file open during the request and avoid manually setting the multipart boundary. Include ordinary fields in data=.
Timeouts or hanging requests
Set a connect/read timeout, verify DNS and proxy configuration, and use streaming for large responses. A longer timeout does not fix an incorrect URL or server-side failure.
Best Value
SSL certificate errors
Install or reference the correct CA bundle with verify=. Treat verify=False as a diagnostic exception, not a production solution.
Different results after a redirect
Log response.url and inspect response.history. Compare how cURL and Requests handle credentials, cookies and method changes across each redirect.
Or skip the browser setup
If the cURL command you are converting is for a website screenshot, ScreenshotNeo provides a direct HTTP endpoint instead of requiring you to configure a headless browser. It accepts consent banners before capture and removes more than 60 known consent platforms, newsletter popups and chat widgets; each cleanup step can be turned off. Bot checks, CAPTCHAs, blank pages, timeouts, failed loads and cache hits are not billed, and the response identifies the page verdict and billing status in X-Page-Verdict and X-Billed headers.
One call in cURL:
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
The equivalent Python request is:
import requests
r = requests.get("https://api.screenshotneo.com/v1/shot", params={"access_key": "YOUR_API_KEY", "url": "https://stripe.com"}, timeout=90)
r.raise_for_status()
open("shot.webp", "wb").write(r.content)
Node.js:
const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
if (!res.ok) throw new Error(`HTTP ${res.status}`);
const fs = await import('node:fs/promises');
await fs.writeFile('shot.webp', Buffer.from(await res.arrayBuffer()));
See the parameter reference and additional options in the ScreenshotNeo documentation. Its API also supports full-page and element captures, device presets, dark mode, custom CSS and JavaScript, waits, request blocking, cookies, headers, PDFs, signed links, asynchronous jobs, bulk capture and a usage API. An MCP server provides take_screenshot, get_page_info and capture_pdf tools for Claude, Cursor and other MCP clients. The free plan includes 1,000 screenshots per month without a card; paid plans start at $5 for 3,000 shots. Create a free ScreenshotNeo account.
Recommended Free Tools
Performance, reliability and cost considerations
- Reuse a
Sessionfor related requests to reduce connection setup. - Set finite connect and read timeouts and handle transient failures at the application layer.
- Stream large downloads and write chunks to disk.
- Do not automatically retry non-idempotent POST requests unless the API documents safe retry behavior or you use an idempotency key.
- Log status, elapsed time, final URL and a request identifier, but redact authorization headers, cookies and personal data.
- Keep the same proxy, CA bundle, DNS context and environment variables used by the original cURL invocation when comparing behavior.
Requests is a convenient documented option for common HTTP features. The available evidence does not establish an empirical performance ranking against other Python HTTP clients, so choose another client only when your project needs capabilities Requests does not provide.
Frequently Asked Questions
Can an automated converter translate every cURL flag perfectly?
No. cURL has options for transport, TLS, redirects, proxies, uploads and command-line output that may require manual decisions in Python. Review the complete command and verify the resulting request.
Should I use data= or json= in Requests?
Use json= for a Python object that the server expects as JSON. Use data= for form-encoded fields or exact raw content, adding the required content type yourself for serialized JSON.
Why does response.json() not prove that the request succeeded?
A server can return a valid JSON error document with an HTTP 4xx or 5xx status. Check response.status_code or call raise_for_status() before treating the response as successful.
Quick wins for a faster PC:
Scan for outdated or missing drivers - takes under a minuteDriver Scan →Clear out junk files and repair common Windows errorsFree Scan →How do I preserve a cURL file upload?
Open the file in binary mode and pass it through files=. Put ordinary multipart fields in data= and let Requests create the multipart boundary.
Quick Recap
Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.




