Website Broken Link Checker
Pricing
from $0.43 / 1,000 link checkeds
Website Broken Link Checker
Finds the broken links on the pages you name. Paste URLs or bare domains and press Start: every link found is followed to its final HTTP status, one row per link, with the verdict, anchor text, source page, redirect count and timing. No login, no browser.
Pricing
from $0.43 / 1,000 link checkeds
Rating
0.0
(0)
Developer
Pradio Actors
Maintained by CommunityActor stats
0
Bookmarked
2
Total users
1
Monthly active users
a day ago
Last modified
Categories
Share
What does Website Broken Link Checker do?
Website Broken Link Checker reads the pages you name and returns one row per link found on them: the checked URL, its final HTTP status, an ok, broken or error verdict, the anchor text and the page it appeared on. Paste URLs or bare domains, press Start, and each page is fetched once, its links collected and every one checked end to end. Sixteen fields land on each data row. It checks the links on the pages you list; it is not a site crawler and it does not fix anything.
Each checked link costs $0.0008, billed only after its row is in your dataset. In a measurement on 40 sites it had never seen, 35 answered with checked links, a hit rate of 87.5% (3 returned no checkable links, 2 errored). A run that finds no links returns one uncharged row that says so.
Who uses Website Broken Link Checker
| Buyer | What they run it for |
|---|---|
| Website owners | Finding dead links on their own pages before visitors or crawlers do. |
| SEO practitioners | Finding dead links that leak ranking; each row names the page and the anchor to fix. |
| Teams after a migration or redesign | Checking that old URLs redirect somewhere instead of dying. |
| Content maintainers | Auditing outbound links that rot quietly over time. |
Features
- Checks every link on the pages you name. Paste full URLs or bare domains; each page is fetched once and every anchor on it is checked.
- HEAD first, GET when refused. A cheap HEAD request leads; a server that answers 405 or 501 is asked again with GET.
- Redirects followed and counted. Up to 10 redirects are followed per link, so
final_urlandredirect_countare read, not assumed. - Three honest verdicts.
okbelow HTTP 400,brokenat 400 or above,errorwhen a link never answers within 15 seconds. - Checked once across pages. A footer link repeated across start pages is checked once;
all_sourceslists every place it appeared. - Unusable entries answered, not dropped. An entry in
queriesthat is not a fetchable URL gets its own free row withstatusbad_urland the reason. - Plain HTTP, no browser, no login. Up to 8 links are checked at once, with a 15 second limit per request.
- Caps you control.
maxResultsPerQuerylimits the links checked per page;maxItemslimits the whole run, 100 by default.
What you can count on
- You pay only for rows the run judged; a row it could not judge is pushed as an uncharged
ITEM_STATUSrow with the reason on it. - Every row is charged only after it is written to your dataset; a row you cannot see is never billed.
- A run that finds nothing returns one
NO_RESULTSrow that says so, never an empty dataset, and it is not charged. - A spending limit stops the run cleanly: one
STOPPED_EARLYrow reports how many rows were returned and how many were not. - Every run writes a
RUN_SUMMARYentry withrowsFetched,rowsPushed,rowsChargedandduplicatesDropped, so a short run and a broken one are told apart. - If a start page cannot be read or changes what it answers, the run fails with the error in the log; it never returns rows full of nulls and calls it success.
- No value is invented: a field the checked page does not show is null, and the output table says which.
Why this one
- This Actor bills $0.0008 per checked link; the most-used alternative on this platform bills $0.001, measured on 2026-09-16.
- Run on the same pages that day, the alternative left its redirect-chain field empty on 12 of its 19 rows and its error field on 16 of 19. Here the redirect count is on every row, and a link that never answers carries the reason in
status_text. - The hit rate above is measured on sites this Actor had never seen, not on a best case.
- Every row names the page the link was found on, so a dead link is a fix, not a riddle.
What data does Website Broken Link Checker return?
One real row from a run over the example pages in the default input:
{"url": "https://crawler-test.com/links/not_found/foo1","final_url": "https://crawler-test.com/links/not_found/foo1","status": 404,"status_text": "Not Found","classification": "broken","is_broken": true,"source_domain": "crawler-test.com","source_url": "https://crawler-test.com/links/broken_links_internal","anchor_text": "Broken Internal Link 1","element": "a","all_sources": [{"source_url": "https://crawler-test.com/links/broken_links_internal","anchor_text": "Broken Internal Link 1","element": "a"}],"method": "HEAD","redirect_count": 0,"duration_ms": 291,"checked_at": "2026-09-18T06:29:01.047Z","row_type": "ROW"}
Each row is one link checked end to end. Every field below sits on every data row; a value a check cannot produce is null, never absent.
| Field | What it is |
|---|---|
url | The link as it was found, made absolute. This is the row's key: one row per unique link URL. |
final_url | Where the link ended after its redirects; the same as url when it never redirected, null when it never answered. |
status | Two shapes by design, documented in the table below: an integer HTTP status on a checked-link row, or the miss word bad_url on an ITEM_STATUS row. |
status_text | The server's status line, like Not Found, or the failure reason when nothing answered. |
classification | The verdict on the row: ok, broken or error. |
is_broken | true on a confirmed 4xx or 5xx, false on a healthy answer, null when unproven. |
source_url | The start page this link was first found on. |
anchor_text | The clickable text of the link on that page. |
element | The HTML element the link came from; a here, since only anchor tags are checked. |
all_sources | Every page and anchor text the same link appeared on, when more than one page carries it. |
method | HEAD or GET: how the check was made. |
redirect_count | How many redirects were followed before the final answer; a URL answered directly shows none. |
duration_ms | How long the check took in milliseconds, redirects included. |
checked_at | The ISO timestamp of the check. |
source_domain | The host of the start page the link was found on (one of the pages you named); the link's own host is in url. |
row_type | ROW on every checked link; the status kinds below mark the run's own messages. |
The Overview tab shows the per-link fields; the All fields tab adds duration_ms, checked_at and source_domain. Across the captured runs (12 rows locally, 1,000 on the platform, 2026-09-18), no field was empty on every row.
status carries two shapes by design:
status value | On which rows | What it means |
|---|---|---|
An integer HTTP code, like 200 or 404 | ROW | The status the link's final address answered with; null when no response came back. |
bad_url | ITEM_STATUS | The queries entry was not a usable URL. The entry is named in url, the reason sits in reason, and the row is pushed free: a malformed entry is answered, never dropped. |
row_type says what a row is. ROW is a checked link. ITEM_STATUS is a per-item miss the schema declares free: pushed so you see it, never billed. NO_RESULTS means no page produced a checkable link. STOPPED_EARLY means your spending limit ended the run. Status rows also carry the run's bookkeeping:
| Field | What it is |
|---|---|
reason | Why the run ended or the item missed; on status rows only. |
rowsFetched | Links read before the run ended; on status rows only. |
rowsReturned | Rows returned before the spending limit stopped the run; on STOPPED_EARLY. |
rowsRemaining | Rows not returned when the spending limit stopped the run; on STOPPED_EARLY. |
Pricing
You pay $0.0008 per checked-link row, the result-returned event, charged only after the row is written. Apify also bills its own apify-actor-start event once per run, $0.00005 at this Actor's memory size. Status rows, bad_url miss rows and dropped duplicate links are free. Every checked link is billed, including broken and error verdicts: the verdict is the answer you paid for.
| Rows returned | Link charges | Start event | Total |
|---|---|---|---|
| 100 | $0.08 | $0.00005 | about $0.08 |
| 1,000 | $0.80 | $0.00005 | about $0.80 |
| 10,000 | $8.00 | $0.00005 | about $8.00 |
A start page carries more than one link. The 35 pages that answered in the measurement produced 3,504 link rows in total, about 100 per page, so budget by sweep:
- 100 start pages: about 88 answer, about 8,800 links checked, about $7.04 plus the start event.
- 1,000 start pages: about 875 answer, about 87,600 links checked, about $70.08 plus the start event.
Your pages will differ, and maxItems caps a run at 100 rows unless you raise it.
Duplicates are billed once, not once per sighting. If 50 link sightings across your pages deduplicate to 47 unique URLs, you pay for 47 checks; the repeats join all_sources free.
How do I use Website Broken Link Checker?
- Open the Actor and press Try for free.
- Paste your start pages into Queries, one URL or bare domain per line. The input carries three example pages from a public test site; replace them with yours.
- Optionally set Max Results Per Query and Maximum items.
- Press Start. Rows land in the dataset as links are checked, and the run ends when every page is read.
Example input:
{"queries": ["https://crawler-test.com/links/broken_links_internal", "https://crawler-test.com/links/broken_links_external", "https://example.com/"],"maxItems": 100}
Or start a run over the API:
curl -X POST "https://api.apify.com/v2/acts/Pradio~broken-link/runs?token=YOUR_APIFY_TOKEN" -H "Content-Type: application/json" -d "{\"queries\":[\"https://example.com/\"]}"
Input
| Input | Default | What it does |
|---|---|---|
queries | three example pages | The start pages to check, one URL or bare domain per line. An entry that is not a fetchable URL gets its own free bad_url row with the reason, so a typo is answered, never dropped. |
maxResultsPerQuery | none | The most links checked from one start page. Unset means every link found is checked. |
maxItems | 100 | The most link rows one run returns in total. Rows past the cap are dropped, and the run summary shows how many links were checked. |
queries
Each entry is one start page: a full URL like https://example.com/blog, or a bare domain like example.com, which is fetched over HTTPS. The page is fetched once, its anchors are collected and each unique link is checked. Only the pages you list are read; the Actor checks the links on them and does not crawl further. An entry nothing can be fetched for, a typo or an unrecognisable line, is answered on its own uncharged row: status bad_url, the entry in url, the reason in reason.
Output
Rows land in the default dataset as links are checked. Four extras tell you how a run went:
ITEM_STATUS: aqueriesentry nothing could be fetched for, named inurlwithstatusbad_urland the reason inreason. Pushed so you see it, never billed.NO_RESULTS: no start page produced a checkable link. One uncharged row carriesreasonandrowsFetched, so an empty answer is an answer, not silence.STOPPED_EARLY: your spending limit ended the run. One uncharged row carriesrowsReturnedandrowsRemaining; raise the limit and re-run for the rest.RUN_SUMMARY: an entry in the run's key-value store carrying the counts: links fetched, rows pushed, rows charged, duplicates dropped, stopped early or not.
A run where every start page fails pushes nothing and fails with the reason in the log. No rows are billed, because none were written.
What can you do with the data?
Sweep your own site before a launch. Run the checker over the pages, filter classification to broken, and hand the list to whoever fixes it. Each row already carries the dead URL, the page it sits on and the anchor text to search for.
Audit a migration. Old URLs that still redirect show how many redirects they took and where they landed in redirect_count and final_url. The ones that answer 404 instead are the ones to remap.
Watch outbound links on a schedule. Reference, partner and affiliate links rot quietly. Put the run on an Apify schedule, export the broken rows to a sheet, and the dataset becomes a monthly fix list.
Qualify a site you are evaluating. A page full of dead links says something about how it is maintained. One run counts them without a manual click-through.
Use Website Broken Link Checker with AI agents
Paste this line to give an agent this Actor through Apify's MCP server:
claude mcp add --transport http apify "https://mcp.apify.com?tools=Pradio/broken-link"
Personal data
- A row carries only the declared link-health fields: the checked URL, its HTTP status and verdict, the anchor text and the page it appeared on. No field names or identifies a person.
- A row keeps facts and the link's own anchor text, never the body or a substantial part of a page.
- Every row links back to the page it was found on, so what was collected is easy to check.
- A page that refuses the read is reported as an
errorrow, and if no start page answers the run fails; nothing retries past a block or works around a refusal. - Personal data is not what a row is about, but it can appear incidentally inside anchor text or a URL path (a name in a link label, a profile slug in a path). Run it against sites you operate or are permitted to check.
- You choose the pages, so you are the controller for the list you supply. To exclude a URL, remove it from the input; the Actor reads only what you list.
Release notes
0.1.13(2026-09-18): an unusablequeriesentry now gets its own freebad_urlrow instead of being dropped; the redirect field isredirect_count.0.1.12(2026-09-17): the Store icon redrawn.0.1.11(2026-09-17): the sample row on this page re-synced to a fresh capture.0.1.10(2026-09-17): a link first seen without anchor text picks it up when a later page shows it.0.1.9(2026-09-17):source_urlandanchor_textnow always describe the same sighting.0.1.8(2026-09-17):RUN_SUMMARYis now an entry in the run's key-value store.0.1.7(2026-09-17):all_sourcesentries carry the element name.0.1.6(2026-09-17):all_sourcesentries use snake_case keys.
Limits
- It checks the links on the pages you name and reports them. It is not a crawler: nothing past the listed pages is read. And it does not fix links: repair stays with you.
- Only anchor tags are checked.
javascript:,mailto:,tel:,data:,sms:andftp:links are skipped, and so are images, scripts and stylesheets. - Links a page builds with JavaScript after load are not seen: pages are fetched over plain HTTP, with no browser.
- Requests are logged-out and public, with a fixed declared user agent. There is no proxy rotation and no retry past a block: a site that refuses is an
errorrow, not a workaround. - A link that never answers within 15 seconds is an
errorrow, suspected but not proven broken. - A redirect chain is followed for at most 10 redirects; a longer chain returns the last redirect's status.
- Every checked link is a billed row, including
errorverdicts. A run of nothing but unanswered links still bills its rows, because the check itself is the answer.
Troubleshooting
The run returned fewer rows than maxItems.
maxItems is a ceiling, not a target. Fewer rows means the pages ran out of links first; rowsFetched in RUN_SUMMARY shows how many links were checked.
The dataset has one row saying NO_RESULTS.
No start page produced a checkable link. That is the answer to this run, not a bug, and the row is free.
A row says bad_url in status.
One queries entry was not a URL the Actor could fetch, a typo or an unrecognisable line. The row names the entry in url, gives the reason in reason, and is free. The other entries ran normally.
A row says error with is_broken empty.
The link never answered within 15 seconds. It is suspected, not proven dead; re-run later to retry it.
The run failed with "no start page could be read". Every entry failed at the network level: nothing was pushed and no rows were billed. Check the URLs you pasted.
FAQ
Can I use integrations with Website Broken Link Checker? Yes. The dataset plugs into Apify integrations like Zapier, Make and webhooks, and Apify schedules can re-run the sweep weekly or monthly without you touching it.
Can I use Website Broken Link Checker with the Apify API? Yes. Start runs and read the dataset back over the API; the curl example above is the whole call.
Can I use Website Broken Link Checker through an MCP server? Yes. The line under "Use Website Broken Link Checker with AI agents" adds it to an agent through Apify's MCP server.
Is it legal to check links this way? The Actor makes the same plain HTTP requests any monitoring tool makes: it bypasses no login and keeps only link facts. You choose the pages, so point it at sites you are responsible for or allowed to test.
Something look wrong? Report it on this Actor's Issues tab in the Apify Console and it will be looked at.
Not affiliated
Website Broken Link Checker is an independent tool, not affiliated with or endorsed by any site you point it at. The example pages in the default input belong to crawler-test.com, a public test site.