Ad Verification with Residential Proxies: Geo Delivery, Evidence, and Brand-Safety QA

Published
Reading Time5 min read

Key Takeaways

A production guide to geo-aware ad verification that separates platform diagnostics, supply-chain standards, browser evidence, sampling, and human review instead of treating a screenshot as proof of delivery.

🎯
Direct answer: residential proxies can provide a controlled geographic browser viewpoint for ad verification, but they do not by themselves prove campaign delivery, viewability, authorization, or fraud. A defensible workflow combines platform-side diagnostics, supply-chain or measurement standards where available, independent route validation, rendered evidence, repeat sampling, and a human-review path for inconclusive results.

Ad verification is an evidence problem, not a screenshot problem. A useful system must answer: which market was requested, which route was actually observed, what page or placement rendered, when it happened, what browser state influenced the result, and how strong the evidence is for the business conclusion.

1. Start with an evidence hierarchy

Not every verification question should begin with a proxy browser. Use the strongest source available for the claim you need to make.

Evidence layerBest forImportant limitation
Platform diagnostics and campaign logsEligibility, campaign state, impressions, platform-native delivery diagnosticsPlatform-specific and may not reproduce the external user experience
Industry measurement or supply-chain standardsViewability/verification signals, authorized seller relationships, supply-chain transparencyOnly applies where the publisher, app, player, or supply chain implements the relevant standard
Controlled browser observationGeo-specific page rendering, creative presence, landing-page localization, surrounding content, redirect behaviorOne observation is not proof of total campaign delivery or non-delivery
Human reviewAmbiguous creative matching, sensitive brand-safety decisions, conflicting evidenceSlower and requires a documented review standard

For example, Google recommends its Ad Preview and Diagnosis tool for checking Search ad appearance without creating impressions. Google also documents that location targeting uses multiple signals and is not guaranteed to be 100% accurate. That means an external residential-IP observation is useful as a supplementary market viewpoint, not a replacement for platform-native diagnostics. See Google Ads: Ad Preview and Diagnosis and Google Ads: geographic targeting.

2. Define exactly what the verification job is trying to prove

A campaign QA plan should become a structured contract before any browser launches.

json
{
  "campaignId": "cmp-2026-08-us-001",
  "targetUrl": "https://publisher.example/article",
  "market": {
    "country": "US",
    "region": "CA",
    "city": "Los Angeles",
    "locale": "en-US",
    "timezone": "America/Los_Angeles"
  },
  "placement": {
    "slotId": "article-top",
    "selector": "[data-ad-slot='article-top']"
  },
  "expected": {
    "advertiserDomain": "brand.example",
    "creativeId": null
  },
  "window": {
    "start": "2026-08-07T09:00:00-07:00",
    "end": "2026-08-07T11:00:00-07:00"
  },
  "sampleCount": 5
}

The contract should distinguish at least three questions:

  1. Delivery observation: was an expected creative or advertiser observed in this sample?
  2. Geo consistency: did the observed route and rendered experience match the requested market?
  3. Brand-safety context: what content surrounded the placement at the time of capture?

Do not convert “not observed in one sample” into “campaign did not deliver.” Inventory, pacing, auction outcome, cookies, consent, device class, user history, frequency caps, time, and other platform signals can change the result.

3. Residential proxies are one location signal, not the whole user location

A residential proxy changes the network egress viewpoint. For geo-sensitive advertising, that is useful—but advertising platforms may also use device, account, language, browser, past activity, or other signals.

Record both requested and observed values:

json
{
  "requested": {
    "country": "US",
    "region": "CA",
    "city": "Los Angeles"
  },
  "observedExit": {
    "ip": "203.0.113.10",
    "country": "US",
    "region": "CA",
    "city": "Los Angeles",
    "asn": 64500
  },
  "browser": {
    "locale": "en-US",
    "timezone": "America/Los_Angeles",
    "viewport": "1440x1000"
  }
}

