Bulk URL Status & Redirect Checker — Link Route Audit avatar

Bulk URL Status & Redirect Checker — Link Route Audit

Pricing

from $0.90 / 1,000 http url results

Go to Apify Store
Bulk URL Status & Redirect Checker — Link Route Audit

Bulk URL Status & Redirect Checker — Link Route Audit

Find broken links and check redirects before or after a site migration or SEO review. Get the final HTTP status, each redirect hop with its code and the final URL for up to 100 URLs per run, then export a table.

Pricing

from $0.90 / 1,000 http url results

Rating

0.0

(0)

Developer

Cliqto Media

Cliqto Media

Maintained by Community

Actor stats

0

Bookmarked

2

Total users

1

Monthly active users

2 days ago

Last modified

Share

Find broken links and check redirects before or after a site migration. Bulk URL status checker for a list of up to 100 public URLs: for each URL it returns the final HTTP status code, the redirect chain with the status of each hop, the final URL, a broken-link flag and the content type, size and server of the answer. No login or API key is needed; it costs USD 0.00225 per checked URL on the Free plan and down to USD 0.0009 on paid Apify plans, with platform usage included. Use it before a site migration, after a release or for an SEO link review; the smallest run checks one URL in a few seconds.

Link Route Audit: a URL list goes through redirect checks and becomes result rows with status code, redirect and broken-link flags

Table of contents

What it does

You give the Actor a list of URLs. It checks each URL like a browser would ask a server for it, follows the redirects and saves one result row per unique URL. The row tells you the final HTTP status code (for example 200, 301 → 200, 404 or 503), every redirect destination in order, the final URL and whether the link is broken.

It does not crawl a site, find new URLs, render JavaScript, sign in or read page text. A 200 status shows that the server answered with success; it does not prove that the page shows the right content.

Good fit

  • Site migration check: old URLs → are they redirected, and where do they end?
  • Broken link check: links from a report or a spreadsheet → which ones return 404, 5xx or no answer?
  • Redirect audit: known redirects → how many hops, loops, or a missing final page?

👉 Try it now: open the input form

How it works

  1. It reads your URL list, removes spaces and duplicates, and rejects items that are not full public http:// or https:// URLs.
  2. It sends a light request (HEAD, or GET without downloading the page when a server rejects HEAD; you can also choose GET only) and follows up to 10 redirects (you can set 0–10). Each new address is checked again for safety. Network errors, 429 and 5xx answers get up to 2 retries.
  3. It saves one row per unique URL in the Dataset and a run summary in the Key-value store.

👉 Try it now: open the input form

Quick start

Input form: box 1 marks the two prefilled URLs to check, box 2 the Plain URL list for your own URLs, box 3 the URLs checked at once setting and the Request options

  1. Open the Input tab. URLs to check (startUrls) already holds http://apify.com and https://apify.com/store (box 1 in the picture).

  2. Keep them, or check your own list: clear startUrls and paste your URLs into Plain URL list (urls), one per line with Bulk edit (box 2).

  3. Keep the default settings: 20 URLs at once, up to 10 redirects, HEAD then GET, no proxy, 10-second request timeout (box 3).

  4. Click Start below the form.

  5. Open Output → URL results and look at statusCode, isBroken, isRedirect and finalUrl (orange boxes in the picture below; ✕ = false, ✓ = true).

    Output table: boxes on the status code, broken-link flag, redirect flag and final URL of two prefill rows

Expected result: two rows in about 5 seconds. http://apify.com shows isRedirect: true and finalUrl https://apify.com/; https://apify.com/store shows status 200 without a redirect.

👉 Try it now: open the input form

Use cases

Buyer jobInput pathOutput entityKey fieldsNext integration
Check redirects after a site migrationstartUrls with old URLsOne row per URLstatusCode, redirectChain, finalUrlCompare finalUrl with your redirect map in a spreadsheet
Find broken links on a website or in a link listurls pasted from a link reportOne row per URLisBroken, errorType, statusCodeFilter isBroken = true and fix the links
Audit redirect chains for SEOstartUrls with known redirectsOne row per URLisRedirect, redirectChain, redirectStatusCodesShorten chains with more than one hop
Check key pages after a releasestartUrls with key public pagesOne row per URLstatusCode, responseTime, checkedAtKeep the dated export; re-check failed rows

