Prompt

How do I set up a scheduled crawl tool for tracking broken outbound links across thousands of editorial pages?

Latest observation

Jul 21, 2026 · OpenAI APIWeb search: off

To set up a scheduled crawl tool for tracking broken outbound links across thousands of editorial pages, you want a pipeline that is:

  1. Discovering URLs to scan
  2. Crawling pages efficiently
  3. Extracting outbound links
  4. Checking link health
  5. Scheduling recurring runs
  6. Reporting and alerting on changes

Here’s a practical blueprint.


1) Define the scope

Decide upfront:

  • Which pages to crawl
    • Sitemap URLs
    • CMS export
    • Database of editorial pages
    • Seed list + internal discovery
  • Which links count as outbound
    • Only external domains?
    • Mailto/tel ignored?
    • Social links excluded?
  • What counts as broken
    • HTTP 4xx/5xx
    • DNS failure
    • Timeout
    • Soft 404s
    • Redirect chains ending in error
  • How often to run
    • Daily, weekly, hourly, per publish event

For editorial sites, a common setup is:

  • Crawl all editorial pages nightly
  • Recheck only pages changed in the last 24h more frequently
  • Revalidate previously broken links separately

2) Choose an architecture

A scalable pattern:

Components

  • Scheduler: cron, Airflow, Celery beat, Cloud Scheduler, etc.
  • Crawler workers: fetch pages and extract links
  • Link checker workers: validate outbound URLs
  • Storage: database for results/history
  • Notification layer: email/Slack/teams
  • Dashboard: view failures, trends, and ownership

Recommended data flow

  1. Scheduler enqueues page URLs
  2. Crawler fetches page HTML
  3. Extract outbound links from DOM
  4. Deduplicate links
  5. Send outbound URLs to checker queue
  6. Checker requests each target URL
  7. Persist status/results
  8. Alert if threshold exceeded or new failures appear

3) Use a queue-based design for scale

For thousands of pages, avoid a single sequential script.

Why queues help

  • Parallelize across workers
  • Retry failures cleanly
  • Rate-limit outbound requests
  • Avoid overloading target sites
  • Separate page crawling from link checking

Typical queue breakdown

  • Page crawl queue: editorial page URLs
  • Link check queue: outbound URLs
  • Dead-letter queue: items that repeatedly fail

4) Crawl pages and extract outbound links

When fetching each editorial page:

Fetch safely

  • Set timeouts
  • Follow redirects
  • Identify yourself with a user agent
  • Respect robots.txt if required by your policy

Extract links

From HTML:

  • <a href="">
  • href in canonical/alternate tags if relevant
  • maybe embedded rich content if your pages generate links dynamically

Filter

Exclude:

  • internal links
  • anchors (#section)
  • mailto:
  • tel:
  • javascript URLs
  • tracking pixels or non-HTTP resources, unless needed

Example filter logic

Treat as outbound if:

  • URL scheme is http or https
  • Domain is not in your allowed internal domain list

5) Check outbound links efficiently

For each outbound URL:

Preferred checks

  1. Try HEAD
  2. Fall back to GET if:
    • HEAD not allowed
    • status is suspicious
    • site blocks HEAD

Record:

  • final URL after redirects
  • status code
  • response time
  • error type
  • redirect depth
  • timestamp

Handle edge cases

  • 403/429 may be anti-bot, not broken
  • Some sites block automated requests
  • Some servers return 200 for soft-404 pages
  • Timeouts and DNS errors should be treated separately from HTTP errors

A good strategy is to categorize:

  • Broken
  • Warning
  • Blocked/unknown
  • Healthy

6) Store results in a database

Use a relational DB or warehouse. Suggested tables:

pages

  • id
  • url
  • last_crawled_at
  • content_hash
  • status

outbound_links

  • id
  • source_page_id
  • target_url
  • normalized_url
  • first_seen_at
  • last_seen_at

link_checks

  • id
  • outbound_link_id
  • checked_at
  • http_status
  • final_url
  • latency_ms
  • error_type
  • is_broken
  • notes

alerts

  • id
  • type
  • created_at
  • severity
  • details

This gives you history and trend tracking instead of just a latest snapshot.


7) Schedule runs

Pick a scheduler based on your environment:

Simple options

  • cron on a server
  • GitHub Actions for smaller jobs
  • Cloud Scheduler + Cloud Run
  • AWS EventBridge + Lambda/ECS
  • Airflow if workflows are complex

