Commerce · Updated

Price Monitoring Proxies: Selection and Safe Workflow

Price monitoring proxies add a controlled network-origin variable to an authorized offer observation. The useful result is not an HTTP 200 or a scraped number: it is a normalized product-and-offer record with seller, fulfillment, availability, price conditions, market context, time, and failure state. Start with an official API, merchant feed, licensed source, or written permission; use a proxy only when the approved question genuinely requires a regional network sample.

Pay as you go, no monthly commitment. Order minimums and traffic validity vary by network.

34M+ Historical residential catalogue total; not live availability
800K+ Historical mobile catalogue total; not live availability
195 Published paid-location routes
Live Public network status and incident history
Proxy service operating since 2022Public network status and incident historyPremium Residential: one country, state, city, ZIP, coordinate, or ASN target at a timeOpt-in, consent-based IP sourcingDatabay Trust Center
Workflow notes

How to run price monitoring through a proxy route

Retain complete authorized offers, rate limits, refusals, and freshness before comparing price.

Normalized offer recordMarket ledger
Observe
Authorized offer source
From
Controlled regional sample
Feed
Comparable offer record
CompareProduct and seller matchFull price conditionsFailure and freshness state
RP-01 / Request pathOne gateway address; the credential string selects the route

Client

your code or browser

Databay gateway

gw.databay.co:8888

Datacenter exit

hosted subnet address

Target

Authorized offer source

credentials: USER-zone-datacenter:PASSWORD

