• config
  • template

A-Parser recipe: check Google rankings in poisoned SERPs

Ready addTask body, a Python wrapper and rules for measuring Google positions with A-Parser SE::Google and Bypass poisoned SERP, plus proxy cost per 1,000 keys.

  • v1.0
  • updated 12 November 2026
  • MIT
  • Vladislav Krivorutško

Google's "poisoned" result pages look like a successful scrape: no error, no captcha, but empty, cut-off or unrelated results. This recipe sets up A-Parser's SE::Google so that a rank check finishes, and sorts every keyword into FOUND, NOT_FOUND or FAILED. The key rule: an empty or cut-off page is a failed measurement, never "your site is not in the results". Without that rule, a bad page looks like a lost position.

It comes as a ready addTask body (task-body.example.json) and a Python wrapper that sends it, waits, parses the result, retries the failed keywords and prints the table. In the author's test of 2026-11-12 (6 keywords x 8 runs against a browser in the same city, mobile), 83 % of the measurements were good with a proxy pool from the same country as the search, and the mean position error inside the browser's top 5 was 2 places. A measurement counted as good if it did not fail and at least 7 of the 10 domains matched the browser. That is the best case of one setup on one date; the section on accuracy has the other numbers.

Files, grouped by job

5 files · or all in one zip

Read me

1 file
  • README.md

    3-step run, status rules, API actions, traffic table, gotchas

    8.0 KB

Run it

2 files
  • task-body.example.json

    The addTask body for SE::Google: 3 pages, mobile, proxy pool, smartSessionWarmup, escaped result format

    1.5 KB
  • aparser_rank_check.py

    Wrapper: Python 3.8+, standard library only; --dry, --parse and --selftest need no network

    33 KB

Try it first

2 files
  • keywords.example.txt

    10 synthetic keywords

    272 B
  • sample-results.txt

    A synthetic A-Parser result file with found, not found, empty and cut-off pages

    8.9 KB

Quick start

  1. Check Python (python3 --version, 3.8 or newer) and run the offline checks. The first starts a mock A-Parser on 127.0.0.1 and runs 33 checks; the second reads a synthetic result file:

    bash
    python3 aparser_rank_check.py --selftest
    python3 aparser_rank_check.py --parse sample-results.txt --domain example-site.com
    
  2. Point the script at your A-Parser and look at the body before sending anything. <APARSER_HOST>, <PORT> and <PROXY_POOL> are placeholders for your own values:

    bash
    export APARSER_API_URL="http://<APARSER_HOST>:<PORT>/API"
    export APARSER_API_PASSWORD="<your API password>"
    python3 aparser_rank_check.py keywords.example.txt --domain example-site.com \
        --pool "<PROXY_POOL>" --google-domain google.com --hl en --gl us --location "New York" --dry
    
  3. Run the same command without --dry on 10 of your keywords. If nearly all of them come back FAILED, the warm-up does not pass on that proxy pool; test this before you buy traffic. Then run the full list with --out ranks.csv.

The table it prints

This is the output of --parse sample-results.txt on the synthetic file (made-up domains and counts), run on 2026-11-12:

text
keyword                  status      pos  organic  note
best running shoes       FOUND         7       30  ok
used car prices          NOT_FOUND             30  not in the 30 organic results returned (ok)
website builder          FAILED                 0  empty - failed measurement, NOT 'not in the results'
what is inflation        FAILED                11  partial - failed measurement, NOT 'not in the results'
how to boil eggs         FOUND         2       28  ok
standing desk reviews    NOT_FOUND             30  not in the 30 organic results returned (ok)
learn python online      FOUND        19       30  ok
best budget laptops      NOT_FOUND             30  not in the 30 organic results returned (ok)
how to change a tire     NOT_FOUND             27  not in the 27 organic results returned (ok)
xqzvbn rare phrase test  NOT_FOUND              3  not in the 3 organic results returned (short_serp)

10 keywords for example-site.com: found 3, not found 5, FAILED 2

pos is the place among the organic results in the order A-Parser returns them, with Google-hosted lines such as /goto redirects dropped, because one of them on the first line would move every position by one. Ads and other result features are not counted.

How a keyword gets its status:

StatusRule
FAILED, emptyNo organic result at all
FAILED, partialFewer than pages x 8 organic results (24 for 3 pages) while Google reports 200 or more results, or no count
valid, shortGoogle reports fewer than 200 results (a rare keyword): valid however short. Two attempts with the same count and the same first URL also count as a real short page
NOT_FOUNDA valid page without your domain; subdomains count as your domain, www. is ignored
FOUNDA valid page with your domain; the first match gives the position

