# Bulk URL Status and Redirect Checker (`fecklesspleb/url-status-checker`) Actor

Check HTTP status codes and redirect chains for thousands of URLs: every hop with status and Location, final URL, response time, broken flag, and warnings for redirect loops, long chains, temporary redirects and HTTPS to HTTP downgrades. Comprobar redirecciones, verificar redirecionamentos.

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

## Pricing

Pay per event

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/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

## Bulk URL Status Checker: HTTP Status Codes and Redirect Chains

Check the HTTP status code of thousands of URLs at once and trace every redirect. For each URL you get:

- the final status code and its category (success, redirect, client-error, server-error or error);
- the full redirect chain, with each hop's status code, `Location` and response time;
- the final URL;
- total time, content type, content length and server;
- a broken-link flag;
- warnings for common redirect problems.

**At a glance:** $0.50 per 1,000 URLs, plus a $0.00005 Apify start event per run at the default 256 MB memory. You pay per result with no subscription, and you can set a maximum cost per run. Export to JSON, CSV or Excel, or call it from the Apify API.

It uses fast async HTTP with no browser and respects robots.txt.

### What this HTTP status code checker is for

- **SEO site migrations:** check that every old URL 301s to the right new page in one hop.
- **Broken link checks** for a list of URLs exported from a CMS, sitemap or spreadsheet. Any 4xx or 5xx status, connection error, timeout, redirect loop or robots.txt skip sets `isBroken: true`.
- **Redirect checking:** expand short links and tracking links to their real destination, and catch redirect loops and HTTPS-to-HTTP downgrades.
- **Uptime spot checks:** schedule the Actor to confirm that key pages still return 200.

### Redirect problems it flags

- Redirect loops and chains longer than `maxRedirects`, reported in `error`.
- `redirect-chain-longer-than-1-hop`.
- `temporary-redirect`: a 302, 303 or 307 where a permanent 301 or 308 is usually intended.
- `https-to-http-downgrade`.

### Input

```json
{
  "urls": [
    {"url": "https://python.org"},
    {"url": "https://httpbin.org/status/404"},
    {"url": "https://httpbin.org/redirect/3"}
  ],
  "method": "GET",
  "maxRedirects": 10
}
```

| Field | Default | Notes |
|---|---|---|
| `urls` | `https://example.com` | URLs to check. A bare domain gets `https://` added. Duplicates are checked once. If the list is empty or is only `https://example.com`, that demo URL is checked free (a `status: warning` row). |
| `method` | `GET` | `GET` reads only the status and headers and does not read the response body. `HEAD` is lighter for most servers. If a server rejects HEAD with 405 or 501, that URL is retried with a normal GET. |
| `maxRedirects` | 10 | Max 30. |
| `includeHeaders` | false | Adds the final response headers to each row (`Set-Cookie` is removed). |
| `maxConcurrency` | 20 | Max 50. Requests to the same host are spaced at least 0.25 s apart. |
| `maxResults` | 0 | Stops after this many URLs. `0` means no cap. |

### Output

Each URL gets one dataset row. These are real rows from a test run on 30 October 2026. Response times vary.

```json
{
  "url": "https://python.org",
  "statusCode": 200,
  "statusText": "OK",
  "category": "success",
  "initialStatusCode": 301,
  "finalUrl": "https://www.python.org/",
  "redirectCount": 1,
  "redirectChain": [
    {"url": "https://python.org", "statusCode": 301, "responseTimeMs": 423, "location": "https://www.python.org/"},
    {"url": "https://www.python.org/", "statusCode": 200, "responseTimeMs": 225}
  ],
  "isRedirected": true,
  "isBroken": false,
  "totalTimeMs": 647,
  "contentType": "text/html; charset=utf-8",
  "contentLength": 11659,
  "server": "nginx",
  "warnings": [],
  "error": null,
  "checkedAt": "2026-10-31T01:33:24.531200+00:00"
}
```

