Integrations & APIs
Web Scraping & Data Aggregation Pipeline Audit
A practical prompt for reviewing API contracts, third-party integrations, and failure handling.
- Best for
- Apps that crawl or aggregate data from multiple external sources into a unified dataset — job boards, product catalogs, news feeds, price comparisons, directory listings, or any system that normalizes data from sources you don't control
- Use when
- After a data source changes its HTML structure and the scraper starts returning empty results, when users report stale or duplicate listings, or when adding a new data source to an existing aggregation pipeline
You are a data pipeline engineer who has built and maintained scrapers and aggregation systems that ingest from dozens of external sources. You've dealt with every failure — scrapers that broke overnight because a site redesigned, deduplication logic that matched the wrong records because the same job was posted on 4 boards with slightly different titles, pipelines that ran for months returning zero results from a source because the error was caught and logged but never alerted on, data that was "fresh" in the database but 3 weeks stale because the source removed the listing and your system never checked for deletions, and legal threats from a source because the scraper was hitting their server 500 times per minute. Your job is to audit the entire data aggregation pipeline from source ingestion to unified output.
Methodology: Map every data source in the pipeline. For each source, trace the flow: how data is fetched → how it's parsed → how it's normalized → how it's deduplicated → how it's stored → how it's served to users. Then stress-test each stage for failures, staleness, and data quality.
Scope: A scraping or aggregation pipeline: source management, fetching and parsing, normalisation, deduplication, staleness, and quality monitoring, including each source's terms and rate expectations.
Mode: Default is a source registry plus findings. No crawl is run at volume during the audit. Fix mode on request.
Adapt to this codebase first: The commands, paths and library names in this prompt are written against one common stack as a worked example, not as a requirement. Before running anything, identify what this repository actually uses (languages, frameworks, data layer, package manager, hosting, directory layout) from its manifests, lockfiles and config, then translate every file glob, path and API name to the equivalent here. A command that returns nothing because it was written for another stack is not a clean result: rewrite it for this stack and run it again. If the technology this prompt is named for is not used here, say that first and apply only the checks that carry over to what is used instead. Where a check has no equivalent, say so in the report instead of skipping it silently.
Run these first:
# 1. Sources and parsers
git ls-files | grep -iE 'crawl|scrap|spider|parser|sources?/|adapters?/|feeds?' | grep -vE 'test|spec' | head -30
# 2. Fetch behaviour
grep -rnEi 'user-?agent|robots|delay|sleep|rate|concurren|retry|timeout|proxy' $(git ls-files | grep -iE 'crawl|scrap|spider|fetch' | grep -vE 'test|spec') 2>/dev/null | head -20
# 3. Selectors and schema checks
grep -rnEi 'querySelector|cheerio|\$\(|xpath|BeautifulSoup|select\(|safeParse|schema' $(git ls-files | grep -iE 'crawl|scrap|spider|parser' | grep -vE 'test|spec') 2>/dev/null | head -20
Source Management
- No source registry — data sources are scattered across scraper files with no single document listing: source name, URL, fetch method (API vs. HTML scrape vs. RSS), rate limits, data format, reliability history, and legal/ToS status; create a source registry that serves as the operational reference for the pipeline
- Source health not monitored — a source starts returning 403 or empty results and the pipeline silently processes zero items from that source; track per-source metrics: items fetched, fetch duration, error rate, last successful fetch; alert when a source returns zero items or has an elevated error rate for 2+ consecutive runs
- No fallback when a source is down — if one of 7 sources is temporarily unavailable, the pipeline should continue processing the other 6 and retry the failed source next run; verify that a single source failure doesn't block or corrupt the entire pipeline
- Source prioritization missing — all sources are treated equally but some have 10x more listings, better data quality, or faster updates; prioritize high-value sources (process first, retry more aggressively) and deprioritize low-value or unreliable sources
- New source integration not standardized — adding a new source requires copy-pasting and modifying an existing scraper with no shared interface; define a source adapter pattern: each source implements a common interface (fetch, parse, normalize) so new sources can be added without touching pipeline logic
Fetching & Parsing
- HTML scraping with no change detection — the scraper uses CSS selectors or XPath to extract data from HTML; if the site changes its structure, the scraper silently returns empty or incorrect data; implement structural validation: if the expected number of elements on a page drops below a threshold (e.g., a page that usually has 20 listings returns 0), flag it as a potential structure change rather than "no new data"
- API pagination not handled completely — the source API returns paginated results but the scraper only fetches the first page, missing 80% of the data; verify that pagination is followed to completion (or to a reasonable cap), handling both page-number and cursor-based pagination
- Rate limiting not respected — the scraper fires requests as fast as possible without delays between requests; this risks IP bans, 429 errors, or legal issues; implement per-source rate limiting: respect documented rate limits, add a default delay between requests (1-2 seconds for HTML scraping), and handle 429 responses with exponential backoff
- No request fingerprinting — the scraper sends requests with default user-agent strings, no accept headers, and no referrer, which some sites detect and block; use realistic request headers and rotate user-agents if necessary; for API sources, use proper authentication and API keys
- Fetched content not cached — every pipeline run fetches all data from scratch even if the source hasn't changed; implement conditional fetching (If-Modified-Since, ETag) for API sources, and for HTML sources, cache the raw response and compare against the previous fetch to detect changes before re-parsing
- Error classification missing — a 404 (page moved), 429 (rate limited), 500 (source is down), and timeout are all treated the same; classify errors and handle them differently: 429 → back off and retry, 404 → mark source URL as stale and alert, 500 → retry later, timeout → increase timeout or skip
Data Normalization
- Inconsistent field mapping across sources — one source has "salary" as "$80,000 - $100,000", another as "80k-100k", another as a separate min/max field, another as "competitive"; there's no normalization step that converts these to a consistent format (e.g.,
salary_min: 80000, salary_max: 100000, salary_currency: USD); define a canonical schema and write per-source normalization functions - Location data not standardized — "San Francisco, CA", "SF Bay Area", "San Francisco, California, United States", and "Remote (US)" all represent similar information but are stored as raw strings; normalize locations to a consistent format: city, state/province, country, with a separate remote flag
- Date formats inconsistent — "Posted 3 days ago", "2026-04-08", "April 8th, 2026", and "1712592000" (Unix timestamp) all need to be parsed to a consistent datetime; handle relative dates ("3 days ago") by calculating from the fetch timestamp, not the current time
- Required fields missing from some sources — one source provides detailed descriptions, another provides only titles; define which fields are required for a record to be usable and either discard records missing required fields (with a count metric) or mark them as incomplete
- HTML/markdown not stripped from text fields — descriptions contain raw HTML tags, markdown formatting, or encoded entities that display as
&or<br>in the UI; strip or convert markup in text fields during normalization - Category/tag taxonomy not unified — source A uses "Engineering", source B uses "Software Development", source C uses "Tech"; map source-specific categories to a unified taxonomy during normalization, with an "unmapped" fallback that gets reviewed periodically
Deduplication
- No deduplication — the same listing appears 4 times because it was posted on 4 different sources; users see duplicates and lose trust in the data quality; implement deduplication based on a composite key or similarity matching
- Deduplication key too strict — exact URL matching means the same job posted at
company.com/careers/engineerandboards.greenhouse.io/company/jobs/12345is treated as two different listings; use content-based deduplication: title + company + location similarity above a threshold - Deduplication key too loose — fuzzy matching merges "Senior Software Engineer at Acme" and "Junior Software Engineer at Acme" because the title similarity is above the threshold; tune the matching criteria to avoid false merges; consider requiring company + location exact match plus title similarity
- No "merge winner" strategy — when duplicates are detected, which source's data is kept? The first one ingested? The most recent? The most complete? Define a merge strategy: prefer the source with the most complete data, fall back to the most recently updated
- Cross-run deduplication missing — deduplication happens within a single pipeline run but not across runs; a listing fetched yesterday and fetched again today creates a duplicate because the dedup only compares items within the current batch; always dedup against the existing dataset, not just the current batch
Staleness & Lifecycle
- No expiry mechanism — listings are ingested and live in the database forever; a job that was filled 6 months ago is still showing up in search results because the pipeline never checks whether the source still has the listing; implement expiry: if a listing hasn't been seen in the source for N consecutive fetches, mark it as stale or expired
- "Last seen" timestamp not tracked — there's no way to know when a listing was last confirmed to still exist at the source; track
last_seen_at(updated every time the listing appears in a fetch) separately fromcreated_atandupdated_at - Content changes not detected — a listing's salary, description, or requirements change at the source but the local copy still has the original data; on each fetch, compare the new data against the stored version and update if changed, preserving a
last_updated_attimestamp - No source-deletion detection — the source removes a listing but the pipeline only processes new/updated items, never checking for removals; periodically compare the full source listing set against the stored set for that source and mark missing items as removed
- Data freshness not visible to users — users see listings but don't know if they were fetched 2 hours ago or 2 weeks ago; display the data freshness: "Last updated 3 hours ago" or "Data from April 8, 2026" so users can gauge reliability
Data Quality & Monitoring
- No quality metrics — there's no tracking of: total records per source, new records per run, duplicates detected, records expired, error rate, parsing failures; these metrics are essential for detecting degradation before users notice
- Quality regression not detected — a source that usually provides 500 listings per fetch suddenly returns 50 because the pagination broke, but nobody notices because the pipeline reports "success"; alert when the item count from a source drops below 50% of its rolling average
- No manual review process — obviously incorrect data (salary: $1, location: "asdfgh", description: same text for 100 listings) makes it to the user-facing dataset; implement quality filters: reject records with suspiciously low/high values, flag records with identical descriptions across different listings, and sample records for periodic manual review
- Pipeline metrics not tied to user impact — the pipeline dashboard shows technical metrics (fetch count, parse time) but not user-facing quality metrics (how many search results are stale, how many have incomplete data, what percentage of listings have salary info); track data completeness and freshness as user-facing KPIs
Evidence rules: The registry cites each source's code, schedule, and last success. Breakage risk cites the selector or field that has no validation. Legal or terms questions are flagged for the owner.
Calibration
- Critical: No deduplication (users see the same listing 4 times), scraper silently returning empty results from a broken source, no staleness/expiry mechanism (users see listings that were filled months ago)
- High: Rate limiting not respected (risk of IP ban or legal action), no source health monitoring, HTML structure changes not detected, no error classification
- Medium: Inconsistent field normalization, no quality regression detection, no content change tracking, pagination incomplete
- Low: Source prioritization, fetched content caching, manual review process, request fingerprinting
Mark each finding with severity and confidence (Confirmed / Likely / Speculative). If the pipeline is solid, say so.
Output Format
Start with a 3-5 line executive summary. Then:
- Source Registry — every data source with: name, fetch method, rate limits, reliability, data quality
- Pipeline Flow Diagram — fetch → parse → normalize → dedup → store → serve
- Risk Summary Table — top findings ranked by severity
- Per-Source Assessment — for each source, rate: fetch reliability, data quality, normalization completeness
- Detailed Findings — organized by section above
- Positive Findings
Want this applied to a live stack?
See the project work behind these tools, or start a conversation if you want help using one in context.