How to Scale Feed Polling: Conditional GETs, ETags, and Smart Backoff
The Scale Bottleneck in Modern Feed Ingestion
Polling a handful of RSS or Atom feeds at a fixed interval is straightforward. Polling tens of thousands is a different systems problem. A naive scheduler may issue requests every 15 or 30 minutes regardless of whether a publisher has changed anything. At scale, that creates a steady stream of redundant connections, repeated TLS handshakes, identical XML or JSON downloads, parser executions, database writes, and downstream notifications. The pipeline spends resources proving that nothing changed.
The waste is not limited to bandwidth. Every unchanged response consumes worker time, connection capacity, CPU for decompression and parsing, memory for document trees, and storage or queue capacity if the result is passed through downstream stages. A feed that changes twice per day can still generate dozens of unnecessary fetches if its polling schedule ignores observed activity. The practical path forward is protocol-aware ingestion: use HTTP validators to avoid transferring unchanged representations, then use scheduling decisions that adapt to feed behavior, response status, and recent failures. For teams evaluating broader web scraping efficiency, the same principle applies: successful, targeted requests are more valuable than indiscriminate request volume.

Mastering Conditional GET Requests with ETags and Last-Modified
Conditional GETs allow a client to ask whether its cached representation is still current. On the first successful fetch, the origin may return an ETag, which identifies a particular representation, and a Last-Modified timestamp. The ingestion service stores these values alongside the feed URL. On the next request, it sends If-None-Match with the stored ETag and, when appropriate, If-Modified-Since with the stored timestamp. If the representation has not changed, the origin returns 304 Not Modified rather than sending the feed body.
A 304 response is intentionally bodyless. The worker should update the feed’s last-checked time, refresh relevant response metadata, release the connection, and stop before decompression, XML parsing, normalization, or item processing. This short circuit is the central performance gain. The HTTP semantics defined in RFC 9110 describe 304 responses as responses without content, making them suitable for validation rather than representation transfer.
ETags and timestamps are related but not interchangeable. A strong ETag is intended to identify an exact representation, including meaningful byte-level differences. A weak ETag, marked with the W/ prefix, indicates semantic equivalence but may tolerate minor representation changes. Timestamps are easier to implement but have lower precision and can be unreliable when servers preserve dates, regenerate files, or operate across clocks. Clients should also expect imperfect publishers. Some origins omit validators, change ETags on every request, or provide timestamps that do not reflect item-level updates. The validation layer must therefore be defensive rather than assuming perfect server behavior.
| Request pattern | Typical response | Pipeline impact |
|---|---|---|
| Unconditional GET | 200 OK with complete XML or JSON body | Network transfer, decompression, parsing, normalization, and item checks |
| Conditional GET with unchanged representation | 304 Not Modified with headers and no body | Update check metadata and exit early |
| Conditional GET with changed representation | 200 OK with the new body and validators | Parse, deduplicate, and commit new or changed items |
In practical terms, a 200 response may contain hundreds of kilobytes or several megabytes, while a 304 generally carries only response headers. The exact savings depend on transport overhead and server behavior, but the architectural effect is consistent: unchanged feeds do not enter the expensive parsing path. Store both validators when available, send the most reliable values on subsequent requests, and treat a returned 200 response as an opportunity to replace stale metadata. The client should also identify itself with a useful User-Agent and advertise gzip support, because changed payloads still benefit from compression.
Designing Adaptive Polling and Exponential Backoff
Conditional requests reduce payload cost, but they do not eliminate request cost. A scheduler that sends a conditional request every minute to thousands of quiet feeds can still overwhelm its own worker pool or a publisher’s infrastructure. Polling should therefore be adaptive. Classify feeds by observed velocity, such as high-velocity feeds that publish frequently, medium-velocity feeds that update several times per day, and dormant feeds that change rarely. The classification should be based on measured history rather than a permanent manual label.
Each feed can maintain a next-due time, a minimum interval, a maximum interval, a consecutive-304 counter, and a consecutive-error counter. A high-velocity feed may begin with a 10 to 30 minute interval, while a quiet feed may start at 30 to 60 minutes or longer. After repeated 304 responses, gradually increase the interval within an operational limit. When a 200 response contains new items, reduce the interval carefully, but avoid immediately returning to the most aggressive schedule unless freshness requirements justify it.
Failures require a different response from successful validation. A 429 response should honor Retry-After when present. Network timeouts, connection resets, and 5xx responses generally deserve retryable treatment, while many 4xx responses indicate configuration or authorization problems. Truncated exponential backoff prevents synchronized retry storms: increase the delay after each failure, cap it at a defined maximum, and add randomized jitter so workers do not wake simultaneously.
- After consecutive 304 responses, multiply the interval by a modest factor such as 1.25, subject to the feed’s maximum freshness window.
- After a successful 200 response with new items, retain the current interval or reduce it gradually instead of making an abrupt change.
- After a transient error, use truncated exponential backoff, for example 2, 4, 8, and 16 minutes, then apply a ceiling.
- After repeated 429 responses, honor publisher guidance, reduce concurrency for that origin, and avoid retrying every feed from the same host at once.
- After persistent 404 or 410 responses, move the feed into a verification state rather than polling indefinitely.
Backoff protects both sides of the connection. It reduces wasted requests, frees workers for healthy feeds, and lowers the likelihood that temporary instability becomes a cascading failure. Resource planning guidance from scraping cost analysis similarly emphasizes request volume, response size, concurrency, and execution time as connected cost drivers. A scheduler should make those variables observable and controllable rather than treating them as fixed infrastructure expenses.
State Storage and Downstream Deduplication Architecture
The scheduler needs durable, low-latency state for every feed. A key-value store is often a good fit because the hot path requires a small record keyed by a normalized feed URL or internal feed identifier. Typical fields include ETag, Last-Modified, last status, last successful fetch time, next scheduled time, interval bounds, consecutive 304 count, error count, content hash, and parser version. Redis, a relational table with an appropriate index, or a distributed database can work; the important properties are atomic updates, predictable latency, and recovery after worker failure.
Validator state must be updated carefully. If a publisher changes its ETag on every request while returning identical content, blindly trusting the tag will produce 200 responses forever. Compare representation checksums when this behavior is detected, record validator churn as an origin quality signal, and avoid treating a new ETag alone as proof of new feed items. Conversely, a stable ETag does not guarantee that every downstream consumer sees the same logical content if an intermediary is misconfigured. Store enough metadata to diagnose these inconsistencies, including response status, content length, and selected response headers.
Item-level deduplication is separate from feed-level validation. A changed feed may repeat its existing entries, reorder them, or regenerate identifiers. Prefer a stable GUID or Atom ID, but combine it with canonicalized links, normalized titles, publication metadata, and a content checksum. A checksum is especially useful when a publisher assigns a new identifier to unchanged content. Canonicalization should remove irrelevant tracking parameters only when the business rules justify it, because aggressive URL normalization can merge genuinely distinct resources.
- Validate the representation. Load the feed’s stored validators, issue a conditional GET, and classify the response as unchanged, changed, retryable, or terminal.
- Parse only changed content. For a 200 response, decompress and parse with bounded memory, tolerate expected RSS or Atom irregularities, and normalize dates, links, identifiers, and text.
- Deduplicate and stage items. Check stable IDs first, then apply canonical link and content checksum rules. Keep staging separate from committed records so partial failures do not create inconsistent state.
- Commit atomically. Store new items, update feed validators and scheduling state, publish downstream events, and record metrics in a transaction or idempotent sequence.
This staged design reduces fragility when workers crash between parsing and storage. It also makes replay safer. If a queue redelivers a completed fetch, item keys and content hashes should make the operation idempotent. Feed management systems commonly treat already processed content as a separate concern from transport retrieval, so ingestion teams should audit all independent paths to ensure the same feed is not being imported through overlapping connectors.
Operationalizing Health Checks and Circuit Breakers
Not every failing feed is temporarily unavailable. A timeout may indicate a short network incident, while a 410 Gone response may indicate permanent deprecation. A 404 can mean a moved endpoint, an invalid URL, or a publisher-side deployment error. Health checks should combine status codes, retry history, DNS results, TLS failures, response shape, and the age of the last successful representation. Preserve the last known good payload or item set where appropriate, but clearly mark it as stale so downstream consumers do not mistake cached data for fresh data.
A circuit breaker isolates unhealthy feeds. In the closed state, normal polling proceeds. After a threshold of consecutive failures, the breaker opens and suppresses ordinary attempts for a cooldown period. A limited half-open probe then tests whether the origin has recovered. The breaker should operate at more than one scope: per feed for isolated failures, per origin for host-level rate limiting, and globally for fleet-wide infrastructure incidents.
- Track the ratio of 304, 200, 3xx, 4xx, 429, and 5xx responses.
- Measure payload size before and after compression, parser latency, and item counts per successful fetch.
- Monitor worker saturation, queue age, connection pool usage, timeout rates, and scheduler drift.
- Alert on sudden drops in 304 ratio, repeated validator churn, or unusual increases in response size.
- Record feed freshness separately from request success, because a successful 304 is healthy transport behavior but does not create new content.
These signals reveal problems before users report stale data. A sudden increase in 200 responses may indicate publisher cache misconfiguration, while a rising 304 ratio combined with delayed freshness may indicate that the feed itself has stopped updating. Metrics should retain origin and feed dimensions, but cardinality must be controlled so observability does not become the next bottleneck.
Build a Resilient Pipeline That Scales Efficiently
Conditional GETs change the economics of feed ingestion. When validators work correctly, unchanged feeds consume a small request and header exchange instead of transferring and parsing a complete representation. That reduces bandwidth, decompression work, parser CPU, memory pressure, database activity, and downstream queue traffic. The benefit compounds across thousands of feeds, particularly when large feeds are checked frequently.
Adaptive scheduling completes the design. A reliable service observes publishing velocity, responds to 304 patterns, honors rate-limit signals, separates transient failures from permanent deprecations, and uses circuit breakers to protect shared infrastructure. The result is not merely a faster scraper. It is a more predictable ingestion system that behaves responsibly toward publishers and remains operable as feed volume grows.
- Persist ETag and Last-Modified values for every feed whenever the origin provides them.
- Short circuit 304 responses before decompression, parsing, and item processing.
- Assign dynamic intervals using observed feed activity, not one global polling constant.
- Implement capped exponential backoff with jitter and explicit handling for 429, 5xx, 404, and 410 responses.
- Use stable item identifiers with canonical links and content checksums for deduplication.
- Make state updates and downstream writes idempotent so retries remain safe.
- Measure validator effectiveness, payload sizes, freshness, queue age, and worker saturation continuously.
