Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

To convert a cURL command to Python Requests, map each cURL option to the corresponding request argument: query values to params, headers to headers, JSON to json, form or raw bodies to data, credentials to auth, cookies to cookies, uploads to files, and time limits to timeout. Then check the response status and handle errors explicitly. This guide walks through that translation and the common reasons a command that works in cURL may fail in Python.

Install Requests and make a first request

Install Requests in the Python environment where your script will run:

python -m pip install requests

The Requests overview lists Python 3.10+ support and identifies Requests 2.34.2 as its current release; these are version-sensitive details, so check the official overview for the current compatibility information.

A useful first request includes a timeout, checks for an unsuccessful HTTP status, and only parses JSON when the server says it returned JSON:

Free tools Windows power users keep installed

One-click scans. No signup required.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
import requests

url = "https://httpbin.org/get"
try:
    response = requests.get(url, timeout=(3, 20))
    response.raise_for_status()
except requests.exceptions.Timeout:
    raise SystemExit("The request timed out")
except requests.exceptions.HTTPError as exc:
    raise SystemExit(f"The server returned an unsuccessful status: {exc}")

print("Status:", response.status_code)
print("Content type:", response.headers.get("Content-Type"))
if "application/json" in response.headers.get("Content-Type", "").lower():
    print(response.json())
else:
    print(response.text[:500])

Requests describes itself as “an elegant and simple HTTP library for Python, built for human beings.” Its quickstart demonstrates the basic request and response workflow.

Translate cURL options into Requests arguments

Start with the server’s contract: it determines the accepted URL, HTTP method, body format, authentication, redirect behavior, and response codes. A cURL command is a convenient way to express that contract, but not every cURL flag has a one-to-one Requests argument.

cURL pattern Requests equivalent What to know
-G with -d key=value params={...} Query parameters belong in the URL query string; Requests encodes them.
-H 'Name: value' headers={...} Header names are strings; values should match the API’s requirements.
-d '{...}' as JSON json={...} Requests serializes the object as JSON and sets a JSON content type.
-d 'a=b' as form data, or a raw body data=... Use a dictionary for form fields or a string/bytes body when the API expects raw content.
-u user:password auth=(user, password) This represents HTTP Basic authentication. Other schemes need their own handling.
-F 'file=@path' files={...} Use multipart uploads when the endpoint expects them.
-b or -c cookies=... or a Session Use a session when cookies must persist across calls.
--max-time timeout=... Requests accepts a scalar or a (connect, read) pair; behavior is not a total wall-clock deadline.

The corresponding arguments are documented in the Requests API reference. Be careful with cURL’s -d: paired with -G, it contributes query parameters; without -G, it normally sends a request body. In Python, express those two intentions separately with params and data or json.

Build requests with query strings, headers, and bodies

Query parameters

Pass query values as a mapping instead of manually concatenating and escaping them:

What’s actually slowing this PC down?

Pick the symptom - the matching free tool is one click away.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
import requests

response = requests.get(
    "https://api.example.com/search",
    params={"q": "red shoes", "page": 2},
    timeout=(3, 20),
)
response.raise_for_status()

Requests handles URL encoding. This avoids common errors with spaces, ampersands, non-ASCII characters, and values that need escaping. If the API requires repeated keys, use a sequence of pairs, such as params=[("tag", "one"), ("tag", "two")].

Headers and JSON

Use headers for values such as an API token or a version header. Send JSON with json:

import os
import requests

response = requests.post(
    "https://api.example.com/items",
    headers={"Authorization": f"Bearer {os.environ['API_TOKEN']}"},
    json={"name": "sample", "enabled": True},
    timeout=(3, 20),
)
response.raise_for_status()

Keep credentials in environment variables or a secret manager, not source code or logs. Do not assume every API uses Bearer tokens or accepts JSON; follow the endpoint’s authentication and content-type requirements.

Form data and raw request bodies

Use data with a dictionary for form-encoded fields:

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
response = requests.post(
    "https://api.example.com/login",
    data={"username": "example", "remember": "yes"},
    timeout=(3, 20),
)
response.raise_for_status()

For an API that expects a specific raw body, pass a string or bytes to data and set the required content type yourself:

response = requests.post(
    "https://api.example.com/ingest",
    data=b"raw payload",
    headers={"Content-Type": "application/octet-stream"},
    timeout=(3, 20),
)
response.raise_for_status()

Do not use data and json together to describe one body. Choose the representation the server expects.

Send authentication, cookies, and files

Authentication

For HTTP Basic authentication, pass a username and password with auth:

response = requests.get(
    "https://api.example.com/private",
    auth=("user", "password-from-secret-store"),
    timeout=(3, 20),
)
response.raise_for_status()