The 8 per page and the 200 are the defaults of the author's tracker, not measured limits (--min-per-page, --short-total). Only about 10 of 100 mobile keywords returned 30 or more organic results on 3 pages, so a full 30 is rare; the threshold of 24 lets pages with 24 to 29 results pass.

The task body, field by field

FieldValueWhy
configPreset, presetdefaultAn addTask without configPreset is rejected
parsersSE::Google, preset default, then option overridesOne parser; its variables are $p1. in the result format
pagecount3About 30 positions. In the author's test, pages 4 and 5 shared about 0 % of domains with a browser
hl, gl, domain, locationen, us, www.google.com, a citySet to the same language, country and city as your reference browser
devicemobileMobile and desktop are different pages. A mobile run matched a mobile browser on 80 to 90 % of the domains on pages 1 and 2 and differed from desktop
useproxy, proxyChecker1, <PROXY_POOL>The name of a residential pool of the same country
smartSessionWarmup1"Bypass poisoned SERP" in the web interface
resultsFormattext \n and \t, header separator |||See the gotchas below
doLognodb made the engine write SQLite errors at every task start on the author's build
threads3Sent, but the thread count comes from the thread config

Every option override is an object {"id": ..., "type": "override", "value": ...}. The API accepts a [name, value] pair without an error and does not apply it, so the task runs with the preset's values and nothing tells you.

What the warm-up option does, and does not

It is the vendor's switch for the poisoned pages; the vendor's article of 2026-11-06 shows it in the SE::Google settings and does not describe how it works. In the author's runs it did not change which results came back. It made runs finish.

Variant, author's runs of 2026-11-10Result
With smartSessionWarmup3 runs of 3 finished
Without it2 of 3 finished; one returned nothing for 8 keywords
5 pages, without itneither of 2 runs finished
Full browser engine0 successful queries
experimentalSplit = 10 results
filter=0, lr=lang_et, single main browser, locationinside the noise

Three runs per variant is a tendency, not proof. A mixed-country proxy pool gave 52 % good measurements against 83 % for a same-country pool, about 70 % once it settled; the first three tasks on it lost 13 of 18 measurements. One site in one keyword moved between places 1 and 4 from one proxy IP to the next. Use a pool of the country you track.

What it costs

A-Parser does not report proxy traffic, so the author measured it in the proxy dashboard: about 1.7 MB per keyword at 3 pages (0.19 GB for about 6,050 requests to Google, about 55 requests per keyword), against 1.3 MB in the author's own estimate. Taking 1.0 to 1.7 MB per keyword, 1,000 keywords cost:

Proxy price1.0 MB per keyword1.3 MB1.7 MB
$1.70 per GB (small pack)$1.70$2.21$2.89
$1.00 per GB (large pack)$1.00$1.30$1.70

Prices come from a proxy price list read on 2026-11-12: from $1.70 per GB on small packs to about $1.00 on large ones. They change, and the 1.0 MB end of the range is the author's low estimate. For scale, a hosted API queue priced about $1.50 per 1,000 keywords at depth 30 when the author checked on 2026-10-15. Two additions: every retried keyword costs another pass, and a license for A-Parser comes on top; the author runs Enterprise, so check which edition includes the HTTP API. --max-mb stops a run whose upper estimate is above your limit.

Speed

On the author's instance, the same 100 keywords each time, 3 pages, mobile: chunk 10 took 632 s (9.5 keywords a minute), chunk 20 took 389 s (15.4), chunk 40 took 338 s (17.7). Empty pages were 4, 1 and 3 of 100, which is noise. The instance allowed 20 threads, so a chunk of 20 already uses all of them and the script defaults to it. Two tasks of 10 in parallel took 364 s. A first large run of 613 keywords took 92 minutes and 18 % of the pages were empty after one retry; on a second project 206 of 487 failed after one retry (190 empty, 16 cut off), which is why the script retries by itself, up to 3 rounds.

Rather have this done for you?

Send the site address and what you want to know. We run the audit and send the report.

Send a request

Where the warm-up fails

With the author's proxy pools the warm-up passed for 7 countries and failed for 3.

ResultCountriesEvidence
PassesEstonia, Norway, Sweden, Finland, Lithuania, Slovakia, Bulgaria1 page per keyword; Norway returned 4 of 4 keywords
FailsUnited Kingdom, United States, Hong KongUnited Kingdom 0 of 4 keywords; United States on 2026-11-12: 5 keywords x 3 pages gave 0 successes and 47 failed warm-ups, 10 keywords x 1 page on desktop gave 1 success and 91 failed warm-ups

