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

Some links on this page are affiliate links: if you buy through them we may earn a commission, at no extra cost to you.

To use the installed curl command from Python, run it with subprocess.run() and a list of arguments. Keep the default shell=False, set a timeout, and choose whether to capture the result or raise an exception when curl fails. If your goal is simply to make an HTTP request, Python’s urllib.request or the Requests library may fit better because they do not require starting a separate program. This guide explains both choices and how to handle their practical trade-offs.

Run cURL from Python with subprocess

Python’s recommended high-level interface for subprocess cases it can handle is subprocess.run(). Pass the executable, each option, and the URL as separate strings in a list. For example, this runs curl, captures the response body as text, reports curl errors on standard error, stops waiting after 20 seconds, and raises an exception for a nonzero exit status:

import subprocess

result = subprocess.run(
    ["curl", "--fail", "--silent", "--show-error", "https://example.com/"],
    capture_output=True,
    text=True,
    timeout=20,
    check=True,
)
print(result.stdout)

The example uses curl command-line options; confirm that the options and behavior suit the curl version and operation in your environment. Python’s subprocess documentation covers run(), argument handling, captured output, timeouts, and return codes.

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

What each subprocess option does

  • capture_output=True collects standard output and standard error for your program to inspect. Without it, output is normally inherited by the parent process instead.
  • text=True asks Python to decode captured output into strings. Leave it off when you need raw bytes, such as for an image or other binary response.
  • timeout=20 limits how long Python waits for the child process. Pick a limit appropriate to the task rather than letting a request wait indefinitely.
  • check=True raises subprocess.CalledProcessError if curl exits with a nonzero status. Omit it if you prefer to inspect result.returncode yourself.
  • --silent suppresses curl’s usual progress display; --show-error keeps its error message visible, and --fail asks curl to treat an HTTP error response as a failure. These are curl options, not Python subprocess options.

Handle failures explicitly

With check=True, catch CalledProcessError when you want to recover from a failed command rather than stop the program. The exception and the captured result can help you log the exit status and diagnostics. If the timeout expires, Python raises TimeoutExpired. If Python cannot locate the executable, the launch fails; verify that curl is installed and discoverable from the environment running your script.

import subprocess

try:
    result = subprocess.run(
        ["curl", "--fail", "--silent", "--show-error", "https://example.com/"],
        capture_output=True,
        text=True,
        timeout=20,
        check=True,
    )
except subprocess.TimeoutExpired as exc:
    print(f"curl exceeded the time limit: {exc}")
except subprocess.CalledProcessError as exc:
    print(f"curl exited with status {exc.returncode}")
    print(exc.stderr or "No captured error message")

Choose whether to capture output based on what the application needs. Capturing the response is useful when Python will parse or transform it; if curl is writing directly to a file, capturing the body may be unnecessary. Treat returned data as bytes when it is not text.

Why use an argument list instead of a shell command?

Do not assemble a command string by concatenating a URL or other untrusted value. Passing a sequence to subprocess.run() lets Python pass arguments to the program without asking a system shell to parse that string. Keep the default shell=False unless you have a specific reason to invoke a shell.

When shell=True is used, the application becomes responsible for correctly quoting whitespace and shell metacharacters. Incorrect quoting can create shell-injection vulnerabilities when values include user input. A list of arguments avoids that shell-parsing step for ordinary invocations. See Python’s subprocess security considerations for the documented distinction.

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

Keep values as individual arguments

Each option and its value should be its own list item when curl expects separate arguments. A space inside one Python string does not cause Python to split that string into shell-style words. Likewise, do not add shell quotes around a URL merely because it contains punctuation; quotes are shell syntax, and with shell=False they would be passed as literal characters if included in the argument itself.

url = "https://example.com/"
args = ["curl", "--fail", "--silent", "--show-error", url]
result = subprocess.run(args, capture_output=True, text=True, timeout=20, check=True)

Choose between cURL, urllib.request, and Requests

Launching curl is appropriate when your project specifically depends on the curl executable or a curl behavior. If your program only needs to communicate over HTTP, an in-process Python HTTP library avoids managing a child process, its executable, and its exit status. These choices are not interchangeable in every case; compare the features and runtime behavior your application needs.

Approach Requires curl executable? Process handling Documentation
subprocess.run() with curl Yes Python starts a child process; your code chooses timeouts, output handling, and whether to raise on a nonzero exit status. Python subprocess
urllib.request No Uses Python’s standard-library URL-opening facilities rather than a separate curl process. Python urllib.request
Requests No Uses the separate Requests Python library; consult its current documentation for installation, API, and supported Python versions. Requests documentation

Use urllib.request for a standard-library option

urllib.request provides URL-opening functions and classes, with documented support for areas including authentication, redirects, and cookies. It is a reasonable starting point when you want HTTP access without a separately installed curl executable or an additional library. Review its documentation for the particular request pattern you need: urllib.request — Extensible library for opening URLs.

