# Redirect Chain Checker (`maximedupre/redirect-checker`) Actor

Trace public HTTP/HTTPS URLs hop by hop to see the terminal URL, domain, status, timing, and redirect issues. Review clear audit rows for SEO checks, site migrations, and link QA.

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

## Pricing

$0.15 / 1,000 redirect checks

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

### 🔗 Trace every redirect path

SEO teams, developers, and site owners can use Redirect Chain Checker to trace public HTTP/HTTPS URLs hop by hop. Get each URL's terminal URL and domain, status code, redirect hops, response times, and diagnosed issues in a clear dataset. Use it to review link changes, find terminal errors, and check redirect paths before a site migration.

**Use cases**

- Use **[Status Code Checker](https://apify.com/maximedupre/redirect-checker/examples/status-code-checker)** to review the terminal HTTP status for each submitted URL.
- Use **[URL Redirect Checker](https://apify.com/maximedupre/redirect-checker/examples/url-redirect-checker)** to see where public URLs end after their redirects.
- Use **[Redirect Chain Checker](https://apify.com/maximedupre/redirect-checker/examples/redirect-chain-checker)** to trace every hop and its response status.
- Use **[Bulk Redirect Checker](https://apify.com/maximedupre/redirect-checker/examples/bulk-redirect-checker)** to audit a list of public URLs in one run.
- Use **[301 Redirect Checker](https://apify.com/maximedupre/redirect-checker/examples/301-redirect-checker)** to review permanent redirect responses in a chain.
- Use **[HTTPS Redirect Checker](https://apify.com/maximedupre/redirect-checker/examples/https-redirect-checker)** to spot HTTP hops in a redirect path.
- Use **[302 Redirect Checker](https://apify.com/maximedupre/redirect-checker/examples/302-redirect-checker)** to find temporary redirects in submitted URLs.

#### 📦 See the full redirect chain

Each saved dataset row represents one submitted public HTTP/HTTPS URL. The Actor follows server-side redirects and records the ordered hops, terminal destination, status codes, timing, and issue details it observes.

The `resultScope` choice controls which rows are returned. `all` keeps clean and issue rows. `issuesOnly` keeps rows with a diagnosed issue. A clean chain has an empty `issueTypes` array; an issue row can include `redirectLoop`, `maxDepthExceeded`, `terminalError`, `temporaryRedirect`, `insecureHop`, `longChain`, or `resolutionError`.

These are point-in-time network observations. A later check can see a different status, destination, or response time.

#### ▶️ Run a URL redirect audit

1. Add one or more public URLs that start with `http://` or `https://`.
2. Choose `all` or `issuesOnly` for `resultScope`.
3. Set `maxDepth` when you need a different redirect boundary, or leave it empty to use `10`.
4. Start the run and open the dataset or the output links.

The Actor checks only the URLs you submit. It does not crawl a domain, discover new URLs, render page content, or follow client-side JavaScript or meta-refresh navigation. It checks public destinations without a source login or API key and does not audit private, local-network, login-gated, paywalled, CAPTCHA-protected, or otherwise access-controlled destinations.

#### ⚙️ Input

**Input fields**

| Field | Type | What it does |
|---|---|---|
| `urls` | array of strings | Required. Adds 1–10,000 public HTTP/HTTPS URLs to check. Each URL is checked separately. |
| `resultScope` | string | Choose `all` to return clean and issue rows, or `issuesOnly` to return only rows with a diagnosed issue. Defaults to `all`. |
| `maxDepth` | integer | Sets the maximum number of redirect responses to follow for each URL. A chain that needs more hops is reported as exceeding this limit. Leave it empty to use `10`. |

This is the public input from a successful current-beta default-input run:

```json
{
  "urls": [
    "https://example.com",
    "https://httpbin.org/redirect/2",
    "https://httpbin.org/status/404"
  ],
  "resultScope": "all",
  "maxDepth": 10
}
```

#### 🧾 Output

**Run output links**

| Field | Type | What it does |
|---|---|---|
| `results` | string | Opens the default dataset with completed URL audit rows. |
| `summary.urlsChecked` | string | Opens the run's count of submitted URLs checked. |
| `summary.urlsWithIssues` | string | Opens the run's count of checked URLs with one or more diagnosed issues. |

**Dataset audit rows**

| Field | Type | What it does |
|---|---|---|
| `submittedUrl` | string | The public URL submitted for checking. |
| `terminalUrl` | string or null | The last URL reached after redirects, or `null` when no terminal response was reached. |
| `terminalDomain` | string or null | The domain at the terminal URL, or `null` when no terminal response was reached. |
| `terminalStatusCode` | integer or null | The final HTTP status code, or `null` when no terminal response was reached. |
| `redirectHopCount` | integer | The number of redirect responses in the chain. |
| `hops` | array of objects | The ordered responses seen while the URL was checked. |
| `hops[].url` | string | The URL requested at that hop. |
| `hops[].statusCode` | integer | The HTTP status code returned at that hop. |
| `hops[].redirectUrl` | string, optional | The absolute URL reached by that redirect. It is omitted for the terminal response. |
| `hops[].responseTimeMs` | integer | The time to receive that response, in milliseconds. |
| `chainResolutionTimeMs` | integer | The total time to resolve the redirect chain, in milliseconds. |
| `checkedAt` | date-time string | The UTC time when the URL audit was recorded. |
| `isShortener` | boolean | Whether the submitted URL uses a recognizable URL-shortener domain. |
| `isClean` | boolean | Whether the chain reached its destination without a diagnosed redirect issue. |
| `issueTypes` | array of strings | Issue identifiers in priority order. An empty array means no issue was found. |
| `diagnosis` | string, optional | A plain-language explanation of the redirect issues. It is omitted when no issue is found. |

This complete row is from a successful current-beta run. It shows a two-hop chain with temporary redirects and a long-chain diagnosis:

```json
{
  "submittedUrl": "https://httpbin.org/redirect/2",
  "terminalUrl": "https://httpbin.org/get",
  "terminalDomain": "httpbin.org",
  "terminalStatusCode": 200,
  "redirectHopCount": 2,
  "hops": [
    {
      "url": "https://httpbin.org/redirect/2",
      "statusCode": 302,
      "redirectUrl": "https://httpbin.org/relative-redirect/1",
      "responseTimeMs": 687
    },
    {
      "url": "https://httpbin.org/relative-redirect/1",
      "statusCode": 302,
      "redirectUrl": "https://httpbin.org/get",
      "responseTimeMs": 1105
    },
    {
      "url": "https://httpbin.org/get",
      "statusCode": 200,
      "responseTimeMs": 831
    }
  ],
  "chainResolutionTimeMs": 2623,
  "checkedAt": "2026-08-09T16:14:24.892Z",
  "isShortener": false,
  "isClean": false,
  "issueTypes": [
    "temporaryRedirect",
    "longChain"
  ],
  "diagnosis": "The chain contains a temporary 302 redirect. The chain contains more than one redirect hop."
}
```

When `maxDepth` stops a chain before a terminal response, the terminal URL, domain, and status can be `null`. This is a genuine row from a successful `issuesOnly` run:

```json
{
  "submittedUrl": "https://httpbin.org/redirect/2",
  "terminalUrl": null,
  "terminalDomain": null,
  "terminalStatusCode": null,
  "redirectHopCount": 2,
  "hops": [
    {
      "url": "https://httpbin.org/redirect/2",
      "statusCode": 302,
      "redirectUrl": "https://httpbin.org/relative-redirect/1",
      "responseTimeMs": 368
    },
    {
      "url": "https://httpbin.org/relative-redirect/1",
      "statusCode": 302,
      "redirectUrl": "https://httpbin.org/get",
      "responseTimeMs": 311
    }
  ],
  "chainResolutionTimeMs": 679,
  "checkedAt": "2026-08-09T16:14:24.825Z",
  "isShortener": false,
  "isClean": false,
  "issueTypes": [
    "maxDepthExceeded",
    "temporaryRedirect",
    "longChain"
  ],
  "diagnosis": "The redirect chain exceeds the selected maximum depth. The chain contains a temporary 302 redirect. The chain contains more than one redirect hop."
}
```

#### 💳 Pricing

Pricing is pay per event. The buyer-facing event is **Redirect check**: one public URL is traced through its redirect chain and saved with hop details, terminal destination when reached, status, and diagnosis. See the Store pricing panel for the current rate.

#### 🔌 Integrations

Use the dataset and output links in Apify Console or the Apify API. Apify schedules and webhooks can run or notify your workflow when you need recurring checks.

https://www.youtube.com/watch?v=bNACk1\_S\_6w\&list=PLObrtcm1Kw6MUrlLNDbK9QRg8VDJg0gOW\&index=4

#### ❓ FAQ

##### Does it follow client-side JavaScript or meta-refresh redirects?

No. It traces server-side HTTP redirects only. It does not render pages or execute client-side JavaScript or meta-refresh navigation.

##### Can it check a private or login-protected URL?

No. It checks public HTTP/HTTPS URLs without a source login or API key. Private, local-network, login-gated, paywalled, CAPTCHA-protected, and other access-controlled destinations are not supported.

##### What does `maxDepth` do?

It sets the maximum number of redirect responses to follow for each URL. If the chain needs more hops, the row can include `maxDepthExceeded`, and terminal fields can be `null` when no terminal response was reached.

##### What does `issuesOnly` change?

It changes which rows are returned. `all` includes clean and issue rows, while `issuesOnly` returns only rows with a diagnosed issue.

##### Can it show 301 and 302 redirects?

Yes. Each hop includes its HTTP `statusCode`. A 302 response can also add `temporaryRedirect` to `issueTypes` when the Actor diagnoses it as an issue.

##### Does it crawl a whole website?

No. Submit the URLs you want to check. The Actor does not discover links or crawl a domain.

##### Why can a later check show a different status or response time?

Each row is a point-in-time observation from the run environment. Server changes, network conditions, and geography can change a later result.

##### Can it identify shortened URLs?

Yes, when the submitted URL uses a recognizable URL-shortener domain, `isShortener` is `true`.

### 📝 Changelog

**0.0: Initial release**

### 🆘 Support

For issues, questions, or feature requests, [file a ticket](https://console.apify.com/actors/maximedupre~redirect-checker/issues) and I'll fix or implement it in less than 24h 🫡

### 🔗 Related Actors

- [Website URL Crawler & Link Extractor](https://apify.com/maximedupre/website-url-crawler) — build a public URL inventory from links and sitemaps before a redirect audit.
- [XML Sitemap Health Validator](https://apify.com/maximedupre/sitemap-validator) — check sitemap-listed URLs for status codes, redirects, and terminal URLs.
- [Bulk Redirect Chain Checker & Loop Finder](https://apify.com/davidbenittah/redirect-chain-auditor) — trace a bulk list and review redirect loops and long chains.
- [Redirect Chain Checker](https://apify.com/eliai/redirect-chain-checker) — inspect a URL's full redirect path for a migration or SEO check.
- [URL Shortener Expander - Redirect Checker](https://apify.com/benthepythondev/url-shortener-expander) — expand short URLs and inspect their final destinations.

**Made with ❤️ by Maxime Dupré**

# Actor input Schema

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

Add one or more public HTTP/HTTPS URLs. Each URL is checked on its own and gets a hop-by-hop redirect trace.

## `resultScope` (type: `string`):

Choose which audit results to return. All results include URLs with no diagnosed issue. Issues only returns URLs with a diagnosed issue.

## `maxDepth` (type: `integer`):

Set the maximum number of redirect responses to follow for each URL. A chain that needs more hops is reported as exceeding this limit. Leave it empty to use 10.

## Actor input object example

```json
{
  "urls": [
    "https://example.com"
  ],
  "resultScope": "all",
  "maxDepth": 10
}
```

# Actor output Schema

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

Open the default dataset with completed URL audit rows.

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

A link to the run's count of submitted URLs checked.

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

A link to the run's count of checked URLs with one or more diagnosed issues.

# 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"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("maximedupre/redirect-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"] }

# Run the Actor and wait for it to finish
run = client.actor("maximedupre/redirect-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"
  ]
}' |
apify call maximedupre/redirect-checker --silent --output-dataset

```

## MCP server setup

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