Choose the region
Track Google.de through Europe, Yandex through CIS, Baidu through China. World MIX covers the rest and the biggest pools.
Search engines show results by region, so a rank checker needs an IP from the right country for every query batch. Resi Core gives a home address in any of 200+ countries right in the connection string, while unlimited packages with a country filter cover large keyword lists. A separate section covers tracking Google AI answers, which also depend on the country and city.
Positions depend on where the query comes from. The pool has to answer that question first.
Addresses from the country you track
Resi Core: the country goes in the login; on packages a country column in the CSV export
A new IP for each query batch
Resi Core changes the address on every request, and packages replace about a third of addresses daily
Enough parallel queries for a large keyword list
No thread limit on Resi, 500 to 5,000 threads on packages
Works with the checker you already run
Resi by login and password in the string, packages by IP; A-Parser, Key Collector and Rank Tracker accept HTTP and SOCKS5
A results page weighs little, so one gigabyte covers thousands of queries. The price per GB drops with the order size.
| Order size | Resi Core | Resi Prime |
|---|---|---|
| 1 GB+ | $0.85 | $2.29 |
| 10 GB+save 7% | $0.79 | $2.09 |
| 50 GB+ | $0.72 | $1.95 |
| 100 GB+ | $0.65 | $1.75 |
| 500 GB+ | $0.57 | $1.55 |
| 1000 GB+ | $0.49 | $1.39 |
Prices are in US dollars per 1 GB. You buy whole gigabytes, starting with a single one.
Pool
You save 7% ($1.50) against the 1 GB+ price
Add 25 GB and every gigabyte drops to $0.72
Buy this amountYou can use the gigabytes for 3 months from the purchase date; whatever is left after that is written off.
Track Google.de through Europe, Yandex through CIS, Baidu through China. World MIX covers the rest and the biggest pools.
Take the CSV export and keep the rows whose country column matches the market you track; the ready builder writes a per-country ip:port file for you.
Rank checkers take a proxy file or a URL. Set the refresh to 10 minutes and the list stays current on its own.
Set DOMAIN and COUNTRY, put your keywords in keywords.txt. The script asks the search engine through a proxy of that country and writes the position of your domain to a CSV. Feed it a clean pool from the proxy checker.
#!/usr/bin/env python3
"""Search-position checker for Advanced.name proxies.
Reads the CSV proxy export, keeps one country, then asks a search engine for each
keyword through a proxy from that country and reports where your domain sits in
the results. Standard library only. Edit CONFIG and run: python3 rank.py
"""
# ================================ CONFIG ====================================
CSV_URL = "https://get.advanced.name/YOUR_LIST_ID.csv" # CSV export link from your dashboard
COUNTRY = "DE" # ISO 3166-1 alpha-2: positions are read from this country
MIN_SPEED = 3 # keep proxies with speed >= this value
DOMAIN = "example.com" # the domain whose position you track
KEYWORDS_FILE = "keywords.txt" # one keyword per line
SEARCH_URL = "https://www.bing.com/search?q={q}&count=50&setlang={cc}" # {q}=keyword, {cc}=country
OUT_FILE = "positions.csv" # keyword;position;proxy is written here
TIMEOUT = 20
# ============================================================================
import csv
import io
import re
import sys
import urllib.parse
import urllib.request
COL_IP, COL_PORT, COL_COUNTRY, COL_SPEED = 0, 1, 2, 3
UA = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 Chrome/124.0 Safari/537.36"
HREF = re.compile(r'href="https?://([^/"]+)', re.I)
def load_proxies():
raw = urllib.request.urlopen(CSV_URL, timeout=30).read().decode("utf-8", "replace")
pool = []
for row in csv.reader(io.StringIO(raw), delimiter=";"):
if len(row) <= COL_SPEED or not row[COL_PORT].strip().isdigit():
continue
if COUNTRY and row[COL_COUNTRY].strip().upper() != COUNTRY.upper():
continue
if int(row[COL_SPEED] or 0) < MIN_SPEED:
continue
pool.append(row[COL_IP].strip() + ":" + row[COL_PORT].strip())
return pool
def position(html, domain):
"""First place the domain shows up among the result links, or 0 if absent."""
rank, seen = 0, set()
for host in HREF.findall(html):
host = host.lower()
if "bing.com" in host or "microsoft.com" in host or host in seen:
continue
seen.add(host)
rank += 1
if host == domain or host.endswith("." + domain):
return rank
return 0
def main():
proxies = load_proxies()
if not proxies:
sys.exit("No proxies for COUNTRY=%r." % COUNTRY)
with open(KEYWORDS_FILE, encoding="utf-8") as fh:
keywords = [line.strip() for line in fh if line.strip()]
print("proxies: %d (%s), keywords: %d" % (len(proxies), COUNTRY, len(keywords)))
with open(OUT_FILE, "w", encoding="utf-8", newline="") as out:
writer = csv.writer(out, delimiter=";")
writer.writerow(["keyword", "position", "proxy"])
for i, keyword in enumerate(keywords):
proxy = proxies[i % len(proxies)]
url = SEARCH_URL.format(q=urllib.parse.quote(keyword), cc=COUNTRY.lower())
handler = urllib.request.ProxyHandler({"http": "http://" + proxy, "https": "http://" + proxy})
req = urllib.request.Request(url, headers={"User-Agent": UA, "Accept-Language": COUNTRY.lower()})
try:
html = urllib.request.build_opener(handler).open(req, timeout=TIMEOUT).read().decode("utf-8", "replace")
pos = position(html, DOMAIN.lower())
except Exception as exc:
pos = "err: %s" % exc.__class__.__name__
writer.writerow([keyword, pos, proxy])
print("%-40s %s" % (keyword[:40], pos))
print("written %s" % OUT_FILE)
if __name__ == "__main__":
main()
Paste a Resi line from your dashboard, set DOMAIN and put the keywords in keywords.txt. The script opens the results in a real Chromium through the proxy, waits for the AI block to finish loading and writes to CSV whether the block is there, which domains it cites and whether yours is among them. It closes the EU cookie banner itself and flags a captcha or a proxy error on a separate line. Google changes its markup, so the block heading lives in LABELS.
#!/usr/bin/env python3
"""AI Overview tracker for Advanced.name residential proxies.
Opens Google results for every keyword through a Resi Core or Resi Prime line
in a real Chromium, because the AI block is drawn by JavaScript. Records
whether the page shows the AI block and which domains it cites.
Setup: pip install playwright && python -m playwright install chromium
"""
# ================================ CONFIG ====================================
PROXY = "res.advanced.name:9000:USER-type-residential-country-us:PASS" # host:port:login:password line from your dashboard
DOMAIN = "example.com" # the domain you want to see among the cited sources
KEYWORDS_FILE = "keywords.txt" # one keyword per line
SEARCH_URL = "https://www.google.com/search?q={q}&hl={hl}&gl={gl}" # {q}=keyword
HL = "en" # interface language of the results page
GL = "us" # country of the results, keep it equal to the country in the login
LABELS = "AI Overview|AI Mode" # heading text of the AI block, separated by |
CONSENT = "Reject all|Accept all" # cookie banner buttons in EU countries, in the HL language
OUT_FILE = "ai_overview.csv" # keyword;ai_block;cited;your_domain is written here
WAIT_MS = 4000 # time for the AI block to finish drawing
HEADLESS = True # set False to watch the browser
# ============================================================================
import csv
import sys
import urllib.parse
from playwright.sync_api import sync_playwright
FIND_BLOCK = """(labels) => {
const heads = [...document.querySelectorAll('h1,h2,h3,div,span')]
.filter(e => e.children.length === 0 && labels.includes(e.textContent.trim()));
if (!heads.length) return null;
let box = heads[0];
for (let i = 0; i < 8 && box.parentElement; i++) {
box = box.parentElement;
const links = [...box.querySelectorAll('a[href^="http"]')];
if (links.length >= 2 && box.innerText.length > 200) return links.map(a => new URL(a.href).hostname);
}
return [];
}"""
def proxy_of(line):
host, port, user, password = line.strip().split(":", 3)
return {"server": "http://%s:%s" % (host, port), "username": user, "password": password}
def bare(host):
host = host.lower()
return host[4:] if host.startswith("www.") else host
def main():
keywords = [k.strip() for k in open(KEYWORDS_FILE, encoding="utf-8") if k.strip()]
labels = [x.strip() for x in LABELS.split("|") if x.strip()]
consent = [x.strip() for x in CONSENT.split("|") if x.strip()]
rows = []
with sync_playwright() as pw:
browser = pw.chromium.launch(headless=HEADLESS, proxy=proxy_of(PROXY))
page = browser.new_page(locale=HL)
for kw in keywords:
url = SEARCH_URL.format(q=urllib.parse.quote_plus(kw), hl=HL, gl=GL)
try:
resp = page.goto(url, wait_until="domcontentloaded", timeout=60000)
if resp is None or resp.status >= 400:
status = "http %s" % (resp.status if resp else "none")
print("%-40s %s" % (kw[:40], status), file=sys.stderr)
rows.append([kw, status, "", ""])
continue
for name in consent:
button = page.get_by_role("button", name=name)
if button.count():
button.first.click()
page.wait_for_load_state("domcontentloaded")
break
if "/sorry/" in page.url:
print("%-40s captcha: change the session id in the login" % kw[:40], file=sys.stderr)
rows.append([kw, "captcha", "", ""])
continue
page.wait_for_timeout(WAIT_MS)
hosts = page.evaluate(FIND_BLOCK, labels)
except Exception as exc:
print("%-40s error: %s" % (kw[:40], str(exc).splitlines()[0]), file=sys.stderr)
rows.append([kw, "error", "", ""])
continue
if hosts is None:
print("%-40s no AI block" % kw[:40])
rows.append([kw, "no", "", ""])
continue
cited = sorted({bare(h) for h in hosts if "google." not in h})
mine = any(h == DOMAIN or h.endswith("." + DOMAIN) for h in cited)
print("%-40s AI block, %d sources, %s %s" % (kw[:40], len(cited), DOMAIN, "cited" if mine else "not cited"))
rows.append([kw, "yes", " ".join(cited), "yes" if mine else "no"])
browser.close()
with open(OUT_FILE, "w", encoding="utf-8", newline="") as f:
out = csv.writer(f, delimiter=";")
out.writerow(["keyword", "ai_block", "cited", "your_domain"])
out.writerows(rows)
print("saved", OUT_FILE)
if __name__ == "__main__":
main()
In A-Parser and Key Collector paste the ip:port list into the proxy field and set the check interval so one keyword hits one address at most once a day. Rank Tracker and similar desktop tools read a proxy file; point them at the list the builder writes per country.
Positions depend on where the query comes from, so keep a separate list per market: run one tracker instance per country and give each the addresses of that country only.
The script above asks the search engine through a country proxy and records your domain's place. Send the language and region parameters of the engine together with the proxy: the address sets the country, the parameters set the city.
Check the pool before a run with the proxy checker; for a light manual test the free proxy list works. A background primer on regional SERPs will live in the articles.
More and more often Google puts an AI block above the regular results: a short answer with links to sources. Part of the clicks goes there, so you need to know whether your site made it into the sources and which queries show no block at all. The block is built for the user location: for one query Berlin and Munich can see different sources.
A regular results scraper never sees this block: Google draws it with JavaScript after the page loads. You need a real browser, such as Chromium through Playwright, and a home IP in the right country. List addresses often hit a captcha on these results, which is why Resi Core and Resi Prime do the job here.
For country-level tracking Resi Core with the country code in the login is enough. When the client cares about a city, take Resi Prime with the city in the string and a fixed session per query batch: the whole batch then leaves from one address in that city.
Run the keyword list once a day or once a week: the AI block changes more slowly than regular positions. For every query write the date, the country or city, whether the block is there, which domains it cites and whether yours is among them. After a month you can see which pages the AI takes into its answer and which dropped out.
The second script above writes exactly these fields to CSV. The first script tracks regular positions in the same country, so both reports line up by query.

Track country results through Resi Core and city results through Resi Prime, and run large keyword lists every day on an unlimited package for less.
Country resultsBest fit
A home IP from the right country on every request. A results page weighs little, so one gigabyte covers thousands of queries.
from $0.49 per GB1 GB for $0.85
City results
City and region in the connection string give the results a user from that city sees.
from $1.39 per GB1 GB for $2.29
Large keyword lists
Tens of thousands of queries a day with no traffic meter. You pick the market country by the column in the CSV export.
$378 per monthor $2.10 per hour
Take 1 GB of Resi Core, set the country in the string and run the checker on live addresses.