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

To use the installed curl command from Python, call it with subprocess.run() and a list of arguments. Leave shell=False (the default), set a timeout, and choose whether to capture its output or raise an exception when it fails. If you only need to make an HTTP request, use a Python HTTP library such as urllib.request or Requests instead of starting a separate curl process.

Run curl from Python with subprocess

Python’s recommended high-level interface for subprocess cases it can handle is subprocess.run(). Pass the executable and each curl option as a separate list item; this avoids shell parsing for an ordinary invocation. Python’s subprocess documentation describes the arguments, output handling, timeouts, and return codes.

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)

This example expects curl to be installed and discoverable through the process PATH. capture_output=True captures stdout and stderr; text=True asks Python to decode captured output as text; and timeout=20 limits how long Python waits. With check=True, a nonzero curl exit status raises subprocess.CalledProcessError rather than letting the program continue as though the command succeeded. The example has not been run or tested here, so check the curl options against the version and operation in your environment.

Print the response body

For a text response, use result.stdout, as above. The --silent option suppresses curl’s progress display, while --show-error preserves an error message when curl fails. --fail makes HTTP error responses fail the command instead of treating the response body as an ordinary successful result. If you omit check=True, inspect result.returncode yourself and decide how your program should handle a nonzero value.

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

Capture bytes instead of text

For binary output, omit text=True and work with bytes. This avoids trying to decode an image or other binary response as text.

import subprocess

result = subprocess.run(
    ["curl", "--fail", "--silent", "--show-error", "https://example.com/file.bin"],
    capture_output=True,
    timeout=60,
    check=True,
)
with open("file.bin", "wb") as file:
    file.write(result.stdout)

Capturing the complete response in memory is convenient for modest outputs. For large downloads, consider having curl write to a file rather than collecting the whole body in Python. Set the timeout to suit the request and expected transfer; a timeout that is too short can interrupt a legitimate slow operation.

Pass curl options and request data safely

Build the argument list as data, not as a shell command string. Each option and its value should be a distinct list element. For example, to send a header, use ["curl", "--header", "Accept: application/json", url]. To send a form field, use ["curl", "--data", "name=value", url]. Adapt options to the request you actually need and to the installed curl version.

import subprocess

url = "https://example.com/api"
args = [
    "curl",
    "--fail",
    "--silent",
    "--show-error",
    "--header",
    "Accept: application/json",
    url,
]

result = subprocess.run(
    args,
    capture_output=True,
    text=True,
    timeout=30,
    check=True,
)
print(result.stdout)

Do not concatenate an untrusted URL, header, or other input into one command string and pass it through a shell. Python does not implicitly choose a system shell for normal subprocess calls. If you set shell=True, you take responsibility for quoting whitespace and shell metacharacters correctly; mistakes can create shell-injection vulnerabilities. Prefer a list and the default shell=False. See the Python subprocess security guidance.

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

Choose between invoking curl and using a Python HTTP library

Launching curl is useful when the curl executable or a particular curl behavior is a project requirement. It also means your program must start another process, find the executable on the deployment machine, and handle that process’s output, timeout, and exit status. A Python HTTP library keeps the request inside the Python process and avoids requiring a separate curl executable, but it is not automatically interchangeable with every curl workflow.

Approach Use it when Things to account for
subprocess.run() with curl The existing curl executable or a specific curl behavior is required. Install and locate curl, pass arguments safely, set timeout and output handling, and handle its exit status. Behavior and executable lookup can vary across platforms.
urllib.request You want an HTTP-oriented option from Python’s standard library. It provides URL-opening functions and classes, with documented support for matters including authentication, redirects, and cookies. Consult its Python documentation for the API.
Requests You want to use the Requests Python library for HTTP communication. It is a separate dependency; consult the Requests documentation for current installation, API details, and supported Python versions.

