Bulk URL Status & Broken Link Checker avatar

Bulk URL Status & Broken Link Checker

Pricing

Pay per event

Go to Apify Store
Bulk URL Status & Broken Link Checker

Bulk URL Status & Broken Link Checker

Check hundreds of URLs at once for status code, full redirect chain, response time, and security headers — spot broken links and redirect loops without downloading page bodies.

Pricing

Pay per event

Rating

0.0

(0)

Developer

DevilScrapes

DevilScrapes

Maintained by Community

Actor stats

0

Bookmarked

2

Total users

1

Monthly active users

a day ago

Last modified

Share


🎯 What this scrapes

Point this at any list of URLs — a sitemap export, a link audit spreadsheet, an affiliate feed, a migration checklist — and get back exactly what a real visitor's browser would see for each one: the final status code, the complete redirect chain (every 301/302/307/308 hop in order), response time, and the security headers that show up in an SEO or infra audit. There's no target API to key against and no site-specific logic — any http:// or https:// URL is a valid input. Nothing gets crawled beyond the URLs you supply, and the page body is never downloaded — this is a status/health check, not a scraper.

🔥 What we handle for you

  • 🌐 Every check runs through Apify Proxy — a clean vantage point for the request, not your own office or CI-runner IP.
  • 🕵️ Real-browser TLS fingerprint — requests go out via curl-cffi Chrome impersonation, so a HEAD or GET from us doesn't look like a bare Python script to the target.
  • 🧱 Per-URL fault isolation — a DNS failure, timeout, or SSL error on one URL never takes the batch down; every URL gets its own row, always.
  • 🧊 Clean, typed dataset rows — Pydantic-validated, one consistent shape whether the URL succeeded or failed, ready for JSON/CSV/Excel export.
  • ⏱️ Concurrency and timeout you control — bounded in-flight requests and a per-request timeout mean one slow host can't stall the whole run.
  • 💰 Pay only for delivered rows — one flat start fee, then a small per-URL charge whether that URL turned out healthy or broken (a recorded failure is still a delivered result).

💡 Use cases

  • SEO audits — find broken internal and external links across a site before Google does.
  • Site-migration QA — confirm every old URL 301s to the right new one, not a 404 or a redirect loop.
  • Affiliate-link health monitoring — catch dead or redirected affiliate URLs before they cost you commission.
  • Uptime and redirect-chain spot checks — verify a domain move or CDN change resolved the way you expect.
  • Sitemap link validation — run a sitemap's URL list through in bulk and get one row per entry.

⚙️ How to use it

  1. Click Try for free at the top of the page.
  2. Fill in the input form — most fields have sensible defaults.
  3. Click Start. Output streams into the run's dataset.
  4. Export from Storage → Dataset as JSON, CSV, or Excel — or fetch via the API.

📥 Input

FieldTypeRequiredDefaultNotes
urlsarrayyes['https://httpbin.org/status/200', 'https://httpbin.org/redirect/2', 'https://httpbin.org/status/404']The URLs to check, one per line. Must be http:// or https://. Deduplicated automatically.
followRedirectsbooleannoTrueWhen true, chase the redirect chain (up to 20 hops) and report the final destination. When false, report only the first…
timeoutSecsintegerno15Timeout applied to every request (HEAD, GET fallback, and each redirect hop).
maxConcurrencyintegerno10Maximum number of URLs checked in flight at once.
maxUrlsintegerno750Hard cap on accepted input URLs after deduplication. The run fails input validation (no charge) if exceeded.
proxyConfigurationobjectno{'useApifyProxy': True}Apify Proxy configuration. No forced proxy group — the target set is arbitrary customer-supplied hosts, not a known…

Example input

{
"urls": [
"https://httpbin.org/status/200",
"https://httpbin.org/redirect/2",
"https://httpbin.org/status/404",
"https://this-domain-does-not-exist-devilscrapes-qa.invalid"
],
"followRedirects": true,
"timeoutSecs": 15,
"maxConcurrency": 10,
"maxUrls": 750,
"proxyConfiguration": {
"useApifyProxy": true
}
}