Use Requests when your project uses that library

Requests is a Python HTTP library, not a wrapper that launches the curl command. It introduces its own dependency and API, so check the project’s documentation for installation instructions and the Python versions it supports: Requests: HTTP for Humans.

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

Keep curl when curl itself is a requirement

If your existing workflow relies on a curl command or behavior, using the executable from Python preserves that boundary: Python starts curl and handles its input, output, timeout, and status. The trade-off is deployment responsibility. The target machine must have a compatible curl executable available, and process lookup can differ between operating systems.

Make the invocation reliable across environments

Confirm which executable Python will run

For maximum reliability, Python recommends using a fully qualified path to the executable. If you want to search the environment’s PATH, use shutil.which() to locate curl and handle a missing result before launching the process. This matters especially when a script runs in a service, scheduled task, container, or other environment whose PATH may differ from your interactive terminal.

import shutil
import subprocess

curl_path = shutil.which("curl")
if curl_path is None:
    raise RuntimeError("curl was not found on PATH")

result = subprocess.run(
    [curl_path, "--fail", "--silent", "--show-error", "https://example.com/"],
    capture_output=True,
    text=True,
    timeout=20,
    check=True,
)

Executable resolution has platform differences. Python specifically documents differences in how Windows resolves an executable when shell=False; test in the same environment where the program will run, and use an explicit executable path if appropriate. The Python subprocess reference provides the platform-specific details.

Set output handling to match the response

For text such as a response body you plan to inspect, capture_output=True with text=True is convenient. For binary output, omit text=True so Python retains bytes. Capturing output also means the child’s output is held for your program; avoid capturing it when you do not need it. The right choice depends on whether Python must parse, log, or otherwise use the response.

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.

Choose timeout and exit-status behavior deliberately

A timeout bounds how long the parent waits for curl. A value that is too short can interrupt a legitimate slow operation; a value that is too long can leave the application waiting longer than intended. With check=True, failures become exceptions at the call site; with the default check=False, inspect returncode and decide what the program should do. Neither choice substitutes for handling errors deliberately.

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

Common cURL-in-Python problems and fixes

  • “No such file” or executable not found: curl may be missing or absent from the Python process’s PATH. Install or configure the executable for that environment, check it with shutil.which("curl"), or pass its fully qualified path.
  • CalledProcessError is raised: this is expected with check=True when the process exits nonzero. Inspect returncode and captured stderr; confirm the URL, curl arguments, and network conditions relevant to your request.
  • TimeoutExpired is raised: the process did not finish within the configured limit. Check whether the operation is expected to take longer, then choose an appropriate timeout and decide how the application should recover.
  • The URL or option is being interpreted incorrectly: verify that every argument is a separate list item and that you have not included shell quoting characters in the values. Avoid building a shell command string.
  • Binary output looks corrupted: do not use text=True for binary content. Keep the captured output as bytes or have curl write it to a file, as appropriate for the task.
  • The script works locally but not on deployment: the executable path or version may differ. Check curl availability and test invocation behavior on the target operating system and runtime.

Or skip the browser setup

If the task is to capture a website screenshot rather than learn or depend on the curl executable, ScreenshotNeo offers a one-request API. This is a different approach from invoking curl through Python: the following command uses curl directly to request a screenshot.

curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp

See the ScreenshotNeo API documentation for request options. ScreenshotNeo removes cookie/consent banners, newsletter popups, and chat widgets before capture; bot checks, blank pages, and failed loads are not billed. Its MCP server lets AI agents use screenshot tools. The free plan includes 1,000 shots per month with no card, and paid plans start at $5 for 3,000 shots. Learn about ScreenshotNeo or sign up for 1,000 free screenshots a month with no card.

Performance and cost considerations

Calling curl starts a separate process, so it adds process-launch and executable-management work compared with making an HTTP request inside Python. Whether that matters depends on the application and how often it makes requests; the available references do not establish a universal performance winner. If process startup and exit handling do not serve a requirement, consider an HTTP library instead.

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.

For a service or repeated task, consider the operational cost of ensuring curl exists on every machine, handling timeouts and failures, and keeping behavior consistent across platforms. In contrast, urllib.request uses Python’s standard library, while Requests is an additional library dependency. Compare those deployment needs alongside the HTTP capabilities your program requires rather than choosing solely by familiarity.

Frequently Asked Questions

Does Python’s subprocess.run() install cURL for me?

No. It launches an executable available to the Python process. Install curl in the runtime environment or provide its executable path.

Is Requests the same thing as running the cURL command?

No. Requests is a Python HTTP library; using subprocess.run() launches the external curl program.

Can I use cURL from Python without shell=True?

Yes. Pass curl and its arguments as a list to subprocess.run(); this is the normal pattern for avoiding shell parsing.

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

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.