A three-hop temporary redirect (`https://httpbin.org/redirect/3`) returns `redirectCount: 3`, `finalUrl: "https://httpbin.org/get"` and `warnings: ["redirect-chain-longer-than-1-hop", "temporary-redirect"]`.

A host that doesn't resolve returns `statusCode: null`, `category: "error"`, `isBroken: true` and `error: "DNS or connection error"`, plus `status: "error"` and a `message` starting "Not charged". This row is free. A robots.txt skip gets `status: "skipped"`, and the free demo row gets `status: "warning"`. Charged rows have no `status` or `message` field.

### Pricing

Pricing is pay per event:

- **$0.0005 per URL checked**, which is $0.50 per 1,000 URLs. Only URLs that were actually checked are charged. Rows for URLs that could not be checked (connection error, timeout, redirect loop, too many redirects) and URLs skipped by robots.txt are free and carry `status` and `message`.
- **$0.00005 per run start** (the standard Apify `apify-actor-start` event). It is charged per GB of run memory, minimum 1. That is 1 event at the default 256 MB (or any size up to 1 GB), or 2 events at the 2 GB maximum.

Free rows waive only the per-URL fee. The start event is charged on every run, including runs where every row is free.

Examples, at the default 256 MB memory:

- An empty input `{}` (or only the prefilled `https://example.com`) returns 1 free demo row and costs only the start event, $0.00005.
- 1,000 URLs that were all checked cost $0.50005.
- 1,000 URLs of which 50 could not be checked (for example DNS errors) cost 950 × $0.0005 + $0.00005 = $0.47505.
- 10,000 URLs that were all checked cost $5.00005.

The `RUN_SUMMARY` record in the run's key-value store shows `rowsPushed`, `rowsCharged` (rows billed as checked URLs) and `rowsFree` (free rows). Free rows still count towards `maxResults`.

Set **Maximum cost per run** and the Actor stops cleanly when it is reached.

### Use it from n8n, Make or Zapier

Run the Actor through the Apify API and read the dataset. You can use the Apify integrations for Make, Zapier or n8n, or a plain HTTP request to the Apify API's "run Actor synchronously and get dataset items" endpoint. In either case:

1. Send the input JSON above.
2. Filter the returned rows on `isBroken` or `warnings`.

The Apify Console's API tab for this Actor shows the exact endpoint URLs.

### Use with AI agents / MCP