Requests also documents Digest authentication and use of .netrc. OAuth and OAuth 2/OpenID Connect typically require obtaining and refreshing tokens with an appropriate integration; they are not interchangeable with Basic authentication. Consult the authentication documentation and the API’s own token, scope, and refresh rules. Redact authorization values when logging request details.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Cookies

For a single request, pass cookies as a mapping:

response = requests.get(
    "https://example.com/account",
    cookies={"session_id": "value-from-a-secure-source"},
    timeout=(3, 20),
)
response.raise_for_status()

When a server sets cookies that later requests need, use a session rather than manually copying cookie headers. Cookie scope and behavior are controlled by the server and the cookie rules applied by the client.

File uploads

Use files for a multipart form upload. Open the file in binary mode and close it when the request finishes:

with open("report.pdf", "rb") as file_handle:
    response = requests.post(
        "https://api.example.com/upload",
        files={"file": file_handle},
        data={"category": "reports"},
        timeout=(5, 60),
    )
    response.raise_for_status()

The field name, additional form fields, and accepted file types are endpoint-specific. For very large payloads, check the service’s size limits and streaming guidance rather than assuming a small example is suitable.

Read responses and handle errors explicitly

A response exposes several useful views:

  • response.status_code is the HTTP status code.
  • response.headers contains response headers, such as content type.
  • response.text decodes the body as text.
  • response.content gives the body as bytes, useful for binary data.
  • response.json() parses JSON, but raises an error if the body is not valid JSON.

Call raise_for_status() when a non-success HTTP status should stop ordinary processing. It raises an HTTPError for unsuccessful responses; it does not mean every failure is an HTTP status. Connection errors, timeouts, invalid JSON, and application-level error objects need their own handling. The quickstart explains response handling and raised exceptions.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
import requests

try:
    response = requests.get("https://api.example.com/status", timeout=(3, 20))
    response.raise_for_status()
    content_type = response.headers.get("Content-Type", "").lower()
    if "application/json" not in content_type:
        raise ValueError(f"Expected JSON, got {content_type or 'unknown content type'}")
    payload = response.json()
except requests.exceptions.Timeout:
    print("The server did not respond within the configured timeout")
except requests.exceptions.ConnectionError:
    print("Could not establish or maintain a connection")
except requests.exceptions.HTTPError as exc:
    print(f"HTTP failure: {exc}")
except ValueError as exc:
    print(f"Response was not usable JSON: {exc}")

Set timeouts and make retries safe

Never rely on an implicit unlimited wait. A scalar timeout applies to the connection and response-wait behavior; a pair such as (3, 20) sets a connect timeout and a read timeout separately. The connect timeout limits the time spent establishing a connection. The read timeout limits how long the client waits for response bytes after connection; it is not necessarily a maximum for the entire operation. See the timeout parameter reference.

Catch requests.exceptions.Timeout or a narrower timeout subclass, then decide whether retrying is safe. Repeating a read-only request is often different from repeating a request that charges a card, creates an order, or otherwise changes server state. A timeout does not prove the server failed to perform the operation; it may have completed it while the response was lost. Use an API-provided idempotency mechanism where available, and follow the service’s retry guidance.

Reuse connections and cookies with a Session

For repeated calls to the same service, a Session persists cookies and reuses pooled connections. It can also carry shared headers. This reduces repeated setup and is useful for multi-request workflows such as login followed by authenticated requests. Requests describes these behaviors in its advanced usage guide.

import requests

with requests.Session() as session:
    session.headers.update({"Accept": "application/json"})
    first = session.get("https://api.example.com/start", timeout=(3, 20))
    first.raise_for_status()

    second = session.get("https://api.example.com/next", timeout=(3, 20))
    second.raise_for_status()
    print(second.json())

The context manager closes the session and its connections when the block ends. Keep a session scoped to the work that needs its shared state; do not accidentally share user-specific cookies or authorization headers across unrelated users in a long-running service.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Why cURL may work while Python Requests fails

The difference is often not “cURL versus Python” but a detail hidden in the original command or environment. Compare the actual request components before changing libraries.

  • Different URL encoding: Move query values into params rather than hand-building the query string. Check whether cURL was using -G.
  • Wrong body format: cURL -d does not by itself tell you whether the endpoint expects JSON, form encoding, or raw bytes. Use json only for JSON, and data for the other documented formats.
  • Missing headers: Reproduce required headers from -H, especially content type, accept, and API version headers. Do not copy browser headers blindly.
  • Authentication mismatch: cURL -u maps to Basic authentication, not automatically to an API key or OAuth flow. Verify the scheme and credential placement expected by the service.
  • Cookie state differs: cURL may be loading or saving cookies with -b and -c. Use a Requests session when cookies must carry across calls.
  • Redirect or status differences: Inspect status_code, response headers, and the final URL. Confirm the endpoint’s redirect policy and do not treat a returned error page as a successful API response.
  • Local network or certificate setup: The two clients may run with different proxy, CA certificate, environment, or runtime configuration. Diagnose the actual environment instead of disabling TLS verification.

