# Bulk URL Status, Redirect Chain & Migration Map Checker (`creativefour/url-status-audit`) Actor

Check thousands of URLs for status codes, redirect chains, loops, and broken links. Expand sitemaps automatically, and validate a site-migration redirect map: does every old URL land on its new URL?

- **URL**: https://apify.com/creativefour/url-status-audit.md
- **Developed by:** [CreativeFour LLC](https://apify.com/creativefour) (community)
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

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

### What does the Bulk URL Status, Redirect Chain & Migration Map Checker do?

It checks **thousands of URLs in one run** and reports each one's **HTTP status code**, the **full redirect chain** (every hop and its status), where it **finally lands**, and any **problems**: broken links, redirect loops, long chains, temporary redirects, and HTTPS-to-HTTP downgrades.

It can also **expand sitemaps** and **validate a site-migration redirect map**. Paste your old,new URL pairs, and every row comes back as pass or fail, with the URL it really landed on.

Because it runs on Apify, you get API access, scheduling (for example, a nightly broken-link check), integrations, and monitoring. There's no browser and no login, and you can choose to pay only for the problems it finds.

### Why use it?

- **Site migrations and redesigns.** Prove that every old URL 301s to the right new page before Google finds out otherwise.
- **Broken link audits.** Find 404s, 410s, 5xx errors, timeouts, and DNS failures across a URL list or a whole sitemap.
- **SEO redirect cleanup.** Collapse chains of two or more hops, fix loops, and turn stray 302s into 301s.
- **Indexability checks.** Catch pages that are accidentally `noindex`, or canonicalised to another URL.
- **Scheduled monitoring.** Run it on a schedule and alert on any new broken URL.

### How to use it

1. Open the **Input** tab.
2. Paste URLs, add a sitemap URL, or paste a redirect map from a spreadsheet (old URL, new URL).
3. Optional: turn on **Save only problems** so you pay only for URLs that need attention.
4. Click **Start**.
5. Open the **Output** tab and export the results as CSV, Excel, JSON, or HTML.

### Input

| Field | What it does |
|---|---|
| **URLs to check** | One per line. `example.com/page` is treated as `https://example.com/page`. Invalid entries are skipped, and you aren't charged for them. |
| **Sitemaps to expand** | Sitemap or sitemap-index URLs; nested indexes are followed. |
| **Redirect map** | One `old,new` pair per line (comma or tab separated, header row optional), or the same as JSON. |
| **Save only problems** | Skip saving healthy 200s. They're still counted in the run summary. |
| **Check indexability** | Also download each final HTML page to read its canonical and robots tags. |
| **Request method** | HEAD (fast) with automatic GET fallback, or GET only. |
| **Limits** | Max redirects, timeout, overall and per-host concurrency, max URLs, user agent. |

```json
{
  "urls": ["https://example.com", "http://example.com/old-page"],
  "sitemapUrls": ["https://example.com/sitemap.xml"],
  "redirectMapCsv": "old,new\nhttp://example.com/old-page,https://example.com/new-page",
  "onlyProblems": true
}
```

### Output

One result per URL. You can download the dataset in various formats such as JSON, HTML, CSV, or Excel. It has two ready-made views: **Overview** and **Redirect chains**.

```json
{
  "url": "http://example.com/old-page",
  "finalUrl": "https://example.com/new-page",
  "finalStatus": 200,
  "statusClass": "2xx",
  "redirectCount": 2,
  "redirectChain": [
    { "url": "http://example.com/old-page", "status": 301, "location": "https://example.com/old-page" },
    { "url": "https://example.com/old-page", "status": 302, "location": "https://example.com/new-page" }
  ],
  "responseTimeMs": 184,
  "contentType": "text/html; charset=utf-8",
  "xRobotsTag": null,
  "flags": ["redirect_chain", "temporary_redirect"],
  "expectedUrl": "https://example.com/new-page",
  "redirectMatch": true,
  "error": null,
  "checkedAt": "2026-09-23T20:15:00.000Z"
}
```

A **SUMMARY** record in the run's key-value store counts URLs by status class, along with broken URLs, redirected URLs, loops, errors, and redirect-map passes and failures.

### Data fields

| Field | Description |
|---|---|
| `url` | The URL you asked about |
| `finalUrl` / `finalStatus` | Where it ended up, and that page's status code |
| `statusClass` | `2xx`, `3xx`, `4xx`, `5xx`, or `error` |
| `redirectCount` / `redirectChain` | Every redirect hop, with its status and `Location` |
| `flags` | `broken`, `redirect_chain`, `temporary_redirect`, `redirect_loop`, `too_many_redirects`, `https_to_http_downgrade`, `error` |
| `expectedUrl` / `redirectMatch` | Redirect-map target, and whether the URL really landed there |
| `canonical` / `metaRobots` / `indexable` | With indexability checks on |
| `responseTimeMs`, `contentType`, `xRobotsTag`, `error`, `checkedAt` | Details for each check |

### Use it from AI agents (MCP)

AI agents can find and run this Actor through the [Apify MCP server](https://docs.apify.com/integrations/mcp).

- **Claude, ChatGPT, or any MCP client:** add `https://mcp.apify.com?tools=creativefour/url-status-audit` as a custom connector, and sign in to Apify when prompted.
- **Claude Code, Cursor, VS Code, or Codex:** run `apify mcp install claude-code` (swap in your client's name), then ask your agent to run `creativefour/url-status-audit`.

The agent sends the same input as the Input tab, gets the same results, and pays the same per-result price.

### How much does it cost to check URLs?

You pay per result: each URL saved to the dataset is one charge, and URLs skipped by **Save only problems** cost nothing. Set a **maximum charge per run** in the run options; the Actor stops cleanly at that limit and keeps everything checked so far. Checks use lightweight HEAD requests, so large lists finish quickly.

### Tips

- **Save only problems** keeps big sitemap audits cheap: you pay for the 3% that are broken, not the 97% that are fine.
- **Per-host concurrency** (default 3) is polite to a single site. Raise overall concurrency, not per-host, when checking many different sites.
- **GET only** helps with the rare server that answers HEAD requests incorrectly.
- **Schedule it** in Apify to catch broken links the day they appear.

### FAQ and support

**Why does a URL show a different status than my browser?** Some sites answer HEAD requests or bots differently. Switch the request method to GET only, or set a browser-like user agent.

**Does it follow JavaScript or meta-refresh redirects?** No. It follows HTTP redirects (301, 302, 303, 307, 308).

**Is it allowed to check any site?** Please check sites you own or are permitted to test. The Actor sends ordinary, rate-limited requests with a clear user agent and downloads no page content unless you turn on indexability checks.

**Found a bug or need a feature?** Open an issue on the **Issues** tab. Custom versions are available on request.

# Actor input Schema

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

One URL per line. A missing scheme is treated as https.

## `sitemapUrls` (type: `array`):

Sitemap or sitemap-index URLs. Every page they list is checked, and nested sitemap indexes are followed.

## `redirectMapCsv` (type: `string`):

For site migrations: one old,new pair per line (comma or tab separated). Each old URL is checked, and the result says whether it really lands on the new URL. A header row is ignored.

## `redirectMap` (type: `array`):

The same as above, as JSON: \[{"from": "https://old.example.com/a", "to": "https://new.example.com/a"}].

## `onlyProblems` (type: `boolean`):

Save only URLs that are broken, redirected, erroring, non-indexable, or failing the redirect map. Healthy 200s are counted in the summary but not saved, so you are not charged for them.

## `checkIndexability` (type: `boolean`):

Also download each final HTML page to read its canonical URL and meta robots tag. Slower.

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

HEAD is fastest. Servers that refuse HEAD are retried with GET automatically.

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

Stop following a chain after this many hops and flag it as too\_many\_redirects.

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

How long to wait for each request before recording a timeout error.

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

How many URLs are checked at the same time overall.

## `perHostConcurrency` (type: `integer`):

Keeps the check polite to any single site.

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

Upper limit on URLs checked in one run, after sitemap expansion.

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

The User-Agent header sent with every request.

## Actor input object example

```json
{
  "urls": [
    "https://apify.com",
    "http://github.com",
    "https://www.google.com/this-page-does-not-exist"
  ],
  "onlyProblems": false,
  "checkIndexability": false,
  "method": "HEAD_THEN_GET",
  "maxRedirects": 10,
  "timeoutSecs": 15,
  "maxConcurrency": 10,
  "perHostConcurrency": 3,
  "maxUrls": 10000,
  "userAgent": "Mozilla/5.0 (compatible; UrlStatusAudit/1.0; +https://apify.com)"
}
```

# 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": [
        "https://apify.com",
        "http://github.com",
        "https://www.google.com/this-page-does-not-exist"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("creativefour/url-status-audit").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",
        "http://github.com",
        "https://www.google.com/this-page-does-not-exist",
    ] }

# Run the Actor and wait for it to finish
run = client.actor("creativefour/url-status-audit").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",
    "http://github.com",
    "https://www.google.com/this-page-does-not-exist"
  ]
}' |
apify call creativefour/url-status-audit --silent --output-dataset

```

## MCP server setup

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

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/YOvpKyXYGK7CNKWkp/builds/hggJD2GIstcSpuLWF/openapi.json