Never silently downgrade a city request to country-only and mark it valid. If city precision is unavailable or the independent geo check disagrees, classify the evidence as geo_invalid or needs_review.

4. Separate rotating samples from sticky journeys

Use rotating routes for independent observations across time windows, markets, or publisher pages.

Use a sticky route when one evidence package requires continuity across multiple page states, for example:

  • publisher page → consent state → placement observation;
  • publisher page → authorized landing-page navigation;
  • multi-step localized experience where changing IP mid-run would invalidate the comparison.

Bind one logical identity together:

plain text
verification_job_id
  -> browser_context_id
  -> proxy_session_id
  -> cookie/consent state
  -> evidence_package_id

Do not share one sticky session across unrelated campaigns or markets.

5. Do not click ads during verification unless the test explicitly authorizes it

A verification crawler should avoid contaminating campaign metrics. Clicking an ad can create click events, attribution signals, fraud alerts, billing implications, or downstream conversion state.

Prefer these techniques:

  • inspect the rendered slot without interacting;
  • capture the placement and surrounding context;
  • record visible destination information only when exposed in the DOM or approved instrumentation;
  • use platform preview/diagnostic tools when they exist;
  • if landing-page verification is contractually required, use a documented, authorized procedure that avoids generating unintended campaign events.

For Google Search campaigns, the platform explicitly recommends Ad Preview and Diagnosis instead of repeatedly searching for your own ad because the preview workflow avoids affecting impression data.

6. Supply-chain checks are complementary evidence

For programmatic inventory, supply-chain transparency can strengthen an investigation but does not prove that one impression was served.

IAB Tech Lab's ads.txt standard lets publishers publicly declare authorized digital sellers. sellers.json helps identify direct sellers and intermediaries participating in the selling chain.

A useful verification package can therefore contain:

json
{
  "publisherDomain": "publisher.example",
  "adsTxtCheckedAt": "2026-08-07T05:30:00Z",
  "sellerAuthorized": true,
  "sellerDomain": "exchange.example",
  "sellerAccountId": "12345",
  "sellersJsonIdentityResolved": true
}

Treat this as supply-chain evidence, not rendered-placement evidence. A valid ads.txt relationship does not mean the expected creative was shown; an observed creative does not by itself validate the complete programmatic supply chain.

7. Prefer standardized measurement signals when the environment supports them

Where a publisher, app, video player, or CTV environment integrates IAB Tech Lab Open Measurement, use those standardized signals as complementary evidence for supported measurement and verification use cases. The Open Measurement SDK exists to facilitate consistent third-party measurement and verification signals across supported environments.

A browser screenshot should not be described as a formal viewability measurement. It can show that pixels were rendered in a particular browser state, but viewability and invalid-traffic measurement require a defined measurement methodology and, where applicable, supported instrumentation.

8. Production architecture

Customer-owned components: campaign contract, scheduler, queue, browser workers, validation logic, evidence storage, review workflow, retention rules, platform integrations, and monitoring.

BytesFlows role: provide a residential network viewpoint for the requested route. The proxy is not the source of truth for campaign eligibility or billing.

9. Use a stable evidence contract

A durable evidence package should keep raw artifacts separate from the normalized conclusion.

json
{
  "jobId": "verify-001",
  "campaignId": "cmp-001",
  "scheduledWindow": "2026-08-07T16:00:00Z/2026-08-07T16:15:00Z",
  "capturedAt": "2026-08-07T16:04:12Z",
  "sourceUrl": "https://publisher.example/article",
  "finalUrl": "https://publisher.example/article",
  "requestedGeo": {
    "country": "US",
    "region": "CA",
    "city": "Los Angeles"
  },
  "observedExit": {
    "ip": "203.0.113.10",
    "country": "US",
    "region": "CA",
    "city": "Los Angeles",
    "asn": 64500
  },
  "browserProfile": {
    "locale": "en-US",
    "timezone": "America/Los_Angeles",
    "viewport": "1440x1000"
  },
  "consentState": "accepted-required-only",
  "placement": {
    "slotId": "article-top",
    "slotFound": true,
    "creativeObserved": true,
    "advertiserDomain": "brand.example"
  },
  "httpStatus": 200,
  "pageClass": "expected",
  "htmlSha256": "...",
  "screenshotUri": "object://evidence/.../page.png",
  "slotScreenshotUri": "object://evidence/.../slot.png",
  "collectorVersion": "adverify-2.1.0",
  "validationStatus": "valid"
}