The log line was Browser warmup: failed (captcha) or (no SERP). The cause is outside the option: Google serves a captcha to the IPs of that pool. The vendor reports a passing test on US pages through residential proxies in New York, so this is most likely a property of one pool, not of the country. The lesson is the 10-keyword test in step 3 of the quick start.

Gotchas

  • \n and \t are text. The result format needs a backslash and a letter. Real newline and tab characters made all 6 keywords of a live test come back empty. If the output looks like a table with columns amp, anchor, gotolink, link, the format was not expanded.
  • A bare $first is empty. In a task body the result variables need the $p1. prefix, even with one parser. The task reports success and the file holds only separators.
  • threads does nothing. The engine log says threads: 20 whatever you send; the count comes from the thread config. To slow a run, use the requestdelay option of the parser; it sets the pace of the whole task.
  • Only asynchronous actions. oneRequest and bulkRequest block the HTTP handler for the whole run; the instance stops answering even ping.
  • A new pool folder is invisible. A-Parser picks up proxy folders only under pool names it already knows. A task with an unknown pool waits for a proxy with 0 HTTP requests and looks like a dead network.
  • Proxies are told apart by address:port. Two pools with the same ports merge into one set. Give each pool its own block of ports.
  • Retries used is not a download count. It showed about 90 where the log held 7 to 10 real attempts, so a traffic estimate from it is 8 to 10 times too high.
  • Delete after reading. Remove the result file and the task, or the queue fills with finished tasks. If you need the task log later, set doLog to db and keep the task.

How accurate it is

Measured against one browser, one keyword, with the warm-up and a same-country pool: page 1 shared 70 to 90 % of the domains, page 2 50 to 75 %, page 3 10 to 44 %, pages 4 and 5 about 0. The exact position matched in 7 to 16 % of cases, and two searches in the browser itself matched on 7 %. So a position is a band, not a number. The cheat sheet Rank error bars turns these numbers into rules, and SERP repeat check flags days when the page flipped.

Who should not use it

  • You need positions deeper than 30. The author does not trust anything beyond that point.
  • You cannot get residential proxies for your country. Without proxies, and with datacenter proxies, A-Parser returned 0 results in the author's tests of 2026-10-11.
  • You track thousands of keywords and have no one to watch the pool. The pool is the weak point: test it, then watch the share of FAILED.
  • Your tracker already passes the 15-minute test in the video. If it does, do not switch.

Tested on

ItemValue
Date2026-11-12
HostWindows 11, Python 3.13.7 (the code was also run with 3.13.14); written for 3.8 and newer, not run on older versions
Commandpython aparser_rank_check.py --selftest, then --parse sample-results.txt and --dry
Self-test33 of 33 checks passed, offline, against a mock A-Parser on 127.0.0.1
Live APIoffline self-test passed; live API call not re-run for this release

The wrapper has never been run against a real A-Parser. What is taken from the author's working integration, which is part of a private rank tracker: the field list and the option objects, configPreset, the escaped result format with the ||| separator, the polling and cleanup actions, the page rules (8 per page, 200), the retry rule (up to 3 rounds, 15 %), the chunk size and the proxy-pool settings. That integration was run live on 2026-11-10 and 2026-11-11 (a six-keyword test, then checks of 613 and 487 keywords), and its first test showed the missing configPreset and the real-newline mistake. Everything else, including the CLI, the CSV, --parse, the traffic estimate and the mock server, is new in this release and is covered only by the offline checks. The mock proves the script follows the rules above; it proves nothing about Google, your proxies or your A-Parser build. The measurements are from 2026-11-10 to 2026-11-12, on mobile pages for Estonia in the local language, and small samples. The United States and other countries were not tested for accuracy.

Sources

SourceUsed for
A-Parser: poisoned SERP in GoogleThe option "Bypass poisoned SERP" in SE::Google, the vendor's test on US pages; the mechanism is not described there
Author's measurements, 2026-11-10 to 2026-11-12Run reliability, accuracy against a browser, chunk speed, traffic, countries
Author's proxy price list, read 2026-11-12$1.70 and about $1.00 per GB

The article was read on 2026-11-12. Automated queries are against Google's terms of service; the author uses this for his own sites. How much risk to accept is your call.

Files are provided as is: run them on your own data and check the result.

Want an audit like this on your site?

If you would rather have it done for you, send a request and describe the site.

Send a request
Download task-body.example.json