For price monitoring: a controlled regional sample reaches the authorized offer source, and the comparable offer record returns on the same path. The credential string selects the route; the client configuration never changes.

  1. Authorized offer source

    Start With a Comparable Offer, Not a Proxy

    Define the pricing decision and its authorized source before buying traffic. An official merchant API, seller export, product feed, licensed dataset, or partner report is the first choice when it supplies the identifiers and offer fields the decision needs. A proxy becomes relevant only when a permitted public-page test must vary network origin or region while the other shopper conditions remain controlled.

    Google's current Merchant API product guide identifies a merchant's products by content language, feed label, and offer ID, and its example keeps availability, price, currency, condition, and identifiers as separate fields. Google also warns that accepting a product input does not mean the product is approved to appear. That is useful evidence for the shape of a normalized record, not a competitor-price feed and not permission to collect another merchant's pages.

    Decision gate before a proxy pilot
    QuestionUse firstProxy decision
    Your own Merchant Center or seller catalogAuthorized API, export, or feedNo proxy unless a separate approved regional page check fills a named gap
    Licensed marketplace or partner dataContracted interface and its quotaUse its market fields before adding a network-origin sample
    Approved public offer comparisonWritten source register and bounded URL setTest a proxy only if requested network geography is an experimental variable
    Login wall, CAPTCHA, 401, 403, or objectionSource owner, official API, export, or permission reviewStop; a new exit does not create access rights
  2. Product and seller match

    Normalize Product, Seller, Price Conditions, and Market

    A number is not comparable until the sellable item and offer context match. Google's current product data specification treats price and ISO 4217 currency, sale price, availability, condition, product identifiers, and market-facing data as distinct attributes. It also distinguishes a public sale price from membership pricing and requires submitted price and availability to agree with the merchant's landing and checkout surfaces. Use those distinctions as a normalization discipline even when an approved source uses different field names.

    Normalized offer record
    GroupRequired comparison contextReject or quarantine when
    Product identitySource key, stable product ID, offer ID, exact variant, pack size, quantity, and conditionThe title looks similar but the sellable item cannot be matched
    Seller and fulfillmentSeller identity, merchant or marketplace fulfillment, stock state, and delivery promiseThe visible number belongs to another seller or fulfillment path
    PriceRegular amount, sale amount and dates, membership or coupon condition, shipping, tax treatment, mandatory fees, total, and currencyA component is missing, zero is substituted for unknown, or conditions differ
    Market controlsRequested and observed network country, feed label or marketplace, language, delivery assumption, account and membership state, device class, and consent stateA network location is mistaken for a delivery, tax, account, or device input
    EvidenceAuthorized source method, retrieval time, HTTP status, parser version, validation state, and redacted evidence referenceA challenge or error page is parsed as an offer

    Preserve null as unknown. Store unusable, incomplete, stale, refused, and rate-limited observations alongside usable records so downstream coverage and cost calculations include what the workflow actually encountered.

  3. Full price conditions

    Choose the Least Complex Databay Route That Fits

    Choose by required network origin and location control, then validate the smallest suitable paid order against one authorized source. Databay products use shared pools, bill transferred traffic, and support HTTP proxying, HTTPS CONNECT, and SOCKS5. A new connection may select an available exit, but a fresh or different IP is not guaranteed. Databay does not offer a free trial.

    Price-monitoring product and entry-path decision, verified August 29, 2026
    Starting optionUse whenControls and session boundarySmallest published entry at review
    Direct or official sourceThe approved API, feed, or export already supplies the required market and offer fieldsNo proxy variableNo proxy purchase
    DatacenterA permitted endpoint accepts a hosting-network origin and no consumer-ISP signal is requiredCountry or continent; requested sticky session up to 120 minutes, with possible early lossReview the current 10 GB entry package; $12.50 total and 31-day validity at review
    Premium ResidentialAn authorized shopper-facing comparison explicitly requires a consumer-ISP origin or finer geographic selectorOne country, state, city, ZIP, coordinate, or ASN selector at a time; requested sticky session up to 120 minutes, with possible early lossReview the current 5 GB PAYG entry; $13.75 starting total and 186-day validity at review
    Residential FlexA permitted country- or continent-level ISP-origin sample fits and granular controls are unnecessaryCountry or continent; requested sticky session up to 30 minutes, with possible early lossReview the current 25 GB minimum; $13.75 total and 31-day validity at review

    Prices, quantities, totals, and validity can change. The linked pricing pages load the current catalogue and are the purchase authority; if a current rate card is unavailable, wait or request a current quote rather than relying on this dated review. Start with Datacenter only when its origin fits, Premium Residential when its finer controls are genuinely required, and Flex when its larger minimum and coarser controls still match the test.

  4. Failure and freshness state

    Separate Geography From Delivery and Session State

    Write the hypothesis as a controlled comparison: hold product, seller, variant, fulfillment, delivery assumption, language, account, membership, consent, device, parser, and time window fixed; change only the requested network region. Verify the observed exit before interpreting a difference. A country-targeted connection is evidence from one routed request, not proof of a shopper's residence, tax nexus, delivery eligibility, GPS location, or every price in that market.

    Use a rotating connection for independent observations only when the sampling plan calls for it. Use one sticky session for a short, permitted multi-request flow that requires route continuity. Keep the same session across an approved 429 follow-up; changing exits in response to a quota or refusal would corrupt the experiment and can turn rate handling into evasion. Connection reuse can retain an exit, and a requested sticky duration is a maximum request rather than a continuity guarantee.

    1. Confirm current country availability for the chosen product rather than inferring it from a location name.
    2. Create the full product username with the proxy configuration generator.
    3. Verify one benign route with the generated client setup before contacting the approved source.
    4. Keep source scheduling and rate budgets global across workers, sessions, and exits.
    5. Record requested and observed region separately; quarantine an unfulfilled route.
  5. Product and seller match

    Run the Error-Retaining Example Against Loopback First

    The example below requires Node.js 22 or newer and uses only built-in modules and the runtime's fetch implementation. Save it as price-monitor.mjs, then run node price-monitor.mjs --self-test. The self-test binds to 127.0.0.1, contacts no public source and no Databay gateway, returns a 429 followed by a valid fixture, then exercises a 403 and malformed JSON. It prints all three records so errors cannot disappear from the result set.

    import { createServer } from 'node:http';
    import { setTimeout as delay } from 'node:timers/promises';
    import { pathToFileURL } from 'node:url';
    
    const MAX_RETRY_AFTER_MS = 60_000;
    const PARSER_VERSION = 'normalized-offer-json-v1';
    
    export function retryAfterMs(value, nowMs = Date.now()) {
      if (!value) return null;
      const input = value.trim();
      if (/^\d+$/.test(input)) {
        const seconds = Number(input);
        return Number.isSafeInteger(seconds) && seconds <= 60 ? seconds * 1_000 : null;
      }
      const retryAt = Date.parse(input);
      if (!Number.isFinite(retryAt)) return null;
      const waitMs = Math.max(0, retryAt - nowMs);
      return waitMs <= MAX_RETRY_AFTER_MS ? waitMs : null;
    }
    
    function failure(state, attempts, kind, detail = {}) {
      return { state, offer: null, error: { kind, ...detail }, attempts };
    }
    
    async function discardBody(response) {
      if (response.body) await response.body.cancel();
    }
    
    function normalizedOffer(payload, context, retrievedAtUtc) {
      return {
        identity: {
          sourceKey: context.sourceKey,
          offerId: payload.offerId ?? null,
          productId: payload.productId ?? null,
          variant: payload.variant ?? null
        },
        seller: {
          id: payload.sellerId ?? null,
          fulfillment: payload.fulfillment ?? null
        },
        availability: payload.availability ?? null,
        price: {
          regularAmountMicros: payload.price?.amountMicros ?? null,
          saleAmountMicros: payload.salePrice?.amountMicros ?? null,
          currency: payload.price?.currencyCode ?? null,
          shippingAmountMicros: payload.shipping?.amountMicros ?? null,
          taxTreatment: payload.taxTreatment ?? 'unknown',
          membership: payload.membership ?? 'public'
        },
        market: { ...context.market },
        evidence: { retrievedAtUtc, responseStatus: 200, parserVersion: PARSER_VERSION }
      };
    }
    
    function missingFields(offer) {
      const required = [
        ['identity', 'sourceKey'], ['identity', 'offerId'], ['identity', 'productId'],
        ['seller', 'id'], ['seller', 'fulfillment'], ['price', 'regularAmountMicros'],
        ['price', 'currency'], ['market', 'requestedCountry']
      ];
      return required
        .filter(([group, field]) => offer[group][field] === null || offer[group][field] === '')
        .map(([group, field]) => group + '.' + field);
    }
    
    export async function observeOffer({
      sourceUrl,
      context,
      fetchImpl = fetch,
      sleep = delay,
      now = () => Date.now(),
      allowOne429Retry = false
    }) {
      const attempts = [];
      for (let number = 1; number <= 2; number += 1) {
        const atUtc = new Date(now()).toISOString();
        let response;
        try {
          response = await fetchImpl(sourceUrl, {
            headers: { accept: 'application/json' },
            redirect: 'manual',
            signal: AbortSignal.timeout(10_000)
          });
        } catch (error) {
          attempts.push({ number, atUtc, status: null, retryAfter: null });
          const kind = error instanceof Error ? error.name : 'UnknownTransportError';
          return failure('transport_error', attempts, kind);
        }
    
        const retryAfter = response.headers.get('retry-after');
        attempts.push({ number, atUtc, status: response.status, retryAfter });
    
        if (response.status === 429) {
          const waitMs = retryAfterMs(retryAfter, now());
          await discardBody(response);
          if (allowOne429Retry && number === 1 && waitMs !== null) {
            await sleep(waitMs);
            continue;
          }
          return failure('rate_limited', attempts, 'http_429', { retryAfter });
        }
    
        if (response.status === 401 || response.status === 403) {
          await discardBody(response);
          return failure('refused', attempts, 'destination_refusal', { status: response.status });
        }
    
        if (response.status === 407) {
          await discardBody(response);
          return failure('proxy_auth_error', attempts, 'proxy_authentication', { status: 407 });
        }
    
        if (!response.ok) {
          await discardBody(response);
          return failure('http_error', attempts, 'http_status', { status: response.status });
        }
    
        let payload;
        try {
          payload = await response.json();
        } catch {
          return failure('parse_error', attempts, 'invalid_json', { status: response.status });
        }
    
        const offer = normalizedOffer(payload, context, atUtc);
        const missing = missingFields(offer);
        if (missing.length) {
          return {
            state: 'incomplete',
            offer,
            error: { kind: 'missing_fields', fields: missing },
            attempts
          };
        }
        return { state: 'usable', offer, error: null, attempts };
      }
      return failure('rate_limited', attempts, 'retry_budget_exhausted');
    }
    
    async function selfTest() {
      let rateHits = 0;
      const server = createServer((request, response) => {
        if (request.url === '/rate-limit' && rateHits++ === 0) {
          response.writeHead(429, { 'Retry-After': '0' });
          response.end('rate limited');
          return;
        }
        if (request.url === '/refused') {
          response.writeHead(403);
          response.end('refused');
          return;
        }
        if (request.url === '/malformed') {
          response.writeHead(200, { 'Content-Type': 'application/json' });
          response.end('{');
          return;
        }
        response.writeHead(200, { 'Content-Type': 'application/json' });
        response.end(JSON.stringify({
          offerId: 'sku12345', productId: 'gtin-0001', variant: 'blue-m',
          sellerId: 'authorized-merchant', fulfillment: 'merchant', availability: 'IN_STOCK',
          price: { amountMicros: '15990000', currencyCode: 'USD' },
          salePrice: null, shipping: null, taxTreatment: 'recorded-separately', membership: 'public'
        }));
      });
      await new Promise((resolve) => server.listen(0, '127.0.0.1', resolve));
      const address = server.address();
      const base = 'http://127.0.0.1:' + address.port;
      const context = {
        sourceKey: 'local-fixture',
        market: {
          requestedCountry: 'US', contentLanguage: 'en', feedLabel: 'US',
          deliveryAssumption: 'none', accountState: 'signed-out'
        }
      };
      try {
        return [
          await observeOffer({ sourceUrl: base + '/rate-limit', context, allowOne429Retry: true }),
          await observeOffer({ sourceUrl: base + '/refused', context }),
          await observeOffer({ sourceUrl: base + '/malformed', context })
        ];
      } finally {
        await new Promise((resolve, reject) =>
          server.close((error) => error ? reject(error) : resolve())
        );
      }
    }
    
    async function main() {
      if (process.argv.includes('--self-test')) return selfTest();
      const sourceUrl = process.env.PRICE_SOURCE_URL;
      if (!sourceUrl) throw new Error('Set PRICE_SOURCE_URL to an approved JSON endpoint.');
      return observeOffer({
        sourceUrl,
        context: {
          sourceKey: process.env.PRICE_SOURCE_KEY ?? 'approved-source',
          market: {
            requestedCountry: process.env.PRICE_MARKET ?? null,
            contentLanguage: process.env.PRICE_LANGUAGE ?? null,
            feedLabel: process.env.PRICE_FEED_LABEL ?? null,
            deliveryAssumption: process.env.PRICE_DELIVERY_ASSUMPTION ?? null,
            accountState: process.env.PRICE_ACCOUNT_STATE ?? 'documented-separately'
          }
        },
        allowOne429Retry: process.env.ALLOW_ONE_429_RETRY === 'true'
      });
    }
    
    if (process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href) {
      console.log(JSON.stringify(await main(), null, 2));
    }

    The fixture's zero-second Retry-After keeps the self-test fast; production values can be either delay-seconds or an HTTP date. The example accepts only a valid delay no longer than 60 seconds and performs at most one follow-up when ALLOW_ONE_429_RETRY=true. Without that explicit opt-in, or when the header is absent, invalid, or longer, it retains a rate-limited record and stops. Choose the ceiling and retry permission with the source owner; 60 seconds is an example guardrail, not a claim about any marketplace.

  6. Full price conditions

    Honor 429 and Retry-After Without Changing Identity

    RFC 6585 section 4 defines 429 as too many requests in a given time and says the response may include Retry-After. It deliberately does not define whether counting is per resource, server, credential, cookie, or another scope. Therefore one source-wide scheduler (not each proxy, worker, or session) must own the permitted request budget.

    RFC 9110 section 10.2.3 permits Retry-After as either an HTTP date or non-negative delay-seconds. Parse both forms, cap automated waiting, retain the original response, and use at most the preapproved retry budget. Do not rotate an exit, alter headers, swap accounts, or increase concurrency after 429. A 401, 403, CAPTCHA, login wall, or explicit objection is a stop-and-review outcome rather than a retry candidate.

    Error outcome retained by the example
    Observed resultStored stateNext action
    429 with approved, bounded Retry-AfterOriginal attempt plus at most one same-policy follow-upWait as directed; do not change identity
    429 without a usable Retry-AfterRate-limited record with status and headerStop automatic work and review schedule or quota
    401 or 403Refused recordUse the official permission or support path
    407Proxy-authentication errorCorrect the issued Databay configuration; do not diagnose it as a destination block
    Other non-2xx or transport failureHTTP or transport error with attempt historyInvestigate the failed layer before any bounded replay
    2xx with malformed or incomplete offerParse error or incomplete normalized recordQuarantine; never substitute zero or reuse an old price silently
  7. Failure and freshness state

    Connect the Tested Pattern to One Approved Route

    The loopback run validates the request and classification logic; it does not test a marketplace, extraction parser, Databay credential, route availability, or destination acceptance. Before a real pilot, map the approved source into the normalized JSON contract, add a source-wide scheduler and cache, then configure one client through the Databay proxy configuration generator. Keep credentials in a secret manager and never print the proxy URL or authorization header.

    Use the Python Requests setup or cURL setup and diagnostic model to prove gateway authentication against a benign endpoint you control. Then inject that reviewed proxy-aware client into observeOffer as fetchImpl; do not assume the direct self-test is routed. Verify requested versus observed exit once, run the authorized offer endpoint, and preserve the same response classification. A successful diagnostic proves only that one connection worked; it does not guarantee that the source will return, expose, or permit the desired offer.

    For a public HTML page, an authorized parser must be versioned and tested separately against retained, permitted fixtures. The example intentionally consumes a normalized JSON shape so it cannot be mistaken for a universal extractor. Layout changes, personalization, stock, promotions, consent state, and experiments can still make two otherwise valid observations incomparable.

  8. Product and seller match

    Pilot on Cost per Usable Comparable Record

    Run the smallest representative pilot that can reject the design: one authorized source, important product and offer shapes, only the markets needed for the decision, a frozen parser version, and one network configuration at a time. Count planned observations, all attempts, bytes, status classes, incomplete offers, location fulfillment, review time, and retained errors. Report latency percentiles with failures separately; do not calculate performance only across successful responses.

    Price-monitoring pilot scorecard
    MetricDecision formulaRequired disclosure
    Usable comparable rateValidated complete offers / eligible planned observationsProduct mix, markets, exclusions, and source
    Route fulfillmentCorrectly observed required region / routed attemptsRequested selector, observed method, and unknowns
    Rate-limit and refusal rate429, 401, 403, and challenge outcomes / attemptsRetry policy and whether any further request occurred
    Freshness complianceUsable offers inside maximum age / usable offersMaximum age and retrieval window
    Cost per usable recordSource, proxy, compute, storage, and review cost / usable comparable offersTraffic validity, billed bytes, retries, and reviewer time

    Expand only if the chosen network supplies decision value that the approved direct source does not, the workflow meets its predeclared quality and cost gates, and stop behavior is verified. No product class guarantees access, extraction, completeness, a unique exit, or a particular success rate.

  9. Comparable offer record

    Keep Price Monitoring Distinct From Ecommerce and Generic Scraping

    This page owns the commercial decision for price monitoring proxies: whether a network-origin variable is needed, which Databay product and smallest paid path fit it, and how to test that choice without dropping errors. The ecommerce proxy guide covers broader catalog QA, localization, storefront testing, seller research, and repricing safeguards. The web-scraping guide covers source approval, request budgets, provenance, and collection design across data types.

    All three workflows share a boundary: public display is not blanket permission. RFC 9309 states that robots rules are not access authorization. Review contracts, API terms, robots instructions where applicable, rate guidance, privacy, intellectual-property, competition, retention, and reuse requirements, and keep every request within Databay's Acceptable Use Policy. Stop when authorization, source behavior, or controls change.

