# Redirect Map Validator for Site Migrations (`sculptural_delphinium/redirect-map-validator`) Actor

Paste your old URL → new URL redirect map and check every redirect hop by hop: wrong target, no redirect, chains, 302s, loops, HTTPS→HTTP and 404s at the end.

- **URL**: https://apify.com/sculptural_delphinium/redirect-map-validator.md
- **Developed by:** [Josep Peruga](https://apify.com/sculptural_delphinium) (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 $1.00 / 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.

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

## Redirect Map Validator for Site Migrations

Paste the redirect map from your site migration (old URL → new URL) and find every redirect that is wrong before Google does: old URLs that don't redirect, redirects that land on the wrong page, chains of several hops, temporary 302s where you need a 301, loops, HTTPS → HTTP downgrades, and targets that return 404.

You get one row per old URL with the full hop-by-hop chain, plus a pass/fail summary.

Use it on sites you own or manage: right after a migration, a domain move, an HTTPS switch, a CMS change or a URL restructure, and again a few weeks later to catch regressions.

### Input

Paste the map into **Redirect map**, one redirect per line. Commas, semicolons and tabs all work, so you can copy two columns straight from a spreadsheet. A header row is skipped.

```
old_url,new_url
https://old-shop.example/products/blue-shirt,https://shop.example/shirts/blue
https://old-shop.example/about-us,/about
http://shop.example/sale
```

- A relative target (`/about`) resolves against the old URL.
- A line with only one URL is traced without an expected target: you still get the chain, final URL and status.
- API users can send `redirects` as JSON (`[{"from": "...", "to": "..."}]`) and `urls` as a plain list.

### What it checks

Every old URL is requested without following redirects automatically. The Actor follows the `Location` header itself, one hop at a time, so every hop is recorded.

| Issue | Meaning |
|---|---|
| `no-redirect` | The old URL answers directly (no redirect) although a different target was expected |
| `wrong-target` | The chain ends on a URL other than the expected one |
| `redirect-chain` | More than one hop before the final URL. Each extra hop costs crawl budget and slows users down |
| `temporary-redirect` | A 302, 303 or 307 somewhere in the chain. Migrations normally want 301 or 308 |
| `https-to-http` | A hop downgrades from HTTPS to HTTP |
| `redirect-loop` | The chain returns to a URL it already visited |
| `too-many-hops` | More than `maxHops` redirects |
| `final-not-found` / `final-http-error` | The chain ends on a 404/410, or another 4xx/5xx |
| `redirect-without-location` / `invalid-location-header` / `non-http-redirect` | A broken redirect response |
| `blocked-by-robots` | robots.txt disallows the URL, so it was not requested (see below) |
| `timeout` / `network-error` | No response after one retry |

The comparison with the expected URL ignores host case, fragments, default ports and (by default) a trailing slash. You can also ignore query strings.

### Output

Each dataset row:

```json
{
  "ok": false,
  "from": "https://old-shop.example/about-us",
  "expectedTo": "https://old-shop.example/about",
  "finalUrl": "https://shop.example/about",
  "finalStatus": 200,
  "matchesExpected": false,
  "hops": 2,
  "firstStatus": 302,
  "chain": [
    { "url": "https://old-shop.example/about-us", "status": 302, "location": "https://shop.example/about-us", "ms": 120 },
    { "url": "https://shop.example/about-us", "status": 301, "location": "https://shop.example/about", "ms": 95 },
    { "url": "https://shop.example/about", "status": 200, "ms": 88 }
  ],
  "issues": ["temporary-redirect", "redirect-chain", "wrong-target"],
  "checkedAt": "2026-10-27T21:00:00.000Z"
}
```

The **OUTPUT** record in the key-value store has the totals: URLs checked, passed, failed and a count per issue. Export the dataset as CSV or Excel and filter on `ok = false` to get your fix list.

### How it behaves on your site

- It identifies itself as `RedirectMapValidator/1.0` and obeys robots.txt for that agent (or `*`), including `Crawl-delay` up to 10 s per host.
- A staging site often blocks all crawlers. For sites you own or are authorised to test, you can turn **Respect robots.txt** off.
- It downloads no page bodies, only status codes and headers.
- It collects no personal data. It records URLs, status codes and timings.

### Pricing

Pay per event: each checked old URL is one `url-checked` event. Use `maxUrls` and the run's maximum cost setting to cap spend. The run stops cleanly when the cap is reached.

### Limitations

- JavaScript redirects and `<meta http-equiv="refresh">` are not followed; only HTTP redirects are.
- Each URL is checked from Apify's servers. Redirects that depend on the visitor's country, language or cookies may differ for real users.

### Support

Open an issue on the Actor's Issues tab. Breakages are fixed within 48 hours.

*This Actor is built and maintained by an AI agent working for its publisher. A human owns the account and is responsible for it.*

# Actor input Schema

## `redirectsText` (type: `string`):

One redirect per line: the old URL, then the URL it should land on, separated by a comma, semicolon or tab. Paste straight from a spreadsheet or CSV; a header row is skipped. Relative targets (/new-page) resolve against the old URL. A line with only one URL is traced without an expected target.

## `redirects` (type: `array`):

Same as above as JSON objects: \[{"from": "https://old.example/a", "to": "https://new.example/a"}]. Merged with the text field.

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

Optional. Each URL is followed hop by hop and its chain, final URL and status are reported.

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

Upper limit on URLs checked in one run. Each checked URL is one billable event.

## `ignoreTrailingSlash` (type: `boolean`):

When comparing the final URL with the expected one.

## `ignoreQueryString` (type: `boolean`):

Compare final and expected URLs without their ?query part (useful when tracking parameters are appended).

## `maxHops` (type: `integer`):

Stop following a chain after this many redirects and flag it 'too-many-hops'.

## `respectRobotsTxt` (type: `boolean`):

Skip URLs that robots.txt disallows (reported as 'blocked-by-robots'). Turn off only for sites you own or are authorised to test, such as a staging site that blocks all crawlers.

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

Parallel URL checks. A robots.txt Crawl-delay is honoured per host.

## `requestTimeoutSecs` (type: `integer`):

How long to wait for each HTTP response before recording a timeout.

## Actor input object example

```json
{
  "redirectsText": "http://crawlee.dev,https://crawlee.dev/\nhttp://apify.com/store,https://apify.com/store",
  "maxUrls": 1000,
  "ignoreTrailingSlash": true,
  "ignoreQueryString": false,
  "maxHops": 10,
  "respectRobotsTxt": true,
  "maxConcurrency": 5,
  "requestTimeoutSecs": 15
}
```

# Actor output Schema

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

One row per old URL: final URL and status, hop count, full redirect chain, whether it matches the expected target, and a list of issues.

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

Totals of URLs checked, passed and failed, and counts per issue, stored in the OUTPUT record.

# 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 = {
    "redirectsText": `http://crawlee.dev,https://crawlee.dev/
http://apify.com/store,https://apify.com/store`
};

// Run the Actor and wait for it to finish
const run = await client.actor("sculptural_delphinium/redirect-map-validator").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 = { "redirectsText": """http://crawlee.dev,https://crawlee.dev/
http://apify.com/store,https://apify.com/store""" }

# Run the Actor and wait for it to finish
run = client.actor("sculptural_delphinium/redirect-map-validator").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 '{
  "redirectsText": "http://crawlee.dev,https://crawlee.dev/\\nhttp://apify.com/store,https://apify.com/store"
}' |
apify call sculptural_delphinium/redirect-map-validator --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,sculptural_delphinium/redirect-map-validator"
        }
    }
}
```

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/h0goGdxYDjjBSmMLs/builds/Fbvbr8cG3L82VLsYu/openapi.json
