Android Emulator Proxy Setup: Studio, ADB, and Auth
Android emulator proxy setup three ways: Extended Controls, the -http-proxy flag and adb global http_proxy, with auth limits and an in-emulator exit IP check.
An android emulator proxy is how you make an AVD browse and run apps as if it sat in Germany or Japan, and the three common ways to set one look interchangeable while behaving very differently. One routes every TCP connection the virtual device opens and accepts a username and password. Another only reaches apps that choose to honour Android's system proxy, and it has nowhere to put credentials. This guide sets up all three against SparkProxy's gateway, shows which apps slip past each one, and finishes with checks that prove the exit country from inside the emulator instead of assuming it.
Behaviour described here comes from Google's emulator documentation (the proxy page was last updated 12 March 2026) and the Dart API reference, both read on 30 October 2026. The newest emulator build in Google's release notes that day was 37.2.13, dated 28 October 2026.
Android emulator proxy methods side by side
Google's emulator proxy documentation splits the job into two mechanisms, and everything else in this post follows from that split.
The Emulator Proxy runs inside the emulator process on your host machine. It catches every TCP connection the virtual device makes and forwards it to an HTTP proxy, so it doesn't matter which networking library an app was built with. Extended Controls and the -http-proxy flag both configure this one.
The Android System Proxy is a setting inside Android. Apps read it, and apps are free to ignore it. The Wi-Fi panel in the emulator's Settings app and adb shell settings put global http_proxy both set this one.
| Extended Controls | `-http-proxy` flag | adb global `http_proxy` | |
|---|---|---|---|
| Mechanism | Emulator Proxy | Emulator Proxy | Android System Proxy |
| Traffic covered | All TCP | All TCP | HTTP and HTTPS from apps that honour it |
| Username and password | Yes, in the panel (see below) | Yes, inside the URL | No field exists: whitelist or local relay |
| Persists | Saved per AVD, restored on restart | Only for that launch | In the device settings until deleted |
| Takes effect | After an emulator restart | At launch | No restart; relaunch open apps to be sure |
| Host bypass list | None | None | Not settable through this key |
| UDP | Not redirected | Not redirected | Not covered |
| HTTPS inspection with a debugging proxy | No | No | Yes, with a CA installed |
Google also states that the two mechanisms are mutually exclusive, with only one enabled at a time. Choose one per run and clear the other, or you'll spend an afternoon debugging a configuration you forgot you had.
A small trap if you read Google's page yourself: its comparison table lists "Network Layer" under the System Proxy and "Application Layer" under the Emulator Proxy. The prose directly below says the opposite, and the prose is the one that matches reality. The Emulator Proxy forwards all TCP traffic; the System Proxy handles only HTTP and HTTPS.
The short verdict for geo testing: use the -http-proxy flag by default, because it is scriptable, catches every TCP client and carries credentials. Extended Controls is the same mechanism behind a GUI, which suits a manual QA pass inside Android Studio. Reach for the adb route only when you also need a debugging proxy in the chain, or when the thing under test is how your app behaves with a system proxy set.
Prove the credentials from the host first
Many "the emulator ignores my proxy" problems are credential or whitelist failures that would break on the host too. Prove the proxy before an AVD is involved.
curl -sS -x http://USERNAME:PASSWORD@gateway.sparkproxy.io:11000 https://ipinfo.io/json
A working proxy returns JSON with ip, country and org for the exit. We ran the same request from our own build server on 30 October 2026 with no credentials, then with deliberately wrong ones, and curl 8.15.0 printed the identical line both times:
curl: (56) CONNECT tunnel failed, response 407
A plain-HTTP request with the bad credentials came back as HTTP/1.1 407 Proxy Authentication Required. A wrong port fails one step earlier, at the TCP connect, and on that run read curl: (7) Failed to connect to gateway.sparkproxy.io port 11999 after 2827 ms: Could not connect to server. Keep those three in mind, because they come back inside the emulator under different names.
If you'd rather script the check and assert the country, this does the same thing in Python with requests:
import sys
import requests
PROXY = "http://USERNAME:PASSWORD@gateway.sparkproxy.io:11000"
EXPECTED_COUNTRY = "DE"
def exit_country(proxy_url: str) -> str:
resp = requests.get(
"https://ipinfo.io/json",
proxies={"http": proxy_url, "https": proxy_url},
timeout=20,
)
resp.raise_for_status()
data = resp.json()
print(f"exit ip {data.get('ip')} country {data.get('country')} org {data.get('org')}")
return data.get("country", "")
if __name__ == "__main__":
try:
country = exit_country(PROXY)
except requests.exceptions.ProxyError as exc:
sys.exit(f"proxy refused the tunnel, check credentials or whitelist: {exc}")
if country != EXPECTED_COUNTRY:
sys.exit(f"expected {EXPECTED_COUNTRY}, proxy exits in {country}")
The SparkProxy gateway listens on port 11000 for HTTP and HTTPS with Random Rotation, 11002 for Sticky Sessions and 13000 for SOCKS5. Google documents the emulator's proxy input as an http:// URL only, so the AVD uses 11000 or 11002. Port 13000 is for other tools on the host.
Scraping at scale? Skip the blocks.
Fast, unblockable datacentre proxies with unlimited bandwidth.
Method 1: Extended Controls in Android Studio
This is the GUI route, and the right one when a tester wants to click through an app by hand.
- Start the AVD from Device Manager.
- Click the three-dot More button on the emulator toolbar to open Extended controls.
- Go to Settings, then the Proxy tab.
- Choose manual proxy configuration and enter host
gateway.sparkproxy.io, port11000(or11002for a sticky exit). - Tick proxy authentication and enter your SparkProxy username and password, then apply.
- Close and restart the emulator.
On step 5: recent builds show a proxy-authentication option with login and password fields, but Google's page only says to "manually enter your HTTP proxy configuration" and doesn't list the fields. If your build lacks them, use the launch flag or an IP whitelist instead.
Step 6 is the one people skip. Google says settings applied through Extended Controls only take effect after a restart, and that the emulator saves them for that device and restores them on every later boot. So a colleague who launches that AVD next week egresses from Germany without knowing it. Naming AVDs by exit country, Pixel_8_API_35_DE for instance, prevents that.
Two more behaviours from the same page:
- Android Studio seeds it once. When the emulator is launched from Studio, it reads the IDE's own proxy settings (Settings > Appearance & Behavior > System Settings > HTTP Proxy) one time to fill in the Emulator Proxy. A Studio install configured for a corporate proxy will therefore start new AVDs behind that corporate proxy. Those IDE settings never touch the Android System Proxy.
- There's no bypass list. The Emulator Proxy has no "no proxy for" setting. If your debug build talks to a backend on your machine through
10.0.2.2, the emulator's alias for host loopback, test that path early, because Google documents no way to exclude hosts.
Method 2: the -http-proxy launch flag
The flag configures the same Emulator Proxy from the command line, which makes it the method to script. List your AVDs, then launch one with the proxy URL:
emulator -list-avds
emulator @Pixel_8_API_35 -http-proxy http://USERNAME:PASSWORD@gateway.sparkproxy.io:11000
On Windows the binary sits under the SDK in your local app data folder:
& "$env:LOCALAPPDATA\Android\Sdk\emulator\emulator.exe" -avd Pixel_8_API_35 `
-http-proxy http://USERNAME:PASSWORD@gateway.sparkproxy.io:11000
Google's command-line reference says the flag "makes all TCP connections through a specified HTTP/HTTPS proxy", accepts http://server:port or http://username:password@server:port, and lets you drop the http:// prefix.
Four details decide whether this works first time.
The environment variable is a hidden input. If you don't pass the flag, the emulator looks up http_proxy in its environment and uses any value that matches the proxy format. Corporate laptops often export one in a shell profile, so an AVD can be proxied before you've configured anything. Run echo $http_proxy (or $env:http_proxy in PowerShell) before you blame the gateway.
Keep the password free of URL syntax. The emulator splits user, password, host and port itself. Google's release notes record a fix for "-http-proxy switch is parsed incorrectly" (Issue 314614435) in emulator 34.1.20 from April 2024, which tells you this parser has had bugs before. A credential containing @, : or / invites trouble, and we haven't verified how current builds treat percent-encoding. Generate a password without those characters.
One flag per host, in practice. The same reference adds: "When running multiple emulators, this flag should be set on the first launch." Since emulator 36.5 several AVDs on one host share a virtual network and receive consecutive addresses from 10.0.2.16 upward, according to Google's network address page. Don't plan on two simultaneous AVDs on one machine exiting in two different countries through two flag values. Give each country its own runner or container, or prove the separation on your build with the checks in the exit IP section below.
Pick the port for the session shape. Port 11002 gives you a Sticky Session, so swap it in when the flow under test is a login or checkout that must keep one IP.
Method 3: adb global http_proxy, whitelist or relay
This route writes the Android System Proxy directly, with no GUI and no restart:
adb devices
adb shell settings put global http_proxy gateway.sparkproxy.io:11000
adb shell settings get global http_proxy
# clear it when the run is over
adb shell settings delete global http_proxy
Android's Settings.Global reference defines the key http_proxy (added in API level 17) as "Host name and port for global http proxy", with a colon between them. That is the entire format. There is no slot for a username or a password, and the ProxyInfo class that Android hands to apps exposes host, port, an exclusion list and a PAC URL, with no credential fields. Requests therefore reach the gateway without a Proxy-Authorization header and get the 407 from the host check. Whether a particular app can answer that challenge is up to the app, so don't build a test on it.
The value stays in the device's settings until you delete it or wipe the AVD, and a forgotten one pointing at nothing looks exactly like "the emulator has no internet".
There are two ways to make this method authenticate.
IP whitelist. Emulator traffic leaves through your host's network stack, so the gateway sees the public IP of your workstation or CI runner, never 10.0.2.15. Find it with curl https://api.ipify.org on the host and add it to the whitelist in your SparkProxy dashboard. It holds until that address changes, which on CI runners with ephemeral IPs means intermittent failures that look random. Our explainer on IP whitelisting for proxies covers the trade-off against credentials. One side effect: with no username on the request, any option a provider encodes in the username can't travel with it, so check how your country choice is applied for whitelisted access.
A local relay that adds the credentials. Point the global proxy at 10.0.2.2, which Google documents as the alias for your host's loopback interface, and run a tiny relay on the host that inserts the header and forwards to the gateway:
import asyncio
import base64
LISTEN_HOST, LISTEN_PORT = "127.0.0.1", 8899
UPSTREAM_HOST, UPSTREAM_PORT = "gateway.sparkproxy.io", 11000
AUTH = b"Proxy-Authorization: Basic " + base64.b64encode(b"USERNAME:PASSWORD")
async def pipe(reader: asyncio.StreamReader, writer: asyncio.StreamWriter) -> None:
try:
while data := await reader.read(65536):
writer.write(data)
await writer.drain()
except ConnectionError:
pass
finally:
writer.close()
async def handle(client_r: asyncio.StreamReader, client_w: asyncio.StreamWriter) -> None:
try:
head = await client_r.readuntil(b"\r\n\r\n")
except (asyncio.IncompleteReadError, asyncio.LimitOverrunError):
client_w.close()
return
first_line, _, rest = head.partition(b"\r\n")
up_r, up_w = await asyncio.open_connection(UPSTREAM_HOST, UPSTREAM_PORT)
up_w.write(first_line + b"\r\n" + AUTH + b"\r\n" + rest)
await up_w.drain()
await asyncio.gather(pipe(client_r, up_w), pipe(up_r, client_w))
async def main() -> None:
server = await asyncio.start_server(handle, LISTEN_HOST, LISTEN_PORT)
print(f"relay {LISTEN_HOST}:{LISTEN_PORT} -> {UPSTREAM_HOST}:{UPSTREAM_PORT}")
async with server:
await server.serve_forever()
asyncio.run(main())
Then adb shell settings put global http_proxy 10.0.2.2:8899. The relay rewrites only the first request on each client connection. HTTPS opens with a CONNECT and becomes an opaque tunnel, so that covers nearly all app traffic; a plain-HTTP keep-alive connection would send later requests without the header.
Why use this method at all? Google documents the System Proxy as the route that allows HTTPS inspection by a debugging proxy once its certificate is installed; chain the debugger's upstream to the gateway for readable traffic from the exit country. Our post on proxies for mobile app testing teams explains when that pairing is worth its fragility.
Apps that ignore the system proxy
Google's page carries a one-line warning that the System Proxy "is used by web browsers and most applications, but some apps may ignore the system's proxy settings." In practice this is the main reason a geo test passes in Chrome and fails in the app.
The clearest documented case is Flutter. Apps that make requests with dart:io's HttpClient decide on a proxy through its findProxy callback, and the Dart API reference (version 3.13.7 when we read it) is blunt: "If this function is not set, direct connections will always be used." The helper findProxyFromEnvironment reads http_proxy, https_proxy and no_proxy environment variables, which Android's global proxy setting does not populate. So a Flutter build on the adb route egresses from your real IP while Chrome on the same AVD reports Germany.
| Client inside the AVD | Honours the System Proxy (adb, Wi-Fi panel) | Caught by the Emulator Proxy (flag, Extended Controls) |
|---|---|---|
| Chrome and WebView | Yes | Yes |
| `HttpURLConnection`, OkHttp with defaults | Yes, through the default ProxySelector | Yes |
| OkHttp built with `Proxy.NO_PROXY` | No, by design | Yes |
| Flutter `dart:io` HttpClient without `findProxy` | No | Yes |
| NDK code opening its own TCP sockets | No | Yes |
| Anything over UDP: QUIC, WebRTC media, game traffic | No | No, UDP is not redirected |
The fix for every "No" in the middle column is to move that test to the Emulator Proxy, which intercepts below the app. The UDP row is different: no proxy fixes it, as the DNS and UDP section explains.
Reading the exit IP from inside the emulator
The host check proves the proxy works, not that the AVD or your app uses it. Each probe below answers a different question.
Probe 1, Chrome. Open an IP echo page in the emulator's browser from the host:
adb shell am start -a android.intent.action.VIEW -d "https://ipinfo.io/json"
That tells you what a client that honours the System Proxy sees. On a fresh AVD Chrome may show its first-run screens instead; dismiss them once and save a snapshot.
Probe 2, the shell. Commands started through adb shell don't read the System Proxy setting, so a shell request only goes through the proxy when the Emulator Proxy is active. If the image's toybox ships nc (check with adb shell which nc), a raw request is enough:
adb shell "printf 'GET /ip HTTP/1.1\r\nHost: httpbin.org\r\nConnection: close\r\n\r\n' | nc httpbin.org 80"
Read the two probes together. Chrome and the shell both in Germany means the Emulator Proxy is on and covers everything TCP. Chrome in Germany with the shell showing your office IP means only the System Proxy is set, and any app from the "No" rows above will leak. Both showing your office means nothing took effect.
Probe 3, your own app. The only probe that exercises the real code path. Add a debug-only call to an IP echo endpoint using the same HTTP client your production code uses, log the result under a fixed tag, and read it from the host:
adb logcat -s GeoProbe
To read the browser result in a script rather than on screen, dump the view hierarchy and pull it, as the CI script below does:
adb shell uiautomator dump /sdcard/ui.xml
adb pull /sdcard/ui.xml
DNS and UDP: what the tunnel does not carry
DNS. Google's DNS page explains that at startup the emulator reads up to four of your host's DNS servers and aliases them at 10.0.2.3 through 10.0.2.6. Under the Emulator Proxy, then, Android resolves a hostname through your host's resolvers first, and only afterwards opens the TCP connection that gets tunnelled. Two consequences follow, which we infer from that design rather than measured: your local resolver sees every lookup, and any DNS-steered answer (which CDN edge, which regional API hostname) is chosen for your location, not the exit's. The server still sees the exit IP on the connection, so geo logic keyed on the client address reads Germany correctly; only DNS-based routing is skewed.
When a test depends on DNS steering, use the System Proxy route. A standard HTTP client sends the hostname inside its CONNECT request, so resolution happens at the proxy's end. The emulator's -dns-server flag changes which resolver is asked, not where the query comes from. Our DNS leak testing guide shows how to confirm where lookups go.
UDP. Google states that "the Emulator Proxy doesn't support UDP redirection" and stops there, without saying whether UDP is dropped or sent out directly. Treat it as outside the tunnel. QUIC, the transport under HTTP/3, runs over UDP, and so does WebRTC media. Reload an IP echo page several times and watch for your real address, and test any calling feature with a WebRTC leak page. Background on both is in what HTTP/3 and QUIC proxies are and what WebRTC leaks are.
An HTTP proxy, from any provider, carries TCP only. If a feature needs UDP to leave from the target country (calls, some multiplayer games), no emulator proxy setting will do it; that needs a VPN-style tunnel on the host.
Rotation, country and concurrency for test runs
Rotation. On port 11000, Random Rotation can hand each new connection a different IP. That is fine for a browse-and-check pass where every screen stands alone. It is wrong for sign-in: an app opens several connections in parallel while logging in, and a backend that ties a session to an address watches it hop between IPs and may challenge or drop it. Use Sticky Sessions on port 11002, which hold one IP for five minutes. Five minutes is shorter than a typical manual session, so re-read the exit at the start of each test case, or keep each scripted flow under the window. More detail is in what a sticky session proxy is.
Country. SparkProxy covers 80+ countries; use the credentials for the country you're testing. Remember that the exit IP changes nothing about the device itself. Set the AVD's language, region and time zone in Android Settings to match, or you'll file "localization bugs" that are really two settings out of step. The browser-side version of this work is covered in our localization testing guide.
Concurrency. SparkProxy plans are priced by plan and concurrency, starting from $75/month (USD, read on sparkproxy.io on 30 October 2026), with unlimited bandwidth included. The meter that matters is open connections, not gigabytes, so an AVD pulling APKs and video costs nothing extra. Measure your peak instead of guessing it: run the heaviest screen in your app and count established connections to the gateway port on the host.
(Get-NetTCPConnection -RemotePort 11000 -State Established).Count
lsof -nP -iTCP:11000 -sTCP:ESTABLISHED | tail -n +2 | wc -l
Multiply the peak by the number of parallel AVDs and add room for retries; our explainer on concurrent connections in proxies covers how the count works. The 24-hour free trial needs no credit card, which is enough to take that measurement before buying; 3, 6 and 12 month terms take 5%, 10% and 15% off.
A headless CI run that asserts the exit country
A nightly geo suite should refuse to run its tests if the exit is wrong, and it should keep evidence of what it saw. This script boots a headless AVD with the flag, waits for boot, reads the country through Chrome, then hands over to your instrumented tests.
import html
import os
import re
import subprocess
import sys
import time
from pathlib import Path
SDK = Path(os.environ.get("ANDROID_SDK_ROOT", str(Path.home() / "Android" / "Sdk")))
EXE = ".exe" if os.name == "nt" else ""
EMULATOR = SDK / "emulator" / f"emulator{EXE}"
ADB = SDK / "platform-tools" / f"adb{EXE}"
AVD = os.environ.get("AVD_NAME", "Pixel_8_API_35_DE")
PROXY = os.environ["SPARK_PROXY_URL"] # http://USERNAME:PASSWORD@gateway.sparkproxy.io:11002
EXPECTED = os.environ.get("EXPECTED_COUNTRY", "DE")
EVIDENCE = Path("evidence")
EVIDENCE.mkdir(exist_ok=True)
def adb(*args: str, check: bool = True) -> str:
out = subprocess.run([str(ADB), *args], capture_output=True, text=True, check=check)
return out.stdout.strip()
def boot() -> subprocess.Popen:
proc = subprocess.Popen(
[str(EMULATOR), "-avd", AVD, "-no-window", "-no-audio", "-http-proxy", PROXY],
stdout=subprocess.DEVNULL,
stderr=subprocess.STDOUT,
)
adb("wait-for-device")
deadline = time.time() + 300
while time.time() < deadline:
if adb("shell", "getprop", "sys.boot_completed", check=False) == "1":
return proc
time.sleep(5)
proc.kill()
sys.exit("emulator did not finish booting within 300 s")
def exit_country(attempts: int = 3) -> str:
for n in range(attempts):
adb("shell", "am", "start", "-a", "android.intent.action.VIEW", "-d", "https://ipinfo.io/json")
time.sleep(8)
adb("shell", "uiautomator", "dump", "/sdcard/ui.xml")
local = EVIDENCE / f"ui-{n}.xml"
adb("pull", "/sdcard/ui.xml", str(local))
text = html.unescape(local.read_text(encoding="utf-8", errors="ignore"))
match = re.search(r'"country"\s*:\s*"([A-Z]{2})"', text)
if match:
return match.group(1)
return ""
if __name__ == "__main__":
emu = boot()
try:
country = exit_country()
print(f"exit country {country or 'unreadable'}, expected {EXPECTED}")
if country != EXPECTED:
sys.exit("exit country check failed, tests not started; see evidence/")
result = subprocess.run(["./gradlew", "connectedCheck"])
sys.exit(result.returncode)
finally:
adb("emu", "kill", check=False)
emu.wait(timeout=60)
Notes on running it for real:
- Failure handling. A 407 or a dead relay shows up as a Chrome error page, no country matches, and after three attempts the job stops before any test runs. Archive
evidence/as a CI artifact. - Scheduling. Run one job per country as a matrix, each on its own runner, which also sidesteps the multiple-emulator note under Method 2. Inject
SPARK_PROXY_URLandEXPECTED_COUNTRYper entry from your secret store. - Session length. If
connectedCheckoutlasts the five-minute sticky window on port 11002, the IP can change mid-suite, so split long suites or re-check the exit per test class. - Snapshots. Bake the dismissed Chrome first-run into the AVD's snapshot, or the probe reads a welcome screen.
Error strings and which ones a proxy can fix
Work down this list in order: it starts with the checks that take seconds. Chrome's error names come from Chromium's net_error_list.h.
| Where you see it | Exact text | What it usually means | Fixed by proxy settings? |
|---|---|---|---|
| curl on the host | `curl: (56) CONNECT tunnel failed, response 407` | Wrong credentials, or host IP not whitelisted | Yes: credentials or whitelist |
| curl on the host | `curl: (7) Failed to connect to gateway.sparkproxy.io port ...` | Wrong port, or outbound traffic blocked by a firewall | Yes: port or firewall rule |
| adb | `adb: no devices/emulators found` | AVD not booted yet, or adb server not running | No, not a proxy issue |
| Chrome in the AVD | `ERR_PROXY_CONNECTION_FAILED` | Chromium couldn't resolve or connect to the proxy itself: a host typo, or the relay on 10.0.2.2 isn't running | Yes: host, port, relay |
| Chrome in the AVD | `ERR_TUNNEL_CONNECTION_FAILED` | The proxy refused the `CONNECT`; on the adb route this is usually the missing credentials | Yes: whitelist or relay |
| Chrome in the AVD | `ERR_NAME_NOT_RESOLVED` | Lookup failed on the host's resolvers | Partly: try `-dns-server`; the proxy isn't involved |
| Your app | Home-country content while Chrome shows the target country | App ignores the System Proxy | Yes: switch to the Emulator Proxy |
| Your app | Right exit country, content still wrong | Device locale, time zone, account region or store country | No: change device or account settings |
| Calls or games | Real IP seen, or the feature fails | UDP traffic, which no HTTP proxy carries | No: needs a host-level tunnel |
Chromium's source draws a useful line between the two proxy errors. PROXY_CONNECTION_FAILED covers failures "either in resolving its name, or in connecting a socket to it" and explicitly excludes failures during the CONNECT method. So that error means the AVD never reached the gateway, and ERR_TUNNEL_CONNECTION_FAILED means it reached the gateway and was turned away. They send you to opposite halves of the setup.
Frequently asked questions
FAQ
Yes, through the Emulator Proxy. Pass -http-proxy http://USERNAME:PASSWORD@gateway.sparkproxy.io:11000 at launch, or fill in the authentication fields in Extended Controls > Settings > Proxy and restart the emulator. The adb global proxy setting has no credential field at all.
The http_proxy global setting stores only a host and port, so requests reach the proxy without a Proxy-Authorization header and are refused with 407. Whitelist your host's public IP with the provider, or point the setting at a local relay on 10.0.2.2 that adds the credentials.
Not through its built-in proxy options. Google documents the -http-proxy value and the Extended Controls proxy as HTTP proxy configuration, so use SparkProxy's HTTP port 11000, or 11002 for Sticky Sessions, rather than the SOCKS5 port 13000.
Your app's networking ignores the Android System Proxy, which is common with Flutter's dart:io HttpClient, OkHttp built with Proxy.NO_PROXY, and native sockets. Launch the emulator with -http-proxy instead, because the Emulator Proxy intercepts every TCP connection regardless of library.
Open https://ipinfo.io/json in the emulator's Chrome with adb shell am start, run a raw request from adb shell if the image has nc, and log an IP echo call from your own app. If Chrome and the shell disagree, only the System Proxy is active.
Don't rely on it on a single host. Google's reference says that with multiple emulators running, -http-proxy should be set on the first launch, and since emulator 36.5 AVDs share a virtual network. Run each country on its own CI runner or container and assert the exit country in each.
Get 20% off your first month
Premium datacentre proxies with unlimited bandwidth. Use the code at checkout.
Save up to 15% more on quarterly, half-yearly and yearly plans
Related articles
How to See Google Ads in Other Countries Without Clicking
How to see Google ads in other countries: Ads Preview first, then a clean browser behind a country proxy, no clicks, and a daily script that logs ad copy.
Crawlee Proxy Configuration: Rotation That Keeps Sessions
Crawlee proxy setup with proxyUrls, newUrlFunction and tieredProxyUrls: how SessionPool binds a proxy, and how to stop the session-retired loop on login crawls.
Build Your Own Rank Tracker With Python and Proxies
Build your own rank tracker in Python: a keyword CSV, SERP fetches through rotating proxies or the Scraping API, Postgres history and a weekly movement report.
