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 Common errors
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. Warnings
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`.
Install compatibility verified last tested: 2026-05-12 v0.13.0 installed · v0.15.0 latest
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 -
Imports
- requests wrong
import requests; requests.get(..., impersonate='chrome')correctfrom curl_cffi import requests - Session
from curl_cffi.requests import Session - AsyncSession
from curl_cffi import AsyncSession - WebSocket
from curl_cffi import WebSocket
Quickstart last tested: 2026-04-24
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()