# URL Status Checker: Bulk HTTP Status Code & Redirect Checker (`jtpalms/url-status-checker`) Actor

Bulk URL status and HTTP status code checker. For each URL: final status, every redirect hop, response time, content type and size, page title, canonical, meta robots, X-Robots-Tag, HSTS, server, and DNS, TLS and timeout errors. For SEO migrations and redirect audits. USD 1.50 per 1,000 URLs.

- **URL**: https://apify.com/jtpalms/url-status-checker.md
- **Developed by:** [JT Palms](https://apify.com/jtpalms) (community)
- **Categories:** SEO tools, Developer tools, Automation
- **Stats:** 3 total users, 2 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

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

## URL Status Checker: bulk HTTP status codes and redirect chains

Paste a list of URLs and get back, for each one, the final HTTP status code, every redirect hop (URL, status and Location), the final URL, response time, content type and size, page title, canonical URL, meta robots, X-Robots-Tag, HSTS and server header. Dead URLs come back with a clear reason: DNS failure, TLS/SSL certificate error, timeout, connection refused, redirect loop or too many redirects.

**USD 1.50 per 1,000 URLs.** Malformed input is free.

### What people use it for

- **SEO site migrations.** Check that every old URL 301-redirects to the right new page in one hop, not through a chain of three, and that the target returns 200 and is indexable.
- **Redirect audits.** Find redirect chains, loops, 302s that should be 301s, and http:// URLs that do not go to https://.
- **Link rot.** Check the outbound links in your articles, docs, bookmarks or a CSV export and find the ones that now 404, time out or point to dead domains.
- **Uptime spot checks.** Check a list of landing pages, APIs or client sites on a schedule and alert when anything is not 2xx.
- **Indexability checks.** See which URLs carry `noindex` in a meta tag or an `X-Robots-Tag` header, and where the canonical tag points.

### Sample output

Real results, trimmed:

```json
{
  "input": "http://apple.com",
  "url": "http://apple.com/",
  "statusCode": 200,
  "ok": true,
  "finalUrl": "https://www.apple.com/",
  "redirected": true,
  "redirectCount": 1,
  "redirectChain": [
    { "url": "http://apple.com/", "status": 301, "location": "https://www.apple.com/", "timeMs": 187, "method": "GET" },
    { "url": "https://www.apple.com/", "status": 200, "location": null, "timeMs": 457, "method": "GET" }
  ],
  "responseTimeMs": 644,
  "contentType": "text/html; charset=utf-8",
  "contentLength": 254160,
  "title": "Apple",
  "canonicalUrl": "https://www.apple.com/",
  "metaRobots": null,
  "xRobotsTag": null,
  "indexable": true,
  "hsts": "max-age=31536000; includeSubdomains; preload",
  "server": "Apple",
  "httpVersion": "2.0",
  "errorType": null,
  "error": null,
  "checkedAt": "2026-09-28T19:21:49.601Z"
}
```

Failures are just as clear:

```json
{ "input": "https://www.google.com/this-page-does-not-exist", "statusCode": 404, "ok": false, "title": "Error 404 (Not Found)!!1", "responseTimeMs": 371 }
{ "input": "https://expired.badssl.com/", "statusCode": null, "errorType": "TLS", "error": "TLS/SSL error: CERT_HAS_EXPIRED certificate has expired" }
{ "input": "https://this-domain-does-not-exist-9f8e7d.com", "statusCode": null, "errorType": "DNS", "error": "Domain not found (DNS): ENOTFOUND ..." }
{ "input": "http://10.255.255.1/", "statusCode": null, "errorType": "TIMEOUT", "error": "Timed out: ETIMEDOUT Timeout awaiting 'request' for 5000ms" }
```

| Field | What it is |
|---|---|
| `statusCode` | Status of the last response (after redirects, if followed). `null` when there was no response. |
| `redirectChain` | Every hop in order: `url`, `status`, `location` (absolute), `timeMs`, and the `method` used. The last hop is the final response. |
| `finalUrl`, `redirectCount` | Where the chain ended and how many redirects it took. |
| `responseTimeMs` | Time to response headers, summed over all hops. |
| `contentType`, `contentLength` | From the headers; the length falls back to the downloaded size when the header is missing. |
| `title`, `canonicalUrl`, `metaRobots` | From the HTML head (GET mode). A canonical URL in a `Link` header is used for PDFs and other non-HTML files. |
| `xRobotsTag`, `hsts`, `server`, `httpVersion` | Response headers of the final hop. |
| `indexable` | `true` when the final status is 200 and neither meta robots nor X-Robots-Tag says `noindex` or `none`. |
| `errorType` | `DNS`, `TLS`, `TIMEOUT`, `CONNECTION_REFUSED`, `CONNECTION_ERROR`, `UNREACHABLE`, `REDIRECT_LOOP`, `TOO_MANY_REDIRECTS`, `REDIRECT_INVALID`, `INVALID_URL` or `OTHER`. |

The run's key-value store also gets a `SUMMARY` record with counts per status class (2xx, 3xx, 4xx, 5xx) and per error type.

### How to use it

1. Paste your URLs into **URLs to check**, one per line (or send them as a JSON array through the API).
2. Pick a **Request method**:
   - **GET** (default) reads the page, so you also get the title, canonical and meta robots.
   - **HEAD, falling back to GET** is lighter and faster: headers only, with GET used only for servers that refuse HEAD.
3. Optional: turn off **Follow redirects** to see only the first response, put your own bot name in **Custom user agent** (the user agent you choose is your responsibility), or add a **Proxy** to check from another country.
4. Click **Start**, then filter the table by status, or export CSV, JSON or Excel.

For uptime or link-rot monitoring, save the input as a task, schedule it daily, and send the results to Slack, Google Sheets or a webhook.

### Pricing

| What | Price |
|---|---|
| URL checked (any status, including 4xx, 5xx, DNS, TLS and timeout errors: that is the answer) | USD 0.0015 (USD 1.50 per 1,000) |
| Malformed input (not an http or https URL) | Free |
| Duplicate URLs in the list | Checked and billed once |

Set a maximum cost per run in the run options; the actor stops cleanly when it is reached and keeps everything checked so far.

### Limits

- It checks the HTML the server sends. Redirects done by JavaScript or `<meta http-equiv="refresh">` are not followed.
- In GET mode it reads up to 2 MB of each HTML page to find the title and head tags; the rest is skipped.
- **The actor identifies itself.** Every request carries the user agent `Mozilla/5.0 (compatible; url-status-checker/1.0; +https://apify.com/JTPalms/url-status-checker)`, with plain request headers. It never pretends to be Chrome or any other browser, and has no browser presets.
- Some sites block bots and answer with 401, 403, 429 or a challenge page. That answer is what the actor reports; it does not try to get past bot protection. Residential proxies are not supported, and a data center proxy only helps when a site blocks data-center addresses.
- Up to 30 redirects per URL (10 by default) and 120 seconds per request.

### Related actors

- [Broken Link Checker: Find Dead Links on Any Website](https://apify.com/JTPalms/broken-link-checker): Crawl a website and find every broken link, image, script and stylesheet: 404s, 5xx, DNS and TLS errors, redirect loops and redirected...
- [Sitemap URL Extractor: All Pages of Any Website](https://apify.com/JTPalms/sitemap-url-extractor): Get every URL of a website from its XML sitemaps.
- [Link Preview & URL Metadata: Open Graph, Meta Tags, Favicon](https://apify.com/JTPalms/link-preview-metadata): Get the URL metadata a link preview shows: title, description, image, site name, Open Graph and Twitter card meta tags, best favicon,...
- [SEO Audit & AI Search Readiness Checker (GPTBot, llms.txt)](https://apify.com/JTPalms/seo-ai-readiness-audit): Crawl a site and audit every page for SEO basics (title, meta, H1, canonical, hreflang, noindex, alt text, JSON-LD, status, speed) plus...

### FAQ

**Why is a URL that works in my browser shown as 403?** Some sites block bots, and this actor says plainly that it is one. The 403 is that site's answer to automated checks; open the URL in a browser to confirm the page itself works.

**Does it follow http to https redirects?** Yes, and every hop is listed in `redirectChain`, so you can see whether a migration takes one hop or several.

**Why am I charged for 404s and timeouts?** Because that is the answer you asked for: the URL was checked and it is broken. Only malformed input is free.

**Is it legal?** It requests public URLs that you supply, under its own name, and reads only the response headers and the page head. It does not log in and collects no personal data.

**Something missing or wrong?** Open an issue with the URL.

# Actor input Schema

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

One URL per line. A URL without http:// or https:// is checked as https://. Duplicates are checked once. Malformed entries are reported as INVALID_URL and are free.

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

GET reads the page, so you also get the title, canonical URL and meta robots. HEAD then GET asks for headers only and falls back to GET when a server refuses HEAD: faster and lighter, but no title, canonical tag or meta robots. HEAD only never downloads a page.

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

Follow each redirect and report every hop (URL, status, Location). Turn off to see only the first response of each URL.

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

Stop following after this many hops and report TOO_MANY_REDIRECTS. Redirect loops are detected and reported as REDIRECT_LOOP.

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

How long to wait for one request (one hop) before reporting TIMEOUT.

## `customUserAgent` (type: `string`):

Leave empty and every request identifies this actor honestly as Mozilla/5.0 (compatible; url-status-checker/1.0; +https://apify.com/JTPalms/url-status-checker). If you type your own, for example MyAppBot/1.0 (+https://example.com), it is sent instead; the user agent you choose is your responsibility. There are no browser presets: this actor does not pretend to be Chrome or any other browser, and some sites block bots.

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

How many URLs to check at the same time. Lower it if many URLs are on one small site.

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

Optional. Use a proxy when sites block data-center requests, or to check from another country. Status codes and redirects then reflect what the proxy location sees.

## Actor input object example

```json
{
  "urls": [
    "http://apple.com",
    "https://www.google.com/this-page-does-not-exist",
    "https://expired.badssl.com/"
  ],
  "method": "GET",
  "followRedirects": true,
  "maxRedirects": 10,
  "timeoutSecs": 20,
  "maxConcurrency": 10
}
```

# Actor output Schema

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

All output rows in the default dataset (JSON, CSV, Excel via the format parameter).

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

Counts and per-input status for this run.

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

// Run the Actor and wait for it to finish
const run = await client.actor("jtpalms/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": [
        "http://apple.com",
        "https://www.google.com/this-page-does-not-exist",
        "https://expired.badssl.com/",
    ] }

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

```

## MCP server setup

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