Key Takeaways
A production-focused web scraping architecture guide covering job contracts, durable queues, HTTP/browser routing, session and proxy policy, retry classification, data validation, evidence retention, failure testing, and observability.
Web Scraping Architecture Should Make Failures Explicit
A production scraper is not just a fetch loop. It is a pipeline that accepts work, chooses the cheapest valid fetch mode, controls concurrency and session state, validates outputs, and records enough evidence to explain failures.
The most useful architecture is therefore not the one with the most services. It is the one that makes work ownership, retries, state, data quality, and stop conditions explicit.
This guide focuses on authorized data collection and testing. A proxy changes network routing; it does not erase browser fingerprints, guarantee access, or override a site's terms, authentication, robots directives, privacy requirements, or security controls.
A Reference Architecture
The boundaries matter more than the exact technology choices. Scrapy's current architecture follows the same broad separation: scheduling, downloading, spider logic, and item pipelines are distinct components, while item pipelines are commonly used for cleansing, validation, duplicate handling, and persistence.Scrapy architecture↗ Item pipeline↗
1. Define the Job Contract Before You Add Workers
Every queued job should contain enough information to execute and audit the request without depending on hidden process state.
A practical job envelope might include:
The values above are examples, not BytesFlows defaults. The important fields are identity, target, fetch policy, attempt budget, deadline, and expected output schema.
Use an idempotency key such as job_id + canonical_url + schema_version when writes could be repeated. Durable queues commonly provide at-least-once delivery rather than exactly-once processing. For example, Amazon SQS documents that a message can be delivered more than once, so consumers still need idempotent processing.Amazon SQS visibility timeout↗
2. Treat the Queue as Work Ownership, Not Just a Buffer
A queue should answer four questions:
- Who owns this job now?
- When does ownership expire if the worker dies?
- Which failures are retryable?
- When is the job permanently stopped?
A common model is lease → process → acknowledge. If the worker crashes before acknowledgement, the lease expires and another worker can claim the job. The visibility timeout or lease duration must be longer than normal work, but it should not become an unlimited lock.
Track at least:
- queued jobs
- oldest-job age
- in-flight jobs
- lease expirations
- retries by reason
- dead-lettered jobs
Queue depth alone is insufficient: a small queue with a very old head job can indicate a stuck partition or target-specific failure.
3. Route Each Job to the Cheapest Fetch Mode That Produces Correct Data
Do not send every URL through a browser.
Use an HTTP client when the required data is present in the response or a documented API and browser state is unnecessary. Escalate to browser automation when the task genuinely depends on client-side rendering, browser storage, navigation state, or interactions that are part of an authorized workflow.
A useful decision sequence is:
- Try the known lowest-cost valid path.
- Validate the response, not just the HTTP status.
- Escalate only when the failure classification says a browser is required.
- Record the selected mode so the decision can be measured later.
A 200 response can still be an unusable login page, challenge page, empty shell, wrong locale, or changed template. Conversely, a browser is not automatically a solution to 403 or policy restrictions.
4. Browser Workers Need Session Isolation
For Playwright workloads, use a fresh or deliberately managed BrowserContext boundary instead of sharing cookies and storage accidentally across unrelated jobs. Playwright documents BrowserContext as an isolated browser session and recommends closing contexts when they are no longer needed.Playwright BrowserContext↗
If authenticated state is persisted, protect it as a secret. Playwright warns that stored browser state may contain cookies and headers capable of impersonating the authenticated account.Playwright authentication↗
For stateful jobs, bind the browser context, application session, and any sticky proxy session to the same logical session_key. Do not rotate a network route in the middle of a workflow that expects continuity unless the application has been designed to recover from that change.
5. Make Proxy Routing a Policy Layer
Proxy configuration belongs in a routing policy, not scattered through spider code.
The policy should be able to decide:
- direct vs proxy routing
- required country, region, city, or ASN when the task genuinely needs it
- rotating vs sticky session behavior
- route health and quarantine state
- whether retrying on a different route is permitted
Do not assume that residential routing is always better. Measure the outcome for the specific target and market. A proxy can change the apparent network origin, but browser/device fingerprints, cookies, account state, request behavior, TLS characteristics, and application-layer signals remain separate concerns.
For more detail on route/session design, see Web Scraping Proxy Architecture and Proxy Rotation Strategies.
6. Classify Failures Before Retrying
Retries should be based on failure class, not a blanket except: retry rule.
| Signal | Likely layer | Default action |
|---|---|---|
407 Proxy Authentication Required | Proxy authentication | Stop blind rotation; verify credentials and proxy configuration |
429 Too Many Requests | Target rate policy | Respect Retry-After when present; reduce request rate |
403 Forbidden | Target authorization / policy / security | Inspect response and authorization; do not assume a new IP is appropriate |
| Connect timeout | Network / proxy / target | Retry only within a bounded budget after layer-specific diagnostics |
| Schema validation failure | Extraction / target change | Do not hide with network retries; quarantine or update extractor |
| Wrong market or locale | Routing or application state | Verify both exit location and page-level market evidence |
HTTP 429 may include Retry-After, but the standard does not require servers to identify clients by IP address. Rate limiting may use credentials, cookies, resources, or other scopes, so rotating an IP is not a universal fix.RFC 6585↗
7. Use Bounded Retry Budgets
A retry policy needs both an attempt budget and a deadline. Without both, a degraded target can consume the entire worker fleet.
This small Python example separates retryable transport/server failures from terminal HTTP responses. It uses only the standard library so the behavior is easy to inspect:
The max_attempts, timeout, retryable status set, and backoff cap are example values. Production values should come from workload latency, target policy, service-level objectives, and the cost of duplicate work. If a response includes a valid Retry-After, prefer honoring that signal over immediately applying generic backoff.
8. Validate Before You Commit Canonical Data
Extraction success is not the same as data success.
Validation should happen before canonical writes and should check the invariants that downstream systems depend on:
- required fields exist
- types and units are correct
- timestamps and currencies are normalized
- known placeholders and challenge pages are rejected
- duplicate records use a documented merge rule
- source URL and capture time are retained
- schema version is recorded
Keep raw evidence separately from normalized business data when debugging or reproducibility matters. Evidence retention should be minimized to what the workload actually needs, with access controls for pages that may contain personal or authenticated data.
9. Store Raw Evidence and Canonical Records Separately
A practical split is:
- raw response / browser evidence → object storage with lifecycle rules
- canonical extracted records → database or warehouse
- job and attempt metadata → operational store
- metrics → time-series or observability backend
Do not make screenshots, HAR files, traces, cookies, or full HTML permanent by default. They can contain tokens, personal data, query parameters, or account state. Set an explicit retention period and redact secrets before logs or artifacts leave the worker.
10. Measure Data Quality, Not Just Request Success
Useful metrics include:
- usable-record rate
- validation-failure rate
- empty/challenge-page rate
- p50 and p95 job completion time
- attempts per successful output
- bytes transferred per usable output
- browser-escalation rate
- queue age and lease-expiration rate
- 407, 403, 429, timeout, and wrong-market counts by target
Avoid presenting a single global “success rate” without defining what counts as success. A request that returns 200 but produces invalid data should not be counted as a successful collection result.
11. Failure Modes to Test Before Scaling
Before adding worker capacity, deliberately test these conditions in a staging or authorized environment:
- worker exits after fetching but before acknowledgement
- duplicate queue delivery
- target returns a login or challenge page with
200 - extractor receives a changed DOM/schema
- proxy authentication fails with
407 - target returns
429with and withoutRetry-After - a browser process or context crashes
- sticky session disappears mid-workflow
- object storage or database becomes temporarily unavailable
- a job exceeds its deadline while still retrying
The expected result should be deterministic: retry, quarantine, dead-letter, or stop. If operators cannot predict the outcome, the failure policy is not finished.
Production Checklist
407, 403, 429, timeout, parser, and validation failures are classified separately.FAQ
Should every production scraper use a distributed queue?
No. A single-process scheduler can be simpler and more reliable for small workloads. Add a durable distributed queue when you actually need independent workers, crash recovery, prioritization, or horizontal scaling.
Should every JavaScript-heavy page use Playwright?
Not necessarily. First determine whether the required data is available through a stable response or documented API. Browser automation is justified when the task depends on browser execution or interaction, not merely because a site uses JavaScript.
Does a residential proxy make a browser session undetectable?
No. Network origin is only one signal. Browser state, request behavior, cookies, TLS/network characteristics, account state, and application-specific controls remain independent.
Is 429 fixed by rotating proxies?
Not reliably. RFC 6585 does not define rate-limit identity as IP-only. Respect target policy and Retry-After when provided, and reduce load before considering routing changes.
What should be stored for debugging?
Store the minimum evidence needed to explain failures: structured attempt metadata first, then selected raw responses, traces, or screenshots when justified. Treat authenticated state and captured page data as sensitive.
Related BytesFlows Guides
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.