Do not store reusable proxy passwords or raw authentication secrets inside evidence objects.

10. Playwright implementation: validate route, wait for the placement, then capture evidence

The following TypeScript is an implementation template, not an executed benchmark. It intentionally avoids clicking the ad and does not use a fixed sleep as its primary readiness signal.

typescript
import { chromium } from 'playwright';
import { createHash } from 'node:crypto';
import { mkdir, writeFile } from 'node:fs/promises';

const required = (name: string): string => {
  const value = process.env[name]?.trim();
  if (!value) throw new Error(`Missing ${name}`);
  return value;
};

const PROXY_SERVER = required('PROXY_SERVER');
const PROXY_USERNAME = required('PROXY_USERNAME');
const PROXY_PASSWORD = required('PROXY_PASSWORD');
const IP_CHECK_URL = process.env.IP_CHECK_URL || 'https://iprobe.io/json';

type Job = {
  jobId: string;
  targetUrl: string;
  slotSelector: string;
  expectedCountry: string;
  locale: string;
  timezoneId: string;
};

async function capture(job: Job) {
  const browser = await chromium.launch({ headless: true });

  try {
    const context = await browser.newContext({
      proxy: {
        server: PROXY_SERVER,
        username: PROXY_USERNAME,
        password: PROXY_PASSWORD,
      },
      locale: job.locale,
      timezoneId: job.timezoneId,
      viewport: { width: 1440, height: 1000 },
    });

    try {
      const page = await context.newPage();

      // 1. Validate the route before collecting campaign evidence.
      const geoResponse = await page.goto(IP_CHECK_URL, {
        waitUntil: 'domcontentloaded',
        timeout: 30_000,
      });
      if (!geoResponse || !geoResponse.ok()) {
        throw new Error('Exit validation failed');
      }

      const geoBody = await page.locator('body').innerText();
      const observedGeo = JSON.parse(geoBody);
      const observedCountry = String(
        observedGeo.country_code ?? observedGeo.countryCode ?? observedGeo.country ?? ''
      ).toUpperCase();

      if (observedCountry !== job.expectedCountry.toUpperCase()) {
        throw new Error(
          `Wrong GEO: expected=${job.expectedCountry} observed=${observedCountry}`
        );
      }

      // 2. Navigate to the approved publisher page.
      const response = await page.goto(job.targetUrl, {
        waitUntil: 'domcontentloaded',
        timeout: 45_000,
      });

      // 3. Wait for a business-specific placement signal.
      const slot = page.locator(job.slotSelector).first();
      await slot.waitFor({ state: 'visible', timeout: 15_000 });

      const box = await slot.boundingBox();
      if (!box || box.width < 2 || box.height < 2) {
        throw new Error('Placement is not visibly rendered');
      }

      const html = await page.content();
      const htmlSha256 = createHash('sha256').update(html).digest('hex');
      const dir = `evidence/${job.jobId}`;
      await mkdir(dir, { recursive: true });

      await page.screenshot({ path: `${dir}/page.png`, fullPage: true });
      await slot.screenshot({ path: `${dir}/slot.png` });

      const record = {
        jobId: job.jobId,
        capturedAt: new Date().toISOString(),
        requestedCountry: job.expectedCountry,
        observedCountry,
        targetUrl: job.targetUrl,
        finalUrl: page.url(),
        httpStatus: response?.status() ?? null,
        pageTitle: await page.title(),
        slotSelector: job.slotSelector,
        slotVisible: true,
        slotBox: box,
        htmlSha256,
        collectorVersion: 'adverify-2.1.0',
      };

      await writeFile(
        `${dir}/evidence.json`,
        JSON.stringify(record, null, 2),
        'utf8'
      );

      return record;
    } finally {
      await context.close();
    }
  } finally {
    await browser.close();
  }
}

