curl-cffi

raw JSON →
0.14.0 verified Tue May 12 auth: no python install: verified

curl-cffi is a Python binding for the curl-impersonate fork via CFFI. It enables Python applications to impersonate browsers' TLS/JA3 and HTTP/2 fingerprints, effectively bypassing many anti-bot systems. The library provides a high-level API that mimics the popular `requests` library, making it intuitive to use. It supports asynchronous operations, HTTP/2, HTTP/3, and WebSockets. The current stable version is 0.14.0, with active development and frequent updates.

pip install curl-cffi
error ModuleNotFoundError: No module named '_cffi_backend'
cause When packaging a `curl-cffi` application with PyInstaller, the `_cffi_backend` module and associated data files are often not automatically included in the final executable.
fix
Instruct PyInstaller to explicitly include the necessary modules and data files. Example: pyinstaller -F your_script.py --hidden-import=_cffi_backend --collect-all curl_cffi
error ImportError: DLL load failed: The specified module could not be found.
cause This Windows-specific error typically occurs because the required Microsoft Visual C++ Redistributable libraries, essential for CFFI-based packages like `curl-cffi`, are missing or incompatible on the system. It can also indicate issues with the underlying `curl-impersonate` dependency.
fix
Install the latest Microsoft Visual C++ Redistributable for Visual Studio (usually 2015-2022) from the Microsoft website. Ensure curl-cffi is installed correctly, allowing it to use pre-compiled binaries or correctly link to curl-impersonate.
error curl_cffi.CurlError: Failed to perform, ErrCode: 92, Reason: 'HTTP/2 stream 0 was not closed cleanly: PROTOCOL_ERROR (err 1)'
cause This HTTP/2 protocol error often arises from issues such as incorrect `Content-Length` headers, problems with the proxies being used, or a server's faulty HTTP/2 implementation.
fix
Try removing the Content-Length header from your request. Evaluate and switch to more reliable proxies if applicable. As a workaround, you can force the request to use HTTP/1.1 by adding http_version=CurlHttpVersion.v1_1 to your requests.get or Session.get call.
error ERROR: Failed building wheel for curl_cffi
cause This build error during `pip install` usually happens when pre-compiled binary wheels are not available for your specific operating system and Python version, and the system lacks the necessary development tools (like `curl-dev` or `libcurl4-openssl-dev` headers) to compile `curl-cffi` and its `curl-impersonate` dependency from source. An unsupported Python version can also contribute to this (e.g., Python < 3.10 for recent `curl-cffi` versions).
fix
Ensure your Python version meets the library's requirements (e.g., Python 3.10+ for curl-cffi v0.14.0). Install system-level development packages for curl (e.g., sudo apt-get install libcurl4-openssl-dev on Debian/Ubuntu, sudo yum install libcurl-devel on RHEL/CentOS). On platforms without pre-built wheels or complex build environments (like some ARM systems), you may need to manually compile curl-impersonate first, then set LD_LIBRARY_PATH or equivalent before installing curl-cffi.
breaking As of version 0.14.0, curl-cffi officially supports Python 3.10 and above. Older Python versions (e.g., 3.9) are no longer officially supported.
fix Upgrade your Python environment to 3.10 or newer.
breaking In version 0.3.0, the `Response.cookies` attribute's type changed from `http.cookies.SimpleCookie` to `curl_cffi.requests.Cookies`. This might affect code expecting the standard library's cookie object for manipulation.
fix Adjust cookie handling to use `curl_cffi.requests.Cookies` methods, which often mimic `requests.cookies.RequestsCookieJar`.
gotcha When configuring HTTPS proxies, specifying the proxy URL with an `https://` scheme might lead to `OPENSSL_internal:WRONG_VERSION_NUMBER` errors. The `http://` scheme is often required for the proxy's URL.
fix Change proxy configuration from `{"https": "https://localhost:3128"}` to `{"https": "http://localhost:3128"}`.
gotcha The error `ErrCode: 92, Reason: 'HTTP/2 stream 0 was not closed cleanly: PROTOCOL_ERROR (err 1)'` often occurs with HTTP/2 requests, sometimes due to proxies or server-side issues. Removing `Content-Length` headers or forcing HTTP/1.1 can be workarounds.
fix Try removing the `Content-Length` header from your request or force HTTP/1.1 by setting `http_version=CurlHttpVersion.v1_1` in your request or session (e.g., `requests.get(url, http_version=CurlHttpVersion.v1_1)`).
gotcha When handling streamed responses (e.g., `stream=True`), content is buffered in memory by default. If not consumed immediately, this can lead to Out Of Memory (OOM) errors for large responses. Using `iter_content()` or `content_callback` is recommended.
fix Iterate over `response.iter_content()` immediately or use the native `content_callback` function for processing streamed content to prevent excessive memory usage.
breaking The exception hierarchy in `curl_cffi.requests.exceptions` differs from `requests`. Broad base exceptions like `requests.exceptions.RequestException` (or similar broad types like `RequestError` if used) might not exist or have different names in `curl-cffi`, leading to `AttributeError` when attempting to catch them.
fix Adjust exception handling to catch specific `curl_cffi.requests.exceptions` types, such as `RequestsError` for general `curl-cffi` errors, or more specific ones like `SSLError` or `ConnectionError`.
python os / libc status wheel install import disk mem side effects
3.10 alpine (musl) wheel - 0.25s 65.2M 8.6M clean
3.10 alpine (musl) - - 0.26s 65.2M 8.6M -
3.10 slim (glibc) wheel 3.2s 0.19s 62M 8.6M clean
3.10 slim (glibc) - - 0.18s 62M 8.6M -
3.11 alpine (musl) wheel - 0.41s 68.7M 9.9M clean
3.11 alpine (musl) - - 0.44s 68.7M 9.9M -
3.11 slim (glibc) wheel 3.1s 0.35s 66M 9.9M clean
3.11 slim (glibc) - - 0.33s 66M 9.9M -
3.12 alpine (musl) wheel - 0.54s 60.3M 10.7M clean
3.12 alpine (musl) - - 0.57s 60.3M 10.7M -
3.12 slim (glibc) wheel 3.0s 0.51s 57M 10.9M clean
3.12 slim (glibc) - - 0.54s 57M 10.9M -
3.13 alpine (musl) wheel - 0.60s 60.0M 11.2M clean
3.13 alpine (musl) - - 0.56s 59.9M 11.2M -
3.13 slim (glibc) wheel 3.1s 0.49s 57M 11.2M clean
3.13 slim (glibc) - - 0.52s 57M 11.2M -
3.9 alpine (musl) wheel - 0.22s 44.2M 8.4M clean
3.9 alpine (musl) - - 0.23s 44.2M 8.4M -
3.9 slim (glibc) wheel 2.7s 0.19s 43M 8.4M clean
3.9 slim (glibc) - - 0.19s 43M 8.4M -

This quickstart demonstrates how to make a basic GET request impersonating a specific browser (Chrome 110) and how to use a `Session` object for persistent connections with another impersonation profile (Safari 15.5) and cookie management. It includes basic error handling.

from curl_cffi import requests

def main():
    try:
        # Make a GET request impersonating Chrome
        response = requests.get('https://www.example.com', impersonate='chrome110')
        response.raise_for_status()  # Raise an exception for HTTP errors (4xx or 5xx)
        print(f"Status Code: {response.status_code}")
        print("Response Header:\n", response.headers)
        print("First 500 chars of Response Body:\n", response.text[:500])

        # Using a session for persistent connections and cookies
        with requests.Session() as s:
            s.impersonate = 'safari15_5'
            res_session = s.get('https://httpbin.org/cookies/set/sessioncookie/123')
            print(f"\nSession Status Code: {res_session.status_code}")
            print(f"Session Cookies: {s.cookies.get('sessioncookie')}")

    except requests.exceptions.RequestError as e:
        print(f"An error occurred: {e}")

if __name__ == '__main__':
    main()