Python's requests library uses the certifi package's bundled root certificate list by default — an outdated certifi version, or a genuinely broken certificate chain on the server you're connecting to, are the two most common causes of this error.
The fix
pip install --upgrade certifi
If upgrading certifi doesn't resolve it, verify the target server's certificate chain independently (via an SSL checker or the OpenSSL command from our chain-verification guide) — the issue may genuinely be on the server side, not your Python environment. Avoid setting verify=False as a workaround outside of clearly controlled testing/development contexts, since it disables certificate validation entirely.
What the requests library's specific SSLError typically indicates
Python's requests library, built on urllib3 and ultimately OpenSSL, raises SSLError for the same underlying categories of TLS failure covered throughout this site — reading the specific exception message and underlying cause, visible in the full traceback, identifies which specific category (chain, hostname, expiry) applies to your situation.
How to specify a custom CA bundle without disabling verification
Passing a verify parameter pointing to a specific CA bundle file path, rather than setting verify to False, lets you validate against a specific trust source (useful for internal or private CAs) while still maintaining genuine certificate verification rather than disabling it entirely.
What the certifi package specifically provides for Python's certificate handling
The certifi package, which requests depends on, bundles a current, regularly updated CA certificate list — ensuring certifi itself is updated to its latest version (via pip install --upgrade certifi) resolves many SSLError instances stemming from an outdated bundled trust list.
How to debug an SSLError with more detail than the default exception message provides
Wrapping your request in a try/except block and printing the full exception's args or the underlying OpenSSL error reason reveals considerably more specific detail than the default, often truncated SSLError message alone provides.
A final note on dependency management
Pinning a specific, recent version of certifi in your project's dependency requirements, rather than leaving it unconstrained, ensures your deployed application consistently uses a reasonably current CA bundle rather than whatever version happened to be available at whatever time your dependencies were last installed.
What the full exception traceback typically reveals beyond the summary message
Python's full exception traceback, rather than just the final summary line, often includes the specific underlying OpenSSL error reason nested within it — reading the complete traceback, not just the last line, frequently reveals considerably more specific diagnostic detail than the outer SSLError message alone provides.
How to test whether the issue is specific to the requests library or more fundamental
Testing the same URL directly with Python's lower-level ssl module, bypassing requests entirely, isolates whether an issue is specific to how requests or urllib3 is configured, or reflects a more fundamental problem with Python's underlying SSL configuration itself.
A quick closing checklist
A quick closing checklist covers reading the full exception traceback for specific detail, updating the certifi package to its current version, and testing with Python's lower-level ssl module directly if the issue persists after basic troubleshooting.
How virtual environments specifically affect which certifi version is actually in use
Because Python virtual environments maintain their own independent package installations, a certifi update applied globally doesn't automatically propagate into an existing virtual environment — confirming you've updated certifi within the specific virtual environment your application actually runs in matters.