📤 Output

Every row is one dataset item.

FieldTypeNotes
input_urlstringThe URL as supplied in the input, echoed back.
final_urlstringThe last resolved URL — equal to input_url when no redirect occurred or followRedirects is false.
final_status_codeintegerThe last response's HTTP status code. Null on a connection failure.
redirect_chainarrayEvery 3xx hop encountered before the final response, in order, as {url, status_code}. Empty list if none.
redirect_countintegerNumber of entries in redirect_chain.
response_time_msintegerWall-clock milliseconds from the first request to the final response, including any redirect hops chased.
content_typestringContent-Type response header. Null if the response omits it.
content_lengthintegerContent-Length response header, in bytes. Null if the response omits it.
serverstringServer response header. Null if the response omits it.
is_brokenbooleanTrue if final_status_code is 400 or higher, or a connection/DNS/TLS/timeout error occurred.
error_classstringOne of timeout, dns_error, connection_error, ssl_error, too_many_redirects. Null when an HTTP response was received.
security_headersobjectstrict_transport_security, x_frame_options, content_security_policy — each nullable, passed through verbatim from the…

Example output

{
"input_url": "http://example.com",
"final_url": "https://example.com/",
"final_status_code": 200,
"redirect_chain": [
{
"url": "http://example.com",
"status_code": 301
},
{
"url": "https://example.com",
"status_code": 301
}
],
"redirect_count": 2,
"response_time_ms": 342,
"content_type": "text/html; charset=UTF-8",
"content_length": 1256,
"server": "ECS (nyb/1D2A)",
"is_broken": false,
"error_class": null,
"security_headers": {
"strict_transport_security": "max-age=31536000",
"x_frame_options": null,
"content_security_policy": null
}
}

💰 Pricing

Pay-Per-Event — you pay only when these events fire:

EventUSDWhat it is
actor-start$0.2One-off warm-up charge per run
url-checked$0.002Per URL checked, broken or not — a recorded failure is still a delivered result

Example: 1 000 URLs checked at the rates above ≈ $2.20. No subscription, no minimum, no card to start — Apify gives every new account $5 of free credit.

🚧 Limitations

  • No response-body parsing — we report status, redirects, timing, and headers, never the page content itself. Point a different tool at the ones that come back healthy if you need HTML extraction.
  • No auth, session, or cookie-jar support — this checks the public, unauthenticated view of a URL. Gated or login-only pages will report whatever an anonymous request sees.
  • No historical diffing across runs — every run is a stateless snapshot. Schedule the Actor and compare exports yourself if you want a change-over-time view.
  • maxUrls is a hard cap (default 750, max 10 000) — an oversized list fails input validation for free rather than getting silently truncated. Split large lists across runs.
  • One attempt per URL, by design — we report what a real visitor sees right now, not a status we retried our way past. A transient 429, 503, or timeout is the deliverable here, not noise to smooth over, so it shows up in your dataset instead of getting hidden behind a retry loop.

❓ FAQ

Does this download the page content?

No. Every URL is HEAD-first, and on the rare server that mishandles HEAD (405/501) we fall back to a GET — but even then we read only the headers and abort the transfer. We never buffer or store a response body.

Will a broken link fail my whole run?

No. Every URL is checked in isolation. A DNS failure, timeout, or 500 on one URL never stops the others — the run always finishes SUCCEEDED with exactly one row per URL, broken or not.

Do you retry rate-limited or 5xx responses?

No, by design. See Limitations below — a single-attempt result is the point of this Actor, not a gap in it.

What counts as "broken"?

Any final status code of 400 or higher, or a connection, DNS, TLS, or timeout error that meant no status code came back at all. Either way you get is_broken: true and, for transport failures, a specific error_class.

💬 Your feedback

Spotted a bug, hit a weird edge case, or need a new field? Open an issue on the Actor's Issues tab on Apify Console — we ship fixes weekly and we read every report.