Example scheduling strategy

  • Nightly full crawl of all editorial pages
  • Hourly crawl for newly published/updated pages
  • Daily recheck of known broken outbound links
  • Weekly cleanup for stale links and duplicates

8) Add normalization and deduplication

Normalize URLs before storing/checking:

  • remove fragments (#...)
  • standardize scheme where appropriate
  • lowercase hostname
  • strip tracking query params if desired
  • resolve relative URLs

Deduplicate at two levels:

  • Same outbound URL appearing on many pages
  • Same page being crawled multiple times

This can massively reduce load.


9) Report meaningful results

Don’t just report “broken links.” Give editors useful context:

  • Source page title
  • Source page URL
  • Broken outbound URL
  • HTTP status or error
  • First seen / last seen
  • Number of pages affected
  • Severity
  • Suggested action

Useful summaries

  • New broken links since last run
  • Broken links by section/author/category
  • Pages with the most broken outbound links
  • Links broken across multiple pages

10) Alerting rules

Avoid alert spam. Trigger alerts on:

  • New broken link discovered
  • Broken link appearing on multiple pages
  • Same link broken for N consecutive runs
  • Spike in broken links after a deployment

Alert destinations:

  • Email
  • Slack
  • MS Teams
  • Jira ticket creation

11) Respect performance and politeness

Especially when checking thousands of pages/links:

  • Use concurrency, but cap it
  • Rate-limit requests per domain
  • Cache known results
  • Honor retry-after headers
  • Use exponential backoff
  • Consider robots.txt/policy constraints

A practical setup might be:

  • 10–50 concurrent page fetches
  • 20–100 concurrent link checks
  • Per-domain concurrency cap of 1–3

12) Tooling options

Build your own

Good if you need custom logic and tight integration.

Common stack:

  • Python
    • Scrapy for crawling
    • Requests/httpx for checking
    • Celery/RQ for queues
    • PostgreSQL for storage
  • Node.js
    • Playwright/Puppeteer for dynamic pages
    • BullMQ for queues
  • Go
    • High performance for checking at scale

Use existing tools

  • Screaming Frog SEO Spider with scheduled crawls
  • Sitebulb
  • Ahrefs/Site audits
  • Custom enterprise link checkers

These are quicker to start but may be harder to customize for editorial workflows and ownership mapping.


13) Recommended implementation pattern

If you want a robust custom setup:

Stack example

  • Crawler: Scrapy or Playwright
  • Queue: Celery + Redis
  • Storage: PostgreSQL
  • Scheduler: cron or Airflow
  • Alerts: Slack webhook + email
  • Dashboard: Metabase / Grafana / custom admin page

Workflow

  1. Nightly scheduler enqueues page URLs from sitemap/CMS
  2. Workers crawl pages and store outbound links
  3. Checker workers validate unique outbound URLs
  4. Results stored and compared to previous run
  5. Alerts sent for new regressions
  6. Dashboard shows trends and ownership

14) Minimal pseudo-workflow

for page in editorial_pages:
    html = fetch(page)
    links = extract_outbound_links(html)
    for link in unique(links):
        status = check_url(link)
        save_result(page, link, status)

if broken_links_new_or_changed:
    notify_slack()

In production, each step should run asynchronously with retries and persistence.


15) If you want the fastest path

If you need something working quickly:

  1. Export editorial URLs from your CMS or sitemap
  2. Run a scheduled Python job nightly
  3. Parse pages with BeautifulSoup/lxml
  4. Check outbound URLs with httpx
  5. Write results to PostgreSQL or CSV
  6. Send a Slack summary
  7. Add dedupe + retries + concurrency next

If you want, I can also give you:

  • a reference architecture diagram
  • a Python example using Scrapy + Celery
  • a Node.js/Playwright version
  • or a database schema and cron setup for this exact use case.

Observation history

Unlock observation history

Create a free Obsurfable account to unlock historical responses, model comparisons, and deeper brand intelligence.

How did Obsurfable measure this prompt?

Obsurfable records AI answers to buyer-style prompts in its research corpus (1 observation for this page). Metrics are distributions over observations, not a single static ranking.

Which AI systems does Obsurfable collect answers from?

OpenAI, ChatGPT, Google, Gemini, Google AI Mode, Anthropic, Claude, Perplexity, Grok, DeepSeek, Mistral, Copilot, and Meta AI.