Playwright supports HTTP(S) and SOCKS proxies at browser or context level. Its documentation also discourages using networkidle as a general readiness condition; a placement-specific selector or other business signal is a better completion condition. See Playwright Network and Playwright Page.

11. Cross-origin ad iframes need a different validation strategy

Many ad slots render inside cross-origin iframes. You may be able to observe the iframe element, size, source URL, or screenshot it, while browser same-origin rules prevent arbitrary DOM inspection inside it.

Do not disable browser security to make verification easier. Instead define evidence that is available from the publisher page and approved measurement layer:

  • slot/iframe exists;
  • rendered dimensions are non-zero;
  • screenshot of the slot;
  • frame URL or approved metadata if exposed;
  • surrounding publisher content;
  • platform-side creative ID or verification signal where available.

If the business requirement needs deeper creative inspection, use a supported verification integration or platform API rather than bypassing origin isolation.

12. Consent and browser state are part of the evidence

Ad output can change with consent state, cookies, language, device class, account state, and previous browsing state.

For every run record:

  • fresh or reused browser context;
  • consent action taken, if any;
  • cookie/storage profile identifier;
  • locale and timezone;
  • viewport/device class;
  • signed-in or signed-out state;
  • whether an extension or ad blocker was present.

Do not compare a fresh signed-out US context against a previously consented DE context and call the difference purely geographic.

13. Sampling design matters more than brute-force retries

A useful verification program spreads observations across meaningful time windows and independent samples.

Example:

plain text
Los Angeles: 5 observations across 2 hours
New York:    5 observations across 2 hours
London:      5 observations across 2 hours

Store each observation independently. Then report:

plain text
expected_creative_observed = 3 / 5
valid_geo_samples = 5 / 5
inconclusive_samples = 1 / 5

Do not turn a missing creative into an automatic “new IP until it appears” loop. That changes the question from verification into outcome hunting and can bias the evidence.

14. Failure classification and retry policy

Failure classExampleDefault action
proxy_auth407Stop and fix credentials
geo_invalidRequested US, observed another countryReject evidence; bounded fresh-route retry
transportConnect timeout/resetBounded retry with jitter
target_rate_limit429Back off and reduce collection rate
target_denied403 or explicit access restrictionRecord and review; do not rotate indefinitely
placement_missingExpected slot never becomes visibleStore diagnostic artifact; classify as inconclusive/negative observation per QA plan
creative_not_observedDifferent creative or empty inventoryValid observation; sample again only if the plan requires it
policy_stopLogin wall, CAPTCHA, restricted workflowStop and require human/policy review

Every job needs limits for attempts, total runtime, bytes, browser navigations, and evidence size.

15. Brand-safety evidence should preserve context, not just the ad slot

A slot-only image can lose the context needed to judge brand suitability. Capture both:

  • full-page or contextual screenshot;
  • placement screenshot;
  • page title and canonical/final URL;
  • nearby heading/text classification;
  • capture timestamp;
  • collector version;
  • content hash.

For sensitive decisions, a machine classifier should produce a review candidate, not an irreversible verdict. Preserve enough source evidence for a human analyst to understand why the page was flagged.

16. Metrics: separate infrastructure health from campaign observations

Track two groups of metrics.

Collection health

  • route-validation pass rate;
  • wrong-geo rate;
  • 407 rate;
  • connection timeout rate;
  • browser render failure rate;
  • evidence write failure rate;
  • retry amplification;
  • queue age and completion latency.