Network decision

Match the IP class to price monitoring

The published cumulative catalogues contain 34M+ residential, 80K+ datacenter, and 800K+ mobile IPs. These cumulative historical catalogue totals are not current live availability. One gateway provides product-specific access. Choose the class per target instead of forcing every job through the same pool.

  • Recommended

    Residential proxies

    34M+ ISP IPs · historical catalogue, not live availabilityProduct-specific; verify the requested route

    Protected targets and precise local views for price monitoring.

    From $0.90/GBat 1 TBExplore
  • Recommended

    Datacenter proxies

    80K+ hosting-network IPs · historical catalogue, not live availabilityKey markets

    Authorized work that permits a hosting-network origin for price monitoring; benchmark the route and destination.

    From $0.50/GBat 1 TBExplore
  • Mobile proxies

    800K+ shared carrier-network IPs · historical catalogue, not live availability155+ countries

    Authorized workflows that explicitly require a carrier-network origin for price monitoring; benchmark the route and destination.

    From $2.50/GBat 512 GBExplore
Field notes

Price Monitoring FAQ

Do I always need a proxy for price monitoring?
No. Use an authorized API, merchant feed, seller export, licensed dataset, or partner report when it supplies the needed offer and market fields. Add a proxy only for a permitted test in which network origin or region is a required variable.
Which Databay product should a price-monitoring pilot start with?
Start with Datacenter when a hosting-network origin and country-or-continent control fit the approved endpoint. Use Premium Residential only when an ISP-origin or finer geographic selector is required, and Residential Flex when country-or-continent ISP origin fits its larger minimum. Validate the smallest suitable current paid order; no class guarantees acceptance.
What is the smallest paid path for price monitoring?
At the August 29, 2026 review, Datacenter began at 10 GB for $12.50, Premium Residential PAYG at 5 GB for $13.75, and Residential Flex at 25 GB for $13.75. Check the linked live pricing page for the current quantity, total, tax treatment, and validity before purchase. Databay does not offer a free trial.
Should a collector retry HTTP 429?
Only within the source's approved global request budget. Preserve the 429, honor a valid Retry-After date or delay, cap automated waiting, keep the same policy and identity, and allow at most the predeclared retry count. Without an approved retry or usable header, retain the rate-limited result and stop.
Why retain failed and incomplete price observations?
Dropping them inflates apparent coverage and hides parser, access, geography, and cost failures. Keep status class, attempt history, validation state, and redacted evidence so usable-record rate and cost per usable record include the complete planned workload.
Does a residential exit reproduce the price every local shopper sees?
No. It changes a network-origin signal. Delivery address, tax, account, membership, cookies, consent, device, inventory, promotions, seller, fulfillment, and experiments can also affect an offer. Record those controls and describe unknowns.
Does the example guarantee extraction from a marketplace?
No. The self-test uses a local normalized JSON fixture and proves only the request, Retry-After, and error-classification logic. A real source needs explicit authorization, a source-specific parser or API mapping, current route validation, and its own measured pilot.

Build the route for price monitoring

Start with the target and the vantage point you need, then pick the network class that fits the work. One account reaches all three.

Pricing, order minimums, and traffic validity vary by network.