TLDR: macOS system Python has no CA cert bundle.
curldoes. Route your one-off fetch scripts through acurlsubprocess — two lines, no dependencies, problem gone.
The Setup
I was running a full supply-chain audit across ~/Developer on June 16th — scanning every project for outdated, git-installed, or unpinned dependencies.
The script was scratch/dep_audit.py. It looped over every package, hit the registry, checked version dates, flagged anything stale.
I kicked it off and let it run.
The Wall
Seven hundred and eighty-seven lookup failures.
Every single external fetch had died silently with the same error:
[SSL: CERTIFICATE_VERIFY_FAILED] unable to get local issuer certificate
The script was logging-and-continuing — so it just kept going, recording failure after failure, and I got a completely useless audit report at the end.
787 times. Before anyone caught it.
Why It Happened
Here's the thing that got me: curl to the same URLs worked fine.
Same machine, same network. But stock Python's urllib? Dead on arrival.
The cause: Apple ships Python without running Install Certificates.command. So the system Python has no CA cert bundle — no way to verify HTTPS endpoints. curl uses the macOS system trust store, which does have everything wired up correctly.
(Worth knowing: Apollo's data ingestion daemons — a Fathom ingestion process and a Zoom ingestion process — were completely unaffected. They run httpx inside a uv-managed venv, which has certs. Same machine, right venv = works. Wrong interpreter = 787 failures. It's not the network — it's the interpreter.)
The Fix That Actually Worked
Route the fetch through curl as a subprocess. Two lines:
import subprocess, json
out = subprocess.run(["curl", "-sS", "--max-time", "30", url], capture_output=True, text=True)
data = json.loads(out.stdout)
No third-party dependencies. No venv required. curl handles TLS, Python handles the JSON. Done.
(The cleaner long-term fix is to wire in certifi — ssl.create_default_context(cafile=certifi.where()) — or just use a uv-managed interpreter that already has certs baked in. But for a one-off audit script, the curl subprocess is the fast, zero-dependency path and I'd make the same call again.)
The Discipline That Matters
This is the part I keep coming back to.
One probe fetch at the top of the script — before threading 787 lookups — would have caught this in two seconds.
# Sanity check before the loop
test = subprocess.run(["curl", "-sS", "https://pypi.org/pypi/requests/json"], capture_output=True, text=True)
assert test.returncode == 0, "Fetch probe failed — abort before looping"
I skipped it. Assumed the network would work. Burned the whole run.
Test one. Then thread hundreds.
Why This Matters to Me
I build a lot of one-off Python scripts — audit tools, scrapers, ingest pipelines. And I tend to trust that "Python on my Mac" just works.
It doesn't. Not for HTTPS. Not without a cert bundle.
Now every fetch script I write opens with a probe. And if I'm running outside a managed venv, I'm routing through curl. It's the boring, dependency-light path that actually runs.
P.S. If you've ever shipped a Python scraper to a client's Mac and gotten SSL errors you couldn't reproduce locally — same root cause. Their system Python has no certs either.