# Bulk URL Status & Broken Link Checker (`nuhemugames/url-status-checker`) Actor

Check thousands of URLs in one run: HTTP status, full redirect chain, final URL, latency and a clear ok/broken verdict with an error category (404, timeout, DNS, SSL, redirect loop). HEAD-first with GET fallback, retries on flaky errors. Checks only the URLs you provide - no crawling.

- **URL**: https://apify.com/nuhemugames/url-status-checker.md
- **Developed by:** [kuon](https://apify.com/nuhemugames) (community)
- **Categories:** SEO tools, Developer tools
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $2.00 / 1,000 results

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.

Learn more: https://docs.apify.com/platform/actors/running/actors-in-store#pay-per-event

## What's an Apify Actor?

Actors are web data automations that power AI and operations. They run on the Apify platform to scrape websites, process data, connect APIs, and automate workflows.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.
Actors are written with capital "A".

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.
The best way to integrate Actors is as follows.

- **AI agents and MCP clients** — the [Apify MCP server](https://docs.apify.com/integrations/mcp.md) at `https://mcp.apify.com` (remote, streamable HTTP, OAuth on first use).
- **Agentic workflows and local Actor development** — [Agent Skills](https://apify.com/.well-known/agent-skills/index.json) with the [Apify CLI](https://docs.apify.com/cli/docs.md): `npm install -g apify-cli`, then `apify login`.
- **JavaScript/TypeScript projects** — the official [JS/TS client](https://docs.apify.com/api/client/js/docs.md): `npm install apify-client`.
- **Python projects** — the official [Python client](https://docs.apify.com/api/client/python/docs.md): `pip install apify-client`.
- **Any other language** — the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).

# README

## Bulk URL Status & Broken Link Checker

Check thousands of URLs in one run and get a clear verdict for each: **ok or
broken, with the exact reason** — HTTP status, full redirect chain, final URL,
latency and a machine-readable error category (404, timeout, DNS, SSL,
redirect loop). Built to avoid false alarms: HEAD-first with GET verification,
automatic retries on flaky errors. **Checks only the URLs you provide — it
never crawls or follows links on pages.** No credentials needed.

### What it does

Give it a list of URLs (duplicates are removed). For each URL you get one
dataset item with:

- `result` / `ok` — `"ok"` (2xx after redirects) or `"broken"`, also as a boolean
- `httpStatus` — final status code (null when nothing responded)
- `finalUrl`, `redirectCount`, `redirectChain[]` — every hop with its status and Location
- `latencyMs` — total time including redirects
- `errorCategory` — `http_client_error`, `http_server_error`, `dns`, `timeout`,
  `ssl`, `connection`, `redirect_loop`, `too_many_redirects`, `invalid_url`
- `error` — human-readable reason (null when ok)
- `contentType`, `contentLengthBytes`, `method`, `attempts`, `checkedAt`

#### Accuracy features (why fewer false alarms)

- **HEAD-first, GET-verified**: many servers mishandle HEAD (405/403/404 on
  pages that load fine). A URL is only reported broken after a GET confirms it.
- **Retries**: network errors and 5xx are retried (configurable) before the
  verdict, so transient blips don't show up as broken links.
- **Bodies are never downloaded** — GET responses are closed after the headers,
  so large files check as fast as small pages.
- **Polite**: never more than 2 concurrent requests to the same host,
  no matter how high you set the overall concurrency.

### Input

| Field | Type | Default | Description |
|---|---|---|---|
| `urls` | array | — | URLs to check (only these are requested; no crawling) |
| `timeoutSecs` | integer | `15` | Per-request timeout; slower URLs are `timeout` |
| `retries` | integer | `1` | Retries on network errors and HTTP 5xx |
| `maxRedirects` | integer | `10` | Hop limit before `too_many_redirects` |
| `concurrency` | integer | `10` | Parallel checks (per-host always capped at 2) |

### Example output (abridged)

```json
{
    "url": "http://github.com",
    "result": "ok",
    "ok": true,
    "httpStatus": 200,
    "finalUrl": "https://github.com/",
    "redirectCount": 1,
    "redirectChain": [{"url": "http://github.com", "status": 301, "location": "https://github.com/"}],
    "latencyMs": 142,
    "contentType": "text/html; charset=utf-8",
    "method": "HEAD",
    "attempts": 1,
    "error": null,
    "errorCategory": null,
    "checkedAt": "2026-08-18T09:30:00Z"
}
```

### Typical uses

- Find dead links from a sitemap, CMS export or backlink list
- Audit redirect chains after a site migration (every hop is recorded)
- Scheduled uptime/health checks of a URL list, with latency numbers
- Clean stale URLs out of datasets and bookmark collections

### Limitations

- Checks the exact URLs given — it does not discover links on pages (pair it
  with a sitemap extractor if you need URL discovery)
- Sites that block automated clients may report broken here yet load in a browser
- JavaScript-rendered soft-404s (page says "not found" but returns 200) are
  reported as ok, because the HTTP layer says ok

### Development (local)

```bash
cd actors/url-status-checker
uv venv --python 3.13 .venv && uv pip install -p .venv/bin/python -r requirements.txt

.venv/bin/python tests/run_local_test.py           # end-to-end test (apify run equivalent), exit 0 = ALL PASS
../../node_modules/.bin/apify run                  # real apify CLI local run (input: storage/key_value_stores/default/INPUT.json)
```

The test spins up a local HTTP server simulating ok / redirect chains / 404 /
persistent 500 / recovers-on-retry / HEAD-hostile / redirect loop / timeout;
expected results live in `tests/expected_output.json`. Publishing → `../../docs/publishing.md`.

# Actor input Schema

## `urls` (type: `array`):

URLs to check (one per line). Duplicates are removed. Only these exact URLs are requested - the checker never crawls or follows links on pages.

## `timeoutSecs` (type: `integer`):

A URL that does not respond within this time is reported as broken (category "timeout").

## `retries` (type: `integer`):

How many times to retry a URL that failed with a network error or HTTP 5xx before reporting it broken.

## `maxRedirects` (type: `integer`):

Give up with category "too\_many\_redirects" after following this many redirects.

## `concurrency` (type: `integer`):

How many URLs are checked in parallel. Requests to the same host are always limited to 2 at a time, whatever this is set to.

## Actor input object example

```json
{
  "urls": [
    "https://apify.com",
    "https://example.com/this-page-does-not-exist"
  ],
  "timeoutSecs": 15,
  "retries": 1,
  "maxRedirects": 10,
  "concurrency": 10
}
```

# Actor output Schema

## `results` (type: `string`):

All dataset items (one per URL): result, httpStatus, finalUrl, redirectChain, latencyMs, errorCategory.

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "urls": [
        "https://apify.com",
        "https://example.com/this-page-does-not-exist"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("nuhemugames/url-status-checker").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = { "urls": [
        "https://apify.com",
        "https://example.com/this-page-does-not-exist",
    ] }

# Run the Actor and wait for it to finish
run = client.actor("nuhemugames/url-status-checker").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print(f"💾 Check your data here: https://console.apify.com/storage/datasets/{run.default_dataset_id}")
for item in client.dataset(run.default_dataset_id).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "urls": [
    "https://apify.com",
    "https://example.com/this-page-does-not-exist"
  ]
}' |
apify call nuhemugames/url-status-checker --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,nuhemugames/url-status-checker"
        }
    }
}

```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/SJFLlMeA33lTYuglD/builds/q6xgHsOQDjTt8Insf/openapi.json