There is no universal winner: the right choice depends on whether curl itself is needed, the request features and runtime behavior your application requires, and what you can deploy. For a small script tied to an existing curl setup, subprocess may be straightforward. For application code whose goal is simply to make HTTP requests, compare the Python libraries before adding a process boundary.

Make subprocess handling more reliable

  • Confirm executable lookup. Python recommends using a fully qualified executable path for maximum reliability, or using shutil.which() when searching PATH. If the program works locally but not in deployment, check the runtime environment’s path and installed curl location.
  • Set a timeout. Choose how long the parent program should wait for the command. Handle subprocess.TimeoutExpired if your application needs to recover or report a timeout rather than terminate that code path.
  • Capture only what you need. Capturing output is useful when Python must parse or save it, but it stores captured data in memory. Use text decoding for text responses and bytes for binary data.
  • Choose a failure policy. Use check=True to raise on nonzero exit status, or leave it false and inspect returncode. In either case, decide what your application should do with stderr and failed requests.
  • Test the target platform. Executable lookup differs across platforms. Python specifically documents Windows differences when resolving executables with shell=False; test the environment where the code will run and use an explicit executable path when appropriate.

Troubleshoot common failures

FileNotFoundError: Python cannot find curl

Install curl in the environment where the Python process runs, or point the argument list at its full executable path. A curl installation on your workstation does not guarantee the executable is available in a container, scheduled task, service account, or another machine.

CalledProcessError: curl returned a nonzero status

This is the expected effect of check=True. Catch subprocess.CalledProcessError if you want to report or recover from the failure; its process result includes the return code and any captured output. If you do not want an exception, omit check=True and test result.returncode. A nonzero status can have different causes, so inspect stderr and the curl options rather than assuming every failure is an HTTP error.

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

TimeoutExpired: the command exceeded its limit

Increase the timeout if the request legitimately needs more time, or handle the timeout as a failure in your application. Check whether the target is slow or unreachable and whether the selected timeout is appropriate for the operation. A timeout bounds how long Python waits; it does not establish that the remote operation was never performed.

Unexpected text decoding or garbled output

If the response is binary, remove text=True and use bytes. If it is text in an encoding different from the default, choose a decoding approach appropriate to that response instead of assuming every body is plain UTF-8.

Works on one machine but not another

Check the curl installation, executable path, account permissions, and platform-specific lookup behavior. Use an explicit path where appropriate and test under the same account and environment as the deployed program.

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

Or skip the browser setup

If your Python task is specifically to capture website screenshots or PDFs rather than to run curl itself, ScreenshotNeo offers a one-call screenshot API. It accepts a URL and returns a screenshot or PDF; the example below saves a WebP response. See the ScreenshotNeo website and API documentation for the request details.

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

r = requests.get(
    "https://api.screenshotneo.com/v1/shot",
    params={"access_key": "YOUR_API_KEY", "url": "https://example.com"},
    timeout=90,
)
open("shot.webp", "wb").write(r.content)
  • Cookie and consent banners are accepted like a visitor and removed along with supported newsletter popups and chat widgets before capture; each of those steps can be turned off.
  • Bot checks or CAPTCHAs, blank pages, timeouts, failed loads, and cache hits are not billed; response headers indicate the page verdict and billing status.
  • An MCP server provides take_screenshot, get_page_info, and capture_pdf tools 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. Every feature is available on every plan.

Sign up for ScreenshotNeo’s free plan: 1,000 screenshots a month, no card required.

Frequently asked questions

Does subprocess.run() make the HTTP request itself?

No. Python starts the external curl executable; curl performs the request. If you want HTTP communication without launching curl, use a Python HTTP library.

Do I need shell=True to pass a URL containing query parameters?

No. Keep the URL as one string element in the argument list. With the default shell=False, Python passes it as an argument rather than asking a shell to parse it.

Can I use this pattern on Windows?

Yes, but verify how curl is installed and resolved in the actual Windows environment. Python documents platform differences in executable resolution, so test deployment and use a full executable path when needed.

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.