AI agents can run this Actor as a tool through [Apify's MCP server](https://docs.apify.com/platform/integrations/mcp). Add this server URL to your MCP client (Claude, Cursor, VS Code, OpenAI Agents SDK, LangChain and others) and sign in with OAuth, or send your Apify token as `Authorization: Bearer <token>`:

```text
https://mcp.apify.com?tools=fecklesspleb/url-status-checker
```

The tool is named `fecklesspleb--url-status-checker`. Agents can also find it with `search-actors` and run it with `call-actor`. The run returns a dataset ID; read the rows with `get-dataset-items`, and pass `fields` (for example `url,statusCode,finalUrl,isBroken,warnings,error`) to keep the response small.

Minimal tool input:

```json
{"urls": [{"url": "https://python.org"}, {"url": "https://httpbin.org/redirect/3"}, {"url": "https://example.invalid/"}]}
```

Two rows of the output (real items from a run of this input; rows come in the order the checks finish):

```json
[
  {
    "url": "https://httpbin.org/redirect/3",
    "statusCode": 200,
    "statusText": "OK",
    "category": "success",
    "initialStatusCode": 302,
    "finalUrl": "https://httpbin.org/get",
    "redirectCount": 3,
    "isRedirected": true,
    "isBroken": false,
    "warnings": ["redirect-chain-longer-than-1-hop", "temporary-redirect"],
    "error": null
  },
  {
    "url": "https://example.invalid/",
    "statusCode": null,
    "category": "error",
    "isBroken": true,
    "error": "DNS or connection error",
    "status": "error",
    "message": "Not charged: this URL could not be checked (DNS or connection error)."
  }
]
```

(Shortened: other fields such as `redirectChain`, `totalTimeMs`, `contentType`, `server` and `checkedAt` are left out here. Only free rows have `status` and `message`.)

Tips for agents:

- Cost is $0.0005 per checked URL, including URLs that return 4xx or 5xx. Rows with `error` set are free. Set `maxTotalChargeUsd` in the run options (`callOptions` in `call-actor`) to cap a run.
- Filter on `isBroken` for broken links, and on `warnings` or `redirectCount` for redirect problems in a migration.
- Rows are not in input order: match them to your list by `url`.
- The `RUN_SUMMARY` record in the run's key-value store shows `byCategory`, `rowsCharged` and `rowsFree`.

### Limits

- On the Apify platform, a URL whose host, or any redirect hop, resolves to a private, loopback or link-local address is not requested. Its row has `error: "private, loopback and link-local addresses are not allowed"` and `status: "error"`, and it is free.

### FAQ

**Does it download page content?** In GET mode, no. It reads the status line and headers and then closes the response. In HEAD mode, a server that refuses HEAD gets one normal GET instead.

**Does it render JavaScript or follow meta-refresh redirects?** No. It follows HTTP redirects (`Location` headers) only.

**Why is a URL marked "not checked: robots.txt disallows it"?** The site's robots.txt forbids that path for crawlers, so the Actor doesn't request it. The row is still returned so your list stays complete. The row is free.

**Can I crawl a whole website for broken links?** This Actor checks the list of URLs you give it. It doesn't discover links by crawling.

**What happens with invalid input?** If the input is not a JSON object, or a field has the wrong type (for example a number where a list of URLs is expected, or `"abc"` for a number), the run fetches nothing and ends with one free status row: `Invalid input, nothing was fetched and no results were charged: <reason>`. No results are charged for it; the standard Apify start event still applies.

**Is it legal?** The Actor sends ordinary requests (one per URL and per redirect hop, plus one robots.txt request per site), obeys robots.txt and never logs in. You are responsible for the URLs you check.

# Changelog

This Actor's version history is a separate document: https://apify.com/fecklesspleb/url-status-checker/changelog.md

# Actor input Schema

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

URLs to check, one row per URL, as objects: \[{"url": "https://python.org"}, {"url": "https://httpbin.org/status/404"}]. A URL without http(s):// gets https:// added and duplicates are checked once. Links are not crawled. If the list is empty or only the default https://example.com, that demo URL is checked free.

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

"GET" (default) reads only the status line and headers; the body is not downloaded. "HEAD" is lighter for most servers; a URL whose server answers HEAD with 405 or 501 is retried with GET.

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

Redirect hops to follow per URL, 1 to 30. Default 10. A URL with more hops gets error "more than N redirects" and a free row.

## `includeHeaders` (type: `boolean`):

If true, each row also gets headers: the final response headers, with Set-Cookie removed. Default false.

## `maxConcurrency` (type: `integer`):

URLs checked in parallel, 1 to 50. Default 20. Requests to the same host are spaced at least 0.25 s apart, longer if robots.txt sets a crawl delay.

## `maxResults` (type: `integer`):

Stop after this many rows. Default 0 = no cap. Free rows (errors, robots.txt skips, the demo URL) also count. Each checked URL costs $0.0005.

## Actor input object example

```json
{
  "urls": [
    {
      "url": "https://example.com"
    }
  ],
  "method": "GET",
  "maxRedirects": 10,
  "includeHeaders": false,
  "maxConcurrency": 20,
  "maxResults": 0
}
```

# Actor output Schema

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

No description

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

No description

# 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": [
        {
            "url": "https://example.com"
        }
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("fecklesspleb/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": [{ "url": "https://example.com" }] }

# Run the Actor and wait for it to finish
run = client.actor("fecklesspleb/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": [
    {
      "url": "https://example.com"
    }
  ]
}' |
apify call fecklesspleb/url-status-checker --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,fecklesspleb/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/LPkTnDGXUW3hMDxxf/builds/OzbUkbafEchEsER3e/openapi.json