Requests verifies TLS certificates by default. Avoid verify=False as a routine workaround: it removes certificate verification rather than fixing an incorrect trust configuration. If an organization uses a private CA, configure the appropriate CA bundle deliberately.

Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

When to choose Requests or curl_cffi

Requests is the straightforward default for ordinary Python HTTP clients. Choose an alternative when a concrete compatibility requirement calls for it, not simply because one request failed.

Need Requests curl_cffi
Familiar Python request interface Common methods and arguments such as get, post, params, json, and timeout. Requests-like interface, described in its quickstart.
Sessions and cookie persistence Sessions persist cookies and reuse pooled connections. Provides sessions; its maintainers say, “You should always use a session whenever possible.”
Basic request controls and errors Documented request parameters, timeouts, TLS verification, and response handling. Offers curl-oriented options alongside its Requests-like API; check the API reference for exact options.
Browser impersonation controls Not the reason to choose Requests. Provides an impersonate parameter for browser-oriented behavior.
Command-line use Typically used as a Python library. Its documentation gives CLI invocations including uv run curl-cffi and python -m curl_cffi; see the documentation PDF.

Browser impersonation is not permission to bypass a site’s terms, access controls, or authorization. For either library, review its installation requirements, release policy, and deployment constraints before making it a production dependency. A similar API can ease migration, but TLS and HTTP behavior can still differ; verify the behavior your integration actually requires.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Or skip the browser setup

If your task is to capture a website as an image or PDF rather than build a general-purpose HTTP client, ScreenshotNeo provides a screenshot API and MCP server. One GET request can return a PNG, JPEG, WebP, or PDF. For example, from Python:

import requests

r = requests.get(
    "https://api.screenshotneo.com/v1/shot",
    params={"access_key": "YOUR_API_KEY", "url": "https://stripe.com"},
    timeout=90,
)
open("shot.webp", "wb").write(r.content)

See the ScreenshotNeo documentation for request options. It accepts cookie or consent banners and removes more than 60 known consent platforms, newsletter popups, and chat widgets before capture; each of those steps can be turned off. Bot checks and CAPTCHAs, blank pages, timeouts, failed loads, and cache hits are not billed, and response headers report the page verdict and billing status. Its MCP server offers take_screenshot, get_page_info, and capture_pdf for AI agents and MCP clients. The free plan includes 1,000 screenshots per month with no card; paid plans start at $5 for 3,000 shots.

Sign up free for 1,000 screenshots a month, with no card required.

Troubleshoot common failures

Symptom Likely cause What to check or change
Connection hangs No timeout was set, or the configured timeout is too long for the task. Set a scalar timeout or a connect/read pair. Handle timeout exceptions and choose limits appropriate to the endpoint.
HTTP 400 or 415 Malformed input or an unexpected body/content type. Confirm whether the API expects JSON, form data, or raw bytes. Use json for JSON and inspect the server’s error response.
HTTP 401 or 403 Missing, invalid, expired, or insufficient credentials; possibly the wrong authentication scheme. Check the API’s credential format, token scopes, refresh rules, and required headers. Do not log secrets.
HTTP 404 Wrong path, API version, or resource identifier. Compare the full URL and path against the endpoint documentation and inspect redirects or the final URL.
JSON parsing error The response is not JSON or contains invalid JSON, often because an error page was returned. Check the status and content type before calling .json(); inspect a safe excerpt of .text.
Certificate verification failure The trust store does not recognize the certificate chain, possibly due to a private CA or environment configuration. Install or configure the appropriate CA bundle. Avoid turning off verification as a general fix.
Repeated request creates duplicates A retry occurred after a timeout even though the server completed the first operation. Use an idempotency key if the API supports one and follow its retry rules before repeating state-changing calls.

FAQ

Does Python Requests run the cURL executable?

No. Requests is a Python HTTP library with its own request API; translating a command means reproducing its HTTP method, URL, headers, body, authentication, cookies, and relevant network behavior.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Can I use Requests for a screenshot API?

Yes. A screenshot service that exposes an HTTP endpoint can be called with Requests; use the service’s documented parameters and save the response bytes in the appropriate file format.

Should I switch to curl_cffi whenever a site rejects Requests?

No. First verify that your request is authorized and correctly formed. Consider curl_cffi only when its documented curl-oriented or browser-impersonation capabilities are a genuine requirement, and respect the site’s policies.

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.