👉 Try it now: open the input form

Input

Input fields

FieldPlain nameTypeRequiredDefault / prefillAllowed valuesEffectExampleRecommendation
startUrlsURLs to checkArray of {"url": "..."} objectsYes, or urlsNo default; form prefill http://apify.com, https://apify.com/storeUp to 100 items together with urls, each URL up to 4,096 characters, http or httpsThe list to check[{"url": "https://apify.com/store"}]Use full public URLs. Split longer lists into runs of 100
urlsPlain URL listArray of stringsYes, or startUrlsEmptyUp to 100 together with startUrls; blank lines skippedThe same check for a pasted plain list, after startUrls["https://apify.com/store"]Paste a column from a spreadsheet with Bulk edit
maxConcurrencyURLs checked at onceIntegerNo201–100How many URLs are checked at the same time5Use 2–5 for one slow or strict site
maxRedirectsMax redirects to followIntegerNo100–10A longer chain is broken (TOO_MANY_REDIRECTS); 0 returns the first answer, for example a 301, without following it0Use 1–3 to flag long chains for SEO
httpMethodRequest methodStringNoHEAD_THEN_GETHEAD_THEN_GET, GETHEAD first, GET again when HEAD gets 400, 403, 404, 405 or 501; or GET only. The page body is never downloadedGETUse GET for a site that answers HEAD badly
userAgentUser-AgentStringNoEmpty (the Actor's own User-Agent)Printable ASCII, up to 256 charactersUser-Agent header of every requestMyCompany-LinkAudit/1.0Set it when a site owner asks you to identify your checks
proxyConfigurationConnection modeObjectNo{"useApifyProxy": false} (direct)useApifyProxy, optional groups, countryCodeSends requests through Apify Proxy{"useApifyProxy": true}Use only when a site blocks direct requests or you need a country
requestTimeoutSecsRequest timeout (seconds)IntegerNo101–30Longest wait for each step of one request15Raise for slow servers; one URL still stops after 40 seconds in total

The list size is counted before duplicates are removed. Duplicates are checked once and counted in the summary. Invalid items are skipped and listed in failedInputs in the summary. If no valid URL is left, the run fails with INVALID_INPUT. In failedInputs, items of urls are numbered after the startUrls items. URL passwords, secret query values, custom proxy URLs, other headers and cookies are not accepted.

Interacting settings

WhenInput AInput BEffective behaviorExampleCost/result effect
A server is slow or does not answerrequestTimeoutSecsretries (fixed, 2)Up to 3 tries per request; one URL stops after 40 seconds in total30-second timeout on a dead siteRow with status 0 and Check time limit reached
Many URLs on one sitemaxConcurrencythe site's rate limitsMore requests at once can get 429 answers and retries100 URLs of one shop at 20 at onceSlower run; check rateLimits in the summary
Proxy is onproxyConfigurationredirects and retriesEvery request and hop goes through the proxyRedirect chain with 3 hopsProxy traffic is small (no page bodies)
HEAD gets 404 but the page existshttpMethod HEAD_THEN_GETthe serverThe Actor repeats the request with GET; usedGetFallback: trueShop that blocks HEAD with 403One extra request, same price per row
Both lists are filledstartUrlsurlsBoth are checked; one URL in both lists is checked once40 + 60 URLsUp to 100 URLs together

Input examples

Migration check of a few old URLs

{
"startUrls": [
{ "url": "http://apify.com" },
{ "url": "https://apify.com/store" },
{ "url": "https://apify.com/old-page" }
]
}

Expected behavior: three checks with the default settings. Expected output: http://apify.com → statusCode 200, isRedirect: true, finalUrl https://apify.com/; https://apify.com/store → 200; https://apify.com/old-page → 404, isBroken: true. Limit or cost note: three useful results. Site answers can change over time.

Plain list, first answer only

{
"urls": ["http://apify.com", "https://apify.com/store"],
"maxRedirects": 0,
"httpMethod": "GET"
}

Expected behavior: one GET request per URL, redirects are not followed. Expected output: http://apify.com → statusCode 301, isRedirect: false, isBroken: false, finalUrl http://apify.com; https://apify.com/store → 200. Limit or cost note: two useful results.

Gentle check of one strict site

{
"startUrls": [
{ "url": "https://apify.com/store" },
{ "url": "https://apify.com/pricing" }
],
"maxConcurrency": 2,
"requestTimeoutSecs": 20
}

Expected behavior: at most two requests at once and a longer wait per request. Expected output: one row per URL with statusCode and responseTime. Limit or cost note: same price per result; a slower run uses more time.

👉 Try it now: open the input form

Output

Output fields

FieldMeaningTypeCan be empty?Example / enumSource / notes
urlThe URL you gaveStringNohttp://apify.comSpaces removed
statusCodeFinal HTTP status codeIntegerNo200, 404, 503; 0 = no HTTP answerLast response in the chain
statusMessageStatus text or reasonStringNoOK, Not Found, Network request failed, Request timeout, Check time limit reached, Redirect loop detectedSafe text; no raw error details
isBrokenThe link needs attentionBooleanNotrue for status 0, 400+, loops, too many hops, blocked targets
errorTypeWhy the link is broken, as a stable codeString or nullYes, null when isBroken is falseHTTP_ERROR, TIMEOUT, CHECK_TIME_LIMIT, NETWORK_ERROR, BLOCKED_URL, REDIRECT_LOOP, TOO_MANY_REDIRECTS, REDIRECT_WITHOUT_LOCATION, CANCELLED, NO_RESPONSEUse it for filters and scripts; statusMessage stays human text
isRedirectAt least one redirect was seenBooleanNotrue
redirectChainRedirect destinations in order, the last one includedArray of stringsYes, [] without redirects["https://apify.com/"]The input URL is not repeated
finalUrlWhere the route endsString or nullYes, null when there is no HTTP answerhttps://apify.com/
responseTimeTime for the whole check in millisecondsIntegerNo99Includes hops and retries; not a page speed score
checkedAtWhen the check startedString, UTC ISO timeNo2026-10-26T13:45:31.564Z
redirectCountNumber of redirects followedIntegerNo1; 0 without redirectsUsually the length of redirectChain; a redirect answer without a Location header is counted but adds no destination
redirectStatusCodesStatus of each redirect hopArray of integersYes, [] without redirects[301], [301, 302]Same order as redirectChain, one code per redirect answer
contentTypeContent type of the last answerString or nullYes, null when missing or no answertext/html; charset=utf-8From the Content-Type header
contentLengthSize of the last answer in bytesInteger or nullYes, null when the server does not send it5120From the Content-Length header; many servers leave it out
serverServer software of the last answerString or nullYes, null when missing or no answernginx, cloudflareFrom the Server header
requestMethodMethod of the request that gave the final answerString or nullYes, null when no answerHEAD, GET
usedGetFallbackHEAD was rejected and GET gave the answerBooleanNofalseShows servers that answer HEAD differently

Output entities and surfaces

SituationDataset/API/KVS/live viewResult shapeUser action
Normal itemDataset URL results (table, JSON, API)One row per unique URL, all 17 fieldsFilter by isBroken, statusCode or isRedirect
Domain-empty resultNot applicableEvery valid URL gets a row—
Partial resultDataset + RUN_SUMMARY / OUTPUT in the Key-value storeRows for checked URLs; unprocessed and failedInputs in the summaryRead the summary, re-run the rest
Technical failure or retryDataset row with statusCode: 0; summary counters attempts, retries, rateLimitsfinalUrl: null, reason in statusMessageRe-check a small set later, lower maxConcurrency or try the proxy

RUN_SUMMARY holds counts (received, valid, duplicates, processed, technicalFailures, unprocessed, attempts, retries, rateLimits), outcome (succeeded, partial or error), timings and failedInputs. OUTPUT holds the rows, the same summary and a link to the Dataset. A 404 result still counts as a successful check: the server gave a clear answer.

In the run's Dataset you get four tabs: URL results (all 17 fields), Broken links (only what you need to fix a dead link), Redirect routes (original URL, each hop with its code, final address) and the standard Apify All fields tab with the raw rows.

Output example

Real row from run Du4rpX5L5plrQxRgX on 2026-10-27 with the prefilled input:

{
"url": "http://apify.com",
"statusCode": 200,
"statusMessage": "OK",
"isBroken": false,
"errorType": null,
"isRedirect": true,
"redirectChain": ["https://apify.com/"],
"finalUrl": "https://apify.com/",
"responseTime": 118,
"checkedAt": "2026-10-27T04:46:28.501Z",
"requestMethod": "HEAD",
"usedGetFallback": false,
"redirectCount": 1,
"redirectStatusCodes": [301],
"contentType": "text/html; charset=utf-8",
"contentLength": null,
"server": null
}

This is the full row for one URL. For a dead site the same row has statusCode: 0, finalUrl: null, errorType: "NETWORK_ERROR" and a reason such as Network request failed. Times differ from run to run.

Use case → fields

Buyer jobInput pathOutput entityKey fieldsNext integration
Find broken linksstartUrls or urlsURL rowisBroken, errorType, statusCodeSpreadsheet filter or ticket list
Check migration redirectsstartUrlsURL rowredirectChain, redirectStatusCodes, finalUrlCompare with the redirect map; 301 vs 302 matters for SEO
Watch key pagesstartUrlsURL rowstatusCode, checkedAtKeep dated exports

👉 Try it now: open the input form

Pricing

Pricing unit

This Actor uses pay per event. You pay only for these events; Apify platform usage is included in the price.

EventWhen it is chargedFree planBronzeSilverGold and above
Actor startOnce per run; the start fee scales with run memory, one event per GB (default 128 MB = one event)USD 0.001USD 0.001USD 0.001USD 0.001
HTTP URL resultA stored row with an HTTP status 100–599, including 404 and 503USD 0.00225USD 0.0018USD 0.00135USD 0.0009
Failed URL checkA stored row where the site did not answer: network error or timeout after all retriesUSD 0.001USD 0.001USD 0.001USD 0.001

Duplicates, invalid inputs, blocked private addresses, cancelled checks and the summary record are free. If every input is invalid, the run fails with a clear message and only the Actor start (USD 0.001) is charged.

Cost examples

RunFree planGold plan
Prefill: 2 URLs that answerUSD 0.0055USD 0.0028
10 URLs that answerUSD 0.0235USD 0.010
100 URLs that answerUSD 0.226USD 0.091
100 URLs, 10 of them do not answerUSD 0.2135USD 0.092

Set Maximum cost per run in the run options. The Actor checks only as many URLs as this limit can pay for; the rest is listed as unprocessed in the summary. The smallest accepted limit is USD 0.00325.

👉 Try it now: open the input form

Limits

  • Up to 100 URLs per run, counted before duplicates are removed. Split longer lists.
  • Only public http and https addresses. Private network, local and unsafe addresses are blocked, also inside redirects.
  • Up to 10 redirect hops (set with maxRedirects); a loop or a longer chain gives a broken row.
  • One URL stops after 40 seconds in total, with retries and hops.
  • A server that sends response headers larger than 16 KB gives a statusCode: 0 row for that URL only; the other URLs are checked as usual.
  • No crawling, sitemap import, JavaScript rendering, login, custom headers (except userAgent) or cookies.
  • No page content checks: a "soft 404" page with status 200 is not detected.
  • Results are saved together at the end of the run. A hard stop before that can lose the rows of that run.
  • 100 URLs that all do not answer take about 3 minutes; the default run timeout is 5 minutes. With a shorter run timeout, new checks stop 50 seconds before it, so the rows are still saved; the rest is listed as unprocessed.
  • A status 0, 403 or 429 shows what this run saw; it does not prove the page does not exist.

👉 Try it now: open the input form

API, export and integrations

  • API: start a run with POST https://api.apify.com/v2/acts/cliqtomedia~bulk-url-status-redirect-checker/runs and the JSON input; read rows from the run's default Dataset. Send your Apify token in the Authorization: Bearer header, never in the URL.
  • Export: open Output → URL results → Export and choose JSON, CSV or Excel. JSON keeps arrays and null values as they are.
  • Scheduling: save the input as a task in Apify Console and add a schedule for it to re-check the same list.
  • Webhooks / no-code: Apify integrations can send the finished Dataset to your next tool; use isBroken and statusCode as the filter.

Use with AI agents (MCP)

  • MCP server URL with only this Actor: https://mcp.apify.com?tools=cliqtomedia/bulk-url-status-redirect-checker.
  • Smallest useful input:
{ "startUrls": [{ "url": "http://apify.com" }, { "url": "https://apify.com/store" }] }
  • Key output fields: statusCode (final HTTP code, 0 = no answer), isBroken (needs attention), errorType (stable reason code), isRedirect, redirectChain (destinations in order), finalUrl (where the route ends, or null).
  • Typical run: 2 URLs in about 5 seconds for USD 0.0055 at the Free tier. Limits: 100 URLs per run, public addresses only, 40 seconds per URL at most.

👉 Try it now: open the input form

Troubleshooting

The run fails at once

Symptom: the run ends as FAILED with INVALID_INPUT. Likely reason: the list is empty, has more than 100 items, or has no valid URL. Check: RUN_SUMMARY → error.message and the startUrls shape ({"url": "..."}), or use urls for plain strings. Action: fix the list and start again.

Many rows with status 0

Symptom: statusCode: 0, finalUrl: null, statusMessage Network request failed or Request timeout. Likely reason: the domain does not exist, the server is down, TLS fails, or the site drops automated requests. Check: open one of the URLs in your browser. Action: re-check a small set later, raise requestTimeoutSecs, or turn on Apify Proxy.

Many 403 answers from big sites

Symptom: popular sites return statusCode 403 with errorType: "HTTP_ERROR", even after the GET retry (usedGetFallback: true). Likely reason: the site's bot protection refuses automated requests; in a test of 100 popular sites 16 did so. The row is still charged as an HTTP result, because the server answered. Check: open the URL in your browser; a page that opens there exists. Action: set your own userAgent, or turn on Apify Proxy, and re-check only those URLs.

Many 429 answers or a slow run

Symptom: rateLimits and retries in the summary are high. Likely reason: the site limits how fast it answers. Check: are most URLs on one site? Action: lower maxConcurrency to 2–5.

Fewer rows than URLs

Symptom: the Dataset has fewer rows than your list. Likely reason: duplicates, invalid items, or the cost limit stopped the list. Check: duplicates, failedInputs and unprocessed in RUN_SUMMARY. Action: fix the items, or raise Maximum cost per run.

👉 Try it now: open the input form

FAQ

Does it show the status code of every redirect hop?

Yes. redirectStatusCodes holds the code of each hop (for example [301, 302]) in the same order as the destinations in redirectChain; statusCode is the final code.

Can I see a redirect without following it?

Yes. Set maxRedirects to 0: each row shows the first answer, for example statusCode 301, and it is not marked broken. isRedirect stays false and redirectChain stays empty, because no redirect was followed; read the 3xx statusCode instead.

No. A redirect that ends at a 200 page has isRedirect: true and isBroken: false. A redirect that ends at 404 is broken.

Does it find soft 404 pages?

No. It does not read the page, so a page that says "not found" with status 200 looks fine.

Can it check pages behind a login or on my local network?

No. It does not sign in, and private or local addresses are blocked for safety.

Is responseTime a page speed score?

No. It is the time of the whole check from the Apify server, with hops and retries.

Does it compare results between runs?

No. Export the rows with checkedAt and compare them in your own sheet.

👉 Try it now: open the input form

👉 Try it now: open the input form

Support

For a bug, question or feature request, open the Actor's Issues tab on Apify. Include the run ID, the number of URLs and the connection mode. Never send tokens, cookies, passwords or a private URL list.

👉 Try it now: open the input form