Verification outcomes

  • valid samples per market;
  • expected creative observed / valid samples;
  • alternate creative observations;
  • empty-slot observations;
  • wrong-language or wrong-currency observations;
  • brand-safety review rate;
  • inconclusive rate.

Do not report “creative not observed” as proxy failure, and do not report “browser returned HTTP 200” as successful ad verification.

17. Security, privacy, and compliance boundaries

Keep the collector narrow:

  • use only approved public targets or systems you are authorized to verify;
  • prefer official platform preview/reporting APIs when they answer the question;
  • do not solve challenges or bypass access restrictions;
  • do not click ads merely to prove they exist;
  • keep credentials in a secret manager;
  • redact query-string secrets and reusable session identifiers;
  • restrict evidence-store access;
  • define retention/deletion rules;
  • minimize unrelated page content and personal data;
  • preserve an audit trail for changes to collectors and validation rules.

Residential routing changes the network viewpoint. It does not create permission to access content or guarantee a particular ad outcome.

18. Production rollout checklist

Before launch

Define the exact claim each job is allowed to make.
Choose platform diagnostics, standards evidence, browser evidence, or a combination.
Validate one approved target manually.
Verify requested and observed GEO independently.
Define consent/browser-state policy.
Define placement selectors or supported measurement signals.
Confirm the collector never clicks ads by default.
Test 407, wrong GEO, timeout, 403, 429, missing slot, and evidence-store failures.
Set per-domain concurrency and retry budgets.
Confirm secrets do not appear in screenshots, traces, or logs.

During rollout

Start with a small sample per market.
Review false positives and inconclusive cases manually.
Track valid evidence rate, not only request success.
Version the collector and evidence schema.
Alert on wrong-GEO spikes and evidence-store failures.

At scale

Partition queues by target and market.
Keep per-target circuit breakers.
Separate lightweight HTTP/supply-chain checks from browser jobs.
Preserve historical evidence; never rewrite old observations after collector changes.
Revalidate route assumptions when targeting requirements change.

19. Related BytesFlows resources

20. Verification boundary

This article describes an evidence-first architecture and unexecuted implementation template. It does not claim that BytesFlows, Playwright, a screenshot, ads.txt, sellers.json, or any single browser observation can independently prove campaign delivery, formal viewability, invalid traffic, or brand safety. Use platform-native diagnostics and standardized measurement signals where available, and validate the exact route, browser state, evidence contract, and authorization before production use.

Validate one route before scaling the program

Start with the smallest defensible test: one approved target, one market, one evidence contract, and a limited set of independent observations.

Primary CTA: Test a residential route with BytesFlows.

Production extension: scheduler, failure state machine, and rollout controls

The evidence model above becomes operational only when collection is controlled as a queue rather than a set of ad-hoc browser launches. Treat each (campaign, publisher, market, time-window, device-profile) tuple as an idempotent job. A scheduler should materialize jobs only for approved public pages and authorized QA targets, then enforce a per-domain concurrency budget before work reaches a browser.

Use separate retry budgets for proxy transport, target-site responses, browser readiness, parsing, and business-data anomalies. A proxy authentication failure is not a publisher failure; a publisher 429 or 5xx is not evidence that the route is bad; a missing placement after a successful page load is not automatically a transport error. Persist the failure class with every attempt so operators can change the correct subsystem instead of rotating IPs blindly.

A practical job key is a hash of the normalized target URL, requested market, device profile, campaign identifier, and scheduled observation window. Before enqueueing, check whether that key already has an accepted evidence package. Cancellation should propagate from the campaign or publisher policy layer to queued jobs and active browser contexts. Domain-level circuit breakers should pause new work after repeated target-side failures and reopen gradually; they should not be used to defeat a site's access controls.

Retry decision table

Failure classExampleRetry policyRoute action
Proxy authentication407, invalid credentialsStop until credentials/config are correctedDo not churn routes
Proxy transportconnect reset, tunnel timeoutBounded exponential backoffNew route may be selected for an independent sample
GEO validationrequested city/region does not match observed exitReject evidence; retry within bounded route budgetReplace route; never silently downgrade precision
Target transient429, 5xx, navigation timeoutRespect target signals, back off, apply domain budgetKeep failure separate from proxy health
Parse/render driftexpected slot or schema changedLimited retry, then human reviewRoute change is not the default fix
Business anomalycreative differs, unexpected surrounding contentPreserve evidence and reviewDo not retry until the evidence is erased
Permanent policy failurelogin required, access denied, disallowed targetStopNo bypass attempt

Production telemetry and error budget

Track at least jobs_started, jobs_completed, jobs_failed, jobs_needs_review, observation latency, retry count, proxy-auth failures, transport failures, GEO mismatches, target 4xx/5xx, placement-not-found, parser/render drift, evidence-write failures, and queue age. Logs should carry jobId, campaignId, normalized domain, requested GEO, observed GEO result, browser profile ID, attempt number, failure class, evidence-package ID, and collector version. Credentials and reusable proxy passwords must be redacted.

Define an error budget from the business SLO rather than inventing a universal success percentage. Alert when the accepted-evidence rate, queue age, GEO-valid rate, or review backlog crosses the team's agreed threshold. Keep a bounded sample of failed HTML/screenshot evidence when policy permits, attach a retention deadline, and route ambiguous brand-safety conclusions to a named human-review queue.

Development → pre-production → production acceptance checklist

Development

  • Use only approved test/public pages and synthetic campaign contracts.
  • Confirm credentials come from environment/secret storage and never from source control.
  • Verify requested vs observed GEO rejection logic with known mismatches.
  • Exercise every failure class without treating route rotation as a universal retry.
  • Confirm screenshots, hashes, timestamps, and normalized evidence are linked by jobId.

Pre-production

  • Run a small market matrix with explicit domain concurrency and rate limits.
  • Validate consent, locale, timezone, viewport, and sticky-session boundaries.
  • Confirm idempotency prevents duplicate observations for the same scheduled window.
  • Exercise cancellation, circuit breaker, review queue, retention, and evidence deletion procedures.
  • Review robots.txt, applicable terms, publisher permissions, personal-data exposure, and whether platform-native diagnostics should be the primary evidence source.

Production

  • Start with a bounded publisher/market allowlist and expand only after evidence quality is stable.
  • Require dashboards and alerts before increasing concurrency.
  • Version selectors, collectors, evidence schema, and route policy independently.
  • Stop collection on authorization changes, persistent access-control responses, unexpected personal-data exposure, or evidence corruption.
  • Roll back collector/rules versions when parser or business-classification drift creates unreliable evidence; preserve prior evidence rather than overwriting it.

Scaling to more publishers and markets usually requires partitioned queues, per-domain rate governors, a durable evidence/object store, selector/config versioning, a review workbench, and region-aware scheduling. Scaling does not remove the requirement to validate every requested GEO or to respect site and platform rules.

Before using a paid route, validate the observation contract

First prove that your requested country/region/city can be independently observed and that your browser contract produces the expected locale and page state on an approved target. Use the Proxy Test as the single primary CTA for this workflow. Related implementation context is available in Proxy Rotation Strategy, Locations, and Pricing; these are supporting references, not additional calls to action.

Implementation status: the code and architecture in this article are implementation templates and were not executed as part of this editorial update. Target selectors, current route availability, platform behavior, and account-specific proxy credentials remain environment-specific and must be verified before production use.

AV
Engineering Team ReviewedBenchmarked & Peer Reviewed

Alex Vance

Lead Proxy Network Architect

Reviewed by the BytesFlows engineering team. Examples are written for compliant public-web data collection, QA, SEO monitoring, and market research workflows. Results can vary by target site, country, client runtime, and request rate.