Gemini network proxy: configure and diagnose a client connection
Route google-genai through an HTTP CONNECT proxy, keep TLS on, and tell a dead hop from an API refusal without treating a tunnel as Google access.
This guide is for developers who need google-genai 2.30.1 to leave through an HTTP CONNECT proxy. The checks used a loopback origin and a dummy hop, not a Google account. Top Proxy does not sell IPs, and there are no free keys here. A working tunnel is not API access.
01Pick one Gemini client and pin it
Gemini is a family of products. The consumer web app, the Gemini Developer API, Vertex AI or Gemini Enterprise Agent Platform, and the Gemini CLI do not share a proxy knob. Mixing them is how you debug a browser while your Python process still goes direct. This walkthrough stays on one client: the Google Gen AI Python SDK talking to the Gemini Developer API.
Gemini API docs, accessed 15 October 2026, last updated 13 October 2026 UTC, describe that API as the path from prompt to production. They do not document a proxy. Proxy wiring lives in the SDK README. Available regions, last updated 28 April 2026, is a country list for AI Studio and the API. A proxy does not add a missing region, a verified age, or a billing account.
Pin what you import. This run used Python 3.14.6, google-genai 2.30.1, httpx 0.28.1, and LibreSSL 3.3.6 as the openssl binary for a lab CA. PyPI listed 2.30.1 the same day. The installed client defaulted the Gemini Developer API to https://generativelanguage.googleapis.com/ and v1beta. Vertex uses a different constructor and a different host. Leave it alone here.
- Create a venv on the Python you will actually run. This machine used
/opt/homebrew/bin/python3.14. - Install
google-genai==2.30.1. Do not let a later wheel rewrite the HTTP stack under you. - Print versions before you touch a proxy. Keep the google-genai and httpx lines.

The README, Proxy section around the httpx default, says the SDK uses httpx for both sync and async clients. google-genai[aiohttp] is an extra for a faster async path, with trust_env=True to match httpx. That extra was not installed in this run. I am not going to pretend aiohttp routed anything. SOCKS5 is documented only after httpx[socks] and HttpOptions.client_args. Also untested here. HTTP CONNECT is the path that actually moved.
Pass api_key into genai.Client or set GEMINI_API_KEY / GOOGLE_API_KEY before init. API key setup is account work. The lab used the dummy string lab-not-a-google-key against a loopback origin. That key never left the machine toward Google.
02Set HTTPSPROXY before you construct Client
The README is blunt. Both httpx and aiohttp read urllib.request.getproxies from the environment. Set the proxy before you construct the client:
export HTTPS_PROXY='http://USER:PASS@PROXY_HOST:PROXY_PORT'
export SSL_CERT_FILE='/path/to/extra-ca.pem'
SSL_CERT_FILE is optional. Production Gemini on public CAs does not need a private CA file. The lab needed one because the origin certificate was minted locally. Installed 2.30.1 does extra work here. _api_client.py builds an SSL context with ssl.create_default_context(cafile=os.environ.get('SSL_CERT_FILE', certifi.where())) because, in the SDK authors' words, httpx does not pull SSL_CERT_FILE on its own. Export the variable before Client(...). Exporting it later will not retrofit a live client.
Userinfo in HTTPS_PROXY is proxy authentication. It is not your Gemini API key. Put the API key in Client(api_key=...) or in GEMINI_API_KEY. If you dump the proxy URL into a log, redact the password. Process listings still see the environment for a moment.
The same README shows SOCKS5 through httpx args, not through HTTPS_PROXY:
from google import genai
from google.genai import types
client = genai.Client(
api_key="YOUR_API_KEY",
http_options=types.HttpOptions(
client_args={"proxy": "http://USER:PASS@PROXY_HOST:PROXY_PORT"},
timeout=60000,
retry_options=types.HttpRetryOptions(attempts=1),
),
)
HttpOptions.timeout is milliseconds. 2.30.1 converts it with / 1000.0 before httpx. retry_options=None on this build means tenacity stops after one attempt. Troubleshooting still says the Python SDK retries transient errors up to four times with about 1s then 60s max. That page is dated 13 October 2026. The installed client does not do that unless you pass HttpRetryOptions. I would rather set attempts=1 while I diagnose a hop than watch a dead port get exponential backoff.
- Export only
HTTPS_PROXYif the target is HTTPS, which Gemini is. This run did not setHTTP_PROXY.urllib.request.getproxies()then returned{'https': 'http://127.0.0.1:62748'}. - Keep
NO_PROXYempty when you intend the proxy to see loopback. A leftoverNO_PROXY=localhostwill skip the hop and look like a success. - Construct
genai.Clientafter the environment is set. Close it when you are done.
Explicit client_args={'proxy': ...} is the other documented door. It also sent CONNECT in this run. Use it when a shell already owns HTTPS_PROXY and you do not want to fight it.
03Prove the route on a target you control
Status 200 is a poor witness. Direct and proxied calls can both return 200. You need a hop log or an origin stamp. The lab pointed HttpOptions.base_url at https://127.0.0.1:PORT/ so google-genai never opened generativelanguage.googleapis.com. The origin answered with JSON labeled SIMULATED. That is routing proof. It is not a model answer, a quota check, or a region pass.
Your own check can be smaller than a public lab. You need the installed client, a proxy you own, and a target you own. Loopback is enough. Do not turn verify off. Do not point this at Google until the hop is boring.
import os
from google import genai
from google.genai import types
os.environ["HTTPS_PROXY"] = "http://USER:PASS@PROXY_HOST:PROXY_PORT"
client = genai.Client(
api_key="YOUR_API_KEY",
http_options=types.HttpOptions(
timeout=60000,
retry_options=types.HttpRetryOptions(attempts=1),
),
)
try:
response = client.models.generate_content(
model="gemini-flash-latest",
contents="ping",
)
print(response.text)
finally:
client.close()
That snippet talks to Google if you leave base_url unset. The loopback check did not. Ports in the saved results are dead. Copy host and port from a proxy that is still up.
- Call once with no proxy environment and no
client_argsproxy. Direct returned simulated text. The dummy CONNECT proxy stayed idle.proxy_events=0. - Export
HTTPS_PROXYtohttp://127.0.0.1:PROXY_PORTand call again. The proxy loggedCONNECT 127.0.0.1:ORIGIN_PORT. The origin returned the same SIMULATED text.getproxiesshowed only the https key. - Repeat with
client_args={'proxy': 'http://127.0.0.1:PROXY_PORT'}and noHTTPS_PROXY. CONNECT happened again. Two doors, one tunnel type.

A browser What is my IP page shows the browser exit. It does not show this Python process. The proxy checker tells you whether a host answers HTTP or SOCKS5. Useful before you paste an endpoint. Still not google-genai.
04Keep TLS on and keep credentials out of logs
HTTPS through an HTTP proxy is CONNECT, then TLS to the origin through that tunnel. The proxy URL stays http://.... An https:// proxy URL would mean TLS to the proxy itself. This run did not measure that.
The lab CA was a one-day certificate with a subject alternative name for 127.0.0.1. SSL_CERT_FILE pointed at that CA. Verification stayed on. When the same proxied client ran without SSL_CERT_FILE, CONNECT still succeeded and httpx raised ConnectError wrapping CERTIFICATE_VERIFY_FAILED: unable to get local issuer certificate. The hop was fine. The trust store was not. That is the case people skip, then they add verify=False and call it done.

- Keep the default verify path. Pass a CA file only when your origin is private.
- Confirm CONNECT in the proxy log before you celebrate a TLS traceback. The traceback is not proof the proxy was skipped.
- Redact
Proxy-Authorization,x-goog-api-key, and proxy userinfo. The lab proxy recordedhas_proxy_authorization: falsebecause the dummy hop had no Basic auth. Your vendor hop probably does.
Do not copy the lab CA into the system trust store. This run never touched macOS trust. SSL_CERT_FILE in the child process was enough.
05Tell a dead hop from a Google-looking error
Troubleshooting splits client SDK issues from backend issues. Retry 429 and 503. Do not retry 400, 402, or 403. Those are invalid keys, empty prepay credits, or bad syntax, not a flaky socket. None of that fires if TCP never completes.
This run forced three failures on the same installed client.
A closed loopback port as client_args proxy raised ConnectError: [Errno 61] Connection refused in 0.001s. No CONNECT log. The origin never saw a request. That is a dead hop.
A simulated origin 403 through a living CONNECT hop raised ClientError: 403 PERMISSION_DENIED with body text that said SIMULATED. CONNECT was logged. The origin counted a hit. Transport worked. The application refused. On real Google that class of error is account, billing, or policy. It is not "buy a different proxy and the model appears."
A 200ms HttpOptions.timeout against a 1.5s origin sleep raised ReadTimeout: The read operation timed out at 0.205s. CONNECT was logged. The tunnel was up. The budget was not.

| Observation | Exception | CONNECT logged | Origin hit | What to change |
|---|---|---|---|---|
| Direct, trusted lab CA | none | no | yes | Control. Proxy must stay idle |
HTTPS_PROXY only | none | yes | yes | Route proven for this client |
client_args proxy | none | yes | yes | Explicit httpx proxy arg |
| Proxy up, no lab CA | ConnectError TLS issuer | yes | no | Fix CA. Do not disable verify |
| Dead proxy port | ConnectError refused | no | no | Host and port, not the API key |
| Simulated 403 through proxy | ClientError 403 | yes | yes | Account or request. Hop already worked |
| 200ms timeout, slow origin | ReadTimeout | yes | no finished body | Timeout budget, not CONNECT |
Mobile and datacenter catalog pages will not save you from that table. Mobile proxies and datacenter proxies change the IP you present. They do not rewrite google-genai error classes. HTTP is the protocol this SDK actually used. SOCKS5 stays a README extra until you install httpx[socks] and measure it.
06Match HTTP CONNECT to a catalog endpoint you can open
Pick a hop the vendor documents as HTTP, with username and password the vendor documents. Then prove google-genai against a fixture you own. Paid gateways were not called.
Oxylabs residential proxies, accessed 15 October 2026, document an HTTP entry. Decodo residential quick start, same day, documents gate.decodo.com:7000 as an HTTP proxy URL. The English catalog still lists that product as smartproxy-com. Compare those shapes on the live cards for Oxylabs and Smartproxy / Decodo. That is catalog fit, not a pool test.
- Match scheme to what 2.30.1 actually sent: HTTP CONNECT. An HTTPS-proxy-to-the-proxy URL is a different product.
- Put vendor prefixes on the proxy user, not on
GEMINI_API_KEY. - Check reachability with the proxy checker if you only need HTTP versus SOCKS on a host. Prove the SDK on your process.

Oxylabs sells residential, datacenter and mobile proxies. Residential self-serve starts at $6/GB for 5 GB/month. Buyer payments: cards, wire, AliPay, PayPal.

Decodo (formerly Smartproxy) sells residential and datacenter proxies. Residential: $4/GB without a monthly plan, or monthly packs from $3.75/GB for 3 GB. Payments: cards, PayPal, Alipay, Google/Apple Pay, crypto (with limits).

Make this client leave through a hop you can name, keep TLS on, and stop treating a tunnel as an entitlement.
How do I set a proxy for the Gemini Python SDK?
Does a working proxy mean Gemini will answer?
Should I turn TLS verification off?
Is SOCKS5 supported?
Why did troubleshooting mention retries I did not see?
Can I use a browser What is my IP page to prove the SDK?
Related articles
AI proxy vs LLM gateway: what is the difference?
Tell network forwarding, an API gateway, and model routing apart so you pick the layer that matches the job.
curl proxy setup: authentication, HTTPS CONNECT and reliable checks
Set an explicit curl proxy, keep -U apart from -u, send HTTPS through CONNECT without -k, and read exit codes against the route you actually used.
HTTPX proxy setup: authentication, environment and timeouts
Set an HTTPX proxy with proxy=, pass credentials, keep HTTPS on CONNECT, and tell environment defaults apart from explicit mounts and timeouts.



