# HTTP Status Code Checker: Bulk URL, Website & Redirect Chains (`data-done-right/status-code-checker`) Actor

Status code checker and redirect checker for lists of URLs. Get the HTTP status, redirect chain, redirect type, final URL, response time and errors for each link. Bulk URL status checks that find broken links, 404s, 5xx errors and redirect loops. Pay per URL checked.

- **URL**: https://apify.com/data-done-right/status-code-checker.md
- **Developed by:** [Data Done Right](https://apify.com/data-done-right) (community)
- **Categories:** SEO tools, Developer tools, Agents
- **Security:** Limited permissions
- **Stats:** 1 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.16 / 1,000 url checkeds

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

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

## What's an Apify Actor?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
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.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

## 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.

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

## HTTP Status Code Checker: Bulk URL, Website & Redirect Chains

This HTTP status code checker takes a list of URLs and returns one row per URL with the final status code, every redirect hop, the redirect type, response time and a plain-language error when a URL cannot be reached. Use it as a bulk URL status checker and redirect checker to find broken links, 404s, 5xx errors, redirect loops and slow pages in seconds, without writing a script.

Paste a few URLs or up to 20,000. Each URL is checked on its own, so one dead domain never stops the run, and every URL you enter produces a row you can sort, filter and export as JSON, CSV or Excel.

### What does the HTTP status code checker return?

- **Final HTTP status** (`statusCode`, `statusText`) and a simple `category`: `2xx ok`, `3xx redirect`, `4xx client error`, `5xx server error` or `error`.
- **`isBroken`**: true for 4xx, 5xx and for URLs that could not be reached at all.
- **Redirect chain**: every hop with its status code and target (`redirectChain`), the number of redirects, the type (`301 permanent`, `302 temporary`, mixed) and the final URL.
- **Redirect flags**: `hasRedirectLoop`, `httpToHttps` (an http URL that ends on https) and `wwwChange` (the redirect only adds or removes `www`).
- **Timing and headers**: total `responseTimeMs`, `ttfbMs`, `contentType`, `contentLength` and `server`.
- **Clear errors**: `dns`, `timeout`, `ssl`, `connection refused`, `connection reset`, `redirect loop`, `too many redirects`, `blocked private address` or `invalid URL`.
- **Expected status check**: give the codes you expect, for example `[200]`, and every row gets `matchesExpected`.
- **A run summary** (`SUMMARY` record) with counts by category and status code, the number of broken URLs and the five slowest URLs.

### How to check HTTP status codes in bulk

1. Paste your URLs into **URLs to check**, one per line (use "Bulk edit" for long lists), or into **Or paste URLs** as plain text. A missing `https://` is added for you.
2. Leave the defaults (HEAD with automatic GET fallback, follow up to 10 redirects, 15 second timeout, 20 parallel checks) or change them.
3. Run the Actor and open the **Overview** table, or download the dataset as CSV, Excel or JSON.

### Redirect checker and redirect chain tracer

With **Follow redirects** on (the default), the Actor follows 301, 302, 303, 307 and 308 redirects hop by hop and records each one in `redirectChain`:

```json
"redirectChain": [
  {"url": "http://github.com/", "statusCode": 301, "location": "https://github.com/"}
]
```

You see where a link really ends, whether the redirects are permanent or temporary, whether a page bounces through several hops, and whether a redirect loop exists. Turn **Follow redirects** off to see only the first response and its `location`. Redirects done with an HTML meta refresh or JavaScript are not detected, because the Actor does not run a browser.

### Bulk URL status check: how to find broken links from a URL list

Export URLs from a sitemap, a CRM, a spreadsheet, Search Console or a backlink report, paste them in, and filter the result for `isBroken = true`. Rows with `category` `4xx client error` are dead pages (404, 410), `5xx server error` are server problems, and `error` rows are domains or certificates that fail. To check a migration, set **Expected status codes** to the code you want (for example `[301]` with Follow redirects off) and filter on `matchesExpected = false`.

### Input example

```json
{
  "urls": [
    "https://example.com",
    "http://github.com",
    "https://httpbin.org/status/404",
    "https://apify.com"
  ],
  "method": "HEAD",
  "followRedirects": true,
  "maxRedirects": 10,
  "timeoutSecs": 15,
  "concurrency": 20,
  "expectedStatus": [200]
}
```

| Field | What it does |
|---|---|
| `urls` | URLs to check. Duplicates are removed, the first one is kept. |
| `urlsText` | Optional: the same, as a plain text block with one URL per line. |
| `method` | `HEAD` (default; falls back to `GET` when a server answers a HEAD with 400 or higher) or `GET`. |
| `followRedirects` | Follow redirects and record the chain (default on). |
| `maxRedirects` | Redirects allowed per URL, 1 to 20 (default 10). |
| `timeoutSecs` | Total seconds per URL including redirects (default 15). |
| `concurrency` | Parallel checks, 1 to 50 (default 20). At most 10 at once per website. |
| `userAgent` | `browser` (default) or `bot` (honest `StatusCodeCheckerBot`). |
| `requestHeaders` | Optional extra headers, stored encrypted, dropped when a redirect leaves the site. |
| `expectedStatus` | Optional codes such as `[200, 301]` or `["2xx"]`, adds `matchesExpected`. |
| `maxUrls` | Cap on unique URLs, up to 20,000 (default 1,000). Also caps your spend. |
| `proxyConfiguration` | Optional Apify Proxy, off by default. |

### Output example

One row per URL, in the order you entered them:

```json
{
  "url": "http://github.com/",
  "inputUrl": "http://github.com",
  "finalUrl": "https://github.com/",
  "statusCode": 200,
  "statusText": "OK",
  "category": "2xx ok",
  "isBroken": false,
  "redirectCount": 1,
  "redirectChain": [
    {"url": "http://github.com/", "statusCode": 301, "location": "https://github.com/"}
  ],
  "redirectType": "301 permanent",
  "hasRedirectLoop": false,
  "httpToHttps": true,
  "wwwChange": false,
  "methodUsed": "HEAD",
  "responseTimeMs": 269,
  "ttfbMs": 125,
  "contentType": "text/html",
  "contentLength": null,
  "server": "github.com",
  "location": null,
  "matchesExpected": true,
  "error": null,
  "checkedAt": "2026-10-25T12:00:00+00:00"
}
```

A URL that cannot be reached still gets a row, for example `"statusCode": null, "category": "error", "isBroken": true, "error": "dns: domain name not found or not resolvable"`.

The `SUMMARY` record in the key-value store looks like this:

```json
{
  "urlsInInput": 4, "duplicatesRemoved": 0, "urlsChecked": 4, "charged": 4,
  "byCategory": {"2xx ok": 3, "3xx redirect": 0, "4xx client error": 1, "5xx server error": 0, "error": 0},
  "byStatusCode": {"200": 3, "404": 1},
  "broken": 1, "redirected": 1,
  "slowest": [{"url": "https://httpbin.org/status/404", "responseTimeMs": 328, "statusCode": 404}]
}
```

### Use with AI agents / MCP

Connect Apify's MCP server (mcp.apify.com) to Claude, ChatGPT or Cursor and an agent can verify links itself. Example prompt: "Check these 200 URLs and tell me which ones are broken or redirect more than once." The agent only needs the `urls` field and reads `isBroken`, `redirectCount` and `finalUrl` from the dataset. The same input works from the Apify API, Python and JavaScript clients, Make, Zapier and n8n.

### Use cases

- **SEO and site migrations:** confirm old URLs return the right 301 and land on the right page, with no chains or loops.
- **Broken link checks:** run a list from a sitemap, backlink export or spreadsheet and fix the dead ones.
- **Uptime spot checks:** schedule the Actor and compare the SUMMARY of each run.
- **Link building and outreach:** drop dead backlinks and prospects before you contact anyone.
- **Redirect audits:** find `http` to `https` and `www` rules, temporary redirects that should be permanent, and long chains.

### How the checks work, and the limits

- **HEAD versus GET.** HEAD is fast because no page is downloaded. Some servers answer HEAD wrongly (405 or even 404), so any HEAD answer of 400 or higher is checked again with GET and the GET result wins. `methodUsed` tells you which one produced the final status. The page body is never downloaded either way.
- **No JavaScript.** The Actor does not run a browser, so JavaScript or meta refresh redirects, and pages that show an error inside a 200 response (soft 404s), are not detected.
- **Blocked or rate-limited sites.** Some sites (large social networks, shops, sites behind a CDN firewall) answer data center IPs with 403, 429 or 999, or drop the connection. Those are reported as the status the site returned. Turn on **Proxy** (Apify Proxy) and try again. The Actor does not try to bypass bot protection.
- **Private addresses are never contacted.** Localhost, private networks, link-local and cloud-metadata addresses are blocked, also after DNS resolution and on every redirect hop. Such URLs get a row with `blocked private address`.
- **Politeness.** At most 10 checks run in parallel against the same website, whatever concurrency you set.
- **Certificates are verified.** An expired or invalid certificate shows up as `ssl` in `error`.
- **Cookies are not kept** between URLs.

### How much does it cost

Pay per event: **$0.20 per 1,000 URLs checked** (event `url-checked`, $0.0002 each), and there is no start fee. You are charged once per URL row that was actually checked, redirects and the automatic GET fallback included. Entries that cannot be checked at all (invalid text, `mailto:` links, or a private address written directly in the URL) are free. A URL that fails with DNS, SSL or timeout still counts as checked. Checking 20,000 URLs costs about $4. Set **Max URLs** and your Apify spending limit to cap a run; the Actor stops cleanly when the limit is reached and says so in the status message and SUMMARY.

### FAQ

#### What is the difference between HEAD and GET for status checks?

HEAD asks the server for headers only, so it is faster and lighter. A few servers refuse HEAD. This Actor retries those URLs with GET automatically, so you get the status a browser would see.

#### How do I find broken links from a list of URLs?

Run the list and filter the dataset for `isBroken = true`. 404 and 410 are dead pages, 5xx are server errors, and `error` rows are unreachable domains, bad certificates or timeouts.

#### Does it follow redirects and show the whole redirect chain?

Yes, up to 20 hops. Each hop is listed in `redirectChain` with its status code and target, and `redirectType` summarises them. Redirect loops are stopped at the first repeated URL and flagged with `hasRedirectLoop`.

#### Why does a site return 403 or 429 here but works in my browser?

Some sites block data center IP addresses or unusual traffic. Try **User agent: browser** and Apify Proxy. If the site still refuses, the result is reported as returned.

#### Can it check pages that need a login?

Only if a header is enough. Put an `Authorization` header or a cookie in **Request headers**. It is stored encrypted and not sent to other sites after a redirect. The Actor does not log in through forms.

#### Does it check the content of the page?

No. It checks the HTTP response only: status, redirects, headers and timing.

#### What happens when a URL fails?

It becomes a row with `category: "error"` and an `error` message. The run continues and still ends as succeeded.

### Related tools

- **SEO Audit Tool & Meta Tag Checker: Crawler, Broken Links** crawls a whole site and audits titles, canonicals and links, when you do not have a URL list yet.
- **HTTP Request Runner: Bulk API Calls, Retries, Proxies** sends POST, PUT and other API calls in bulk when you need more than a status check.

# Actor input Schema

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

The URLs to check, one per line (use Bulk edit to paste a long list). A missing https:// is added. Duplicates are removed (the first one is kept). Example: https://example.com, http://github.com.

## `urlsText` (type: `string`):

Alternative to the list above: paste a block of URLs, one per line. Added after the URLs list; duplicates across both are removed.

## `method` (type: `string`):

HEAD asks only for the headers and is fastest; if a server answers a HEAD with an error (400 or higher) the URL is checked again with GET, so the result matches what a browser sees. GET sends a GET to every URL (the body is never downloaded).

## `followRedirects` (type: `boolean`):

Follow 301, 302, 303, 307 and 308 redirects and record every hop in the redirect chain. Turn off to see the first response only (the redirect itself).

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

Stop with the error 'too many redirects' after this many redirects for one URL. Redirect loops are detected earlier and flagged with hasRedirectLoop.

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

Total time allowed for one URL, all redirects included. Slow URLs get the error 'timeout' and still produce a row.

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

URLs checked in parallel. At most 10 run in parallel against the same website, whatever this is set to.

## `userAgent` (type: `string`):

Browser sends a normal desktop browser User-Agent (matches what visitors get; recommended). Bot sends an honest 'StatusCodeCheckerBot' User-Agent, which some sites answer differently.

## `requestHeaders` (type: `object`):

Extra headers sent with every request, for example {"Authorization": "Bearer TOKEN"} to check URLs behind a login you own. Stored encrypted, never written to the output, and dropped when a redirect leaves the original site.

## `expectedStatus` (type: `array`):

Status codes you expect, for example \[200] or \[200, 301] or \["2xx"]. Every row then gets matchesExpected true or false; leave empty to skip.

## `maxUrls` (type: `integer`):

Upper limit of unique URLs checked in this run (also caps what you can be charged). Extra URLs are skipped and counted in SUMMARY.

## `proxyConfiguration` (type: `object`):

Off by default. Use an Apify proxy when a site blocks datacenter IPs or you need to check a URL from a specific country.

## Actor input object example

```json
{
  "urls": [
    "https://example.com",
    "http://github.com",
    "https://httpbin.org/status/404",
    "https://apify.com"
  ],
  "method": "HEAD",
  "followRedirects": true,
  "maxRedirects": 10,
  "timeoutSecs": 15,
  "concurrency": 20,
  "userAgent": "browser",
  "maxUrls": 1000,
  "proxyConfiguration": {
    "useApifyProxy": false
  }
}
```

# Actor output Schema

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

One row per URL: final status code, category, redirect chain, response time and error (all result items of this run, as JSON, CSV, Excel and more).

## `summary` (type: `string`):

SUMMARY record: counts by category and status code, number of broken URLs, the slowest 5 URLs and any notes.

## `records` (type: `string`):

Key-value store records of this run (SUMMARY and any other files).

# 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://example.com",
        "http://github.com",
        "https://httpbin.org/status/404",
        "https://apify.com"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("data-done-right/status-code-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://example.com",
        "http://github.com",
        "https://httpbin.org/status/404",
        "https://apify.com",
    ] }

# Run the Actor and wait for it to finish
run = client.actor("data-done-right/status-code-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://example.com",
    "http://github.com",
    "https://httpbin.org/status/404",
    "https://apify.com"
  ]
}' |
apify call data-done-right/status-code-checker --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,data-done-right/status-code-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/rO2DDV6LPmvtDZQa4/builds/2437vm09GzSiKz621/openapi.json
