# URL Status Checker API — Bulk HTTP Status, Redirects & Links (`highbrow_fame/url-status-checker-api`) Actor

Check thousands of URLs at once: HTTP status, redirect chain, final URL, response time, content type, SSL expiry and a dead-link flag. Unofficial URL Status Checker API.

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

## Pricing

$0.20 / 1,000 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/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 API — Bulk HTTP Status, Redirects & Links

Check thousands of URLs in one run and get one flat JSON record per link: the final HTTP status, the redirect chain hop by hop (301 → 302 → 200, with every Location), the final URL, response time, content type, size and server headers, the SSL certificate's expiry and issuer, and a plain `isDeadLink` flag. Fast HEAD requests with an automatic GET fallback, up to 100 checks in parallel, no proxy traffic unless a connection drops.

**Try it in 1 minute:**

1. Press **Start** — the prefilled input checks four public URLs: a plain page, a site that redirects http → https, a URL that answers 404 and a Wikipedia article.
2. A few seconds later the **Output** tab shows each URL's status, final URL, redirect hops, timing, content type and certificate days left.
3. Download JSON, CSV or Excel, or schedule the run to watch your links every day.

Price: **$0.20 per 1,000 checks**. A check is complete when the URL answered — with any status, 200 or 404 alike. URLs that cannot be reached at all (DNS failure, timeout, TLS failure, redirect loop) and inputs that are not web addresses are free.

**Use it to:**

- Find broken links in a sitemap, a content export or a backlink list: every record carries `isDeadLink` and the real final status.
- Audit redirect chains after a site migration — see every hop, catch loops, relative Locations and chains that are longer than they should be.
- Expand short links and tracking URLs to their final destination without visiting them in a browser.
- Monitor uptime and SSL expiry of your domains on a schedule and alert on `sslDaysLeft` below 14 or `ok: false`.
- Feed an AI agent or a spreadsheet structured link health instead of raw HTTP responses.

### How it works

1. Every input is normalised (a missing scheme becomes https://, the fragment is dropped) and duplicates are checked once.
2. The URL is requested with HEAD and `redirect: manual`, so each 3xx answer is recorded as a hop `{ url, status, location }` (relative Locations are resolved against the current URL) and followed, up to **Max redirects**. The same URL seen twice stops the chain as a redirect loop.
3. When a server refuses HEAD (405, 403, 501) or does not answer it in time, the same URL is asked again with GET; the body is not downloaded.
4. For an https final URL the Actor opens one TLS connection to read the certificate: expiry, days left, issuer, subject, and whether Node's verifier trusts it. A bad certificate is reported (`sslValid: false`, `sslError`), never worked around — the HTTP check keeps full TLS verification and comes back FAILED with the TLS error if the handshake fails.
5. A timeout or a dropped connection is tried once more through the Apify proxy (datacenter); the first try always goes out directly, so a normal run costs no proxy traffic.

Checks run up to **Parallel checks** at a time (default 20). Response times are measured from the Actor's own servers.

### Input

| Field | What it does |
|---|---|
| `urls` | Web addresses to check, one per line. Required. |
| `method` | `HEAD` (default; GET fallback on 405/403/501 or when HEAD hangs) or `GET`. |
| `followRedirects` | Follow and record 3xx hops (default on). Off: the first answer is the result. |
| `checkSsl` | Read the certificate of https final URLs (default on). |
| `maxRedirects` | Hops before FAILED "too many redirects" (default 10). |
| `timeoutSecs` | Per request / per hop (default 20). |
| `userAgent` | Optional User-Agent; default `highbrow-fame-url-status-checker/1.0 (+https://apify.com/highbrow_fame)`. |
| `customHeaders` | Optional JSON object of extra request headers. |
| `maxConcurrency` | Parallel checks (default 20, up to 100). |
| `proxyConfiguration` | Used only to retry after a network error; the first try is direct. |

### Output

One record per distinct URL:

| Field | What it is |
|---|---|
| `input`, `url` | The address as given, and as checked |
| `finalUrl`, `httpStatus`, `statusText` | Where the chain ended and the final answer's code and phrase |
| `ok`, `isDeadLink` | `ok`: 2xx/3xx final status; `isDeadLink`: final status ≥ 400 or the check could not be completed |
| `redirectCount`, `redirects[]` | Hops followed, each `{ url, status, location }` |
| `responseMs`, `firstByteMs` | Total time for all hops; time to the first hop's headers |
| `contentType`, `contentLength`, `server`, `lastModified` | Headers of the final answer (`lastModified` ISO 8601) |
| `methodUsed` | `HEAD` or `GET` (GET also after a fallback) |
| `sslValid`, `sslValidTo`, `sslDaysLeft`, `sslIssuer`, `sslSubject`, `sslError` | Certificate of the final https host; null when the SSL check is off or the final URL is http |
| `status`, `error`, `scrapedAt` | `OK` (answered, charged), `FAILED` (not reachable, free) or `INVALID_INPUT` (free), the reason, and when |

#### Example

```json
{
  "input": "http://github.com",
  "url": "http://github.com/",
  "finalUrl": "https://github.com/",
  "httpStatus": 200,
  "statusText": "OK",
  "ok": true,
  "isDeadLink": false,
  "redirectCount": 1,
  "redirects": [
    { "url": "http://github.com/", "status": 301, "location": "https://github.com/" }
  ],
  "responseMs": 412,
  "firstByteMs": 96,
  "contentType": "text/html; charset=utf-8",
  "contentLength": null,
  "server": "GitHub.com",
  "lastModified": null,
  "methodUsed": "HEAD",
  "sslValid": true,
  "sslValidTo": "2027-03-07T23:59:59.000Z",
  "sslDaysLeft": 139,
  "sslIssuer": "Sectigo Limited",
  "sslSubject": "github.com",
  "sslError": null,
  "status": "OK",
  "error": null,
  "scrapedAt": "2026-10-19T12:00:00.000Z"
}
```

A dead link looks like this: `"httpStatus": 404, "ok": false, "isDeadLink": true, "status": "OK"` — the check completed, the answer is what you asked for. A host that does not exist looks like this: `"httpStatus": null, "isDeadLink": true, "status": "FAILED", "error": "DNS lookup failed: host not found"` — free.

### FAQ

**Is this an official API?** No, it's an unofficial API built on public pages — it sends ordinary HTTP requests to the URLs you give it and reports what the servers answer.

**Why is a 404 charged?** Because it is the result: the whole point of a link checker is finding dead links, and a server that answers 404 (or 410, 500, 403) has completed the check. Only checks that got no answer at all — DNS failure, timeout, TLS handshake failure, redirect loop, too many redirects — are `FAILED` and free.

**HEAD or GET?** HEAD is fastest and most servers answer it correctly. Some refuse it (405) or answer it differently from GET; those URLs are retried with GET automatically and `methodUsed` tells you which one gave the answer. Choose GET when you want every URL checked the way a browser would ask. The body is never downloaded, so `contentLength` is the server's own Content-Length header (null when it sends none, which is common for compressed HTML).

**Does it bypass bot protection?** No. A site that answers 403 or a challenge page to automated requests gets exactly that in `httpStatus`. You can set a browser-like `userAgent` and `customHeaders` to see what a visitor gets, but there is no captcha solving or IP rotation to get around a block.

**What does the SSL check see?** The certificate the final https host presents, verified against Node's trusted roots and the hostname: expiry date, days left, issuer and subject. Expired, self-signed and wrong-name certificates are reported with `sslValid: false` and the reason; the HTTP check is still made with full verification and is `FAILED` with the TLS error when the handshake is refused.

**Like it?** A short review on the Store page helps other people find this Actor. Something missing or broken? Tell us on the Issues tab — we read every one.

### Related Actors

- [Sitemap Extractor](https://apify.com/highbrow_fame/sitemap-url-extractor) — every URL, date and image from a site's sitemaps
- [Domain Checker](https://apify.com/highbrow_fame/domain-whois-dns-ssl) — WHOIS, DNS and SSL expiry in bulk
- [Website Tech Stack Detector](https://apify.com/highbrow_fame/website-tech-stack) — CMS, frameworks and analytics of any site
- [PageSpeed Insights API](https://apify.com/highbrow_fame/pagespeed-api) — Lighthouse scores and web vitals
- [Markdown Site Crawler](https://apify.com/highbrow_fame/markdown-site-crawler) — a whole site as clean Markdown

# Actor input Schema

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

Web addresses to check, one per line. A missing scheme gets https://. Duplicates are checked once.

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

HEAD is fastest and asks only for headers; when a server refuses HEAD (405, 403, 501) or does not answer it, the same URL is asked again with GET. GET always downloads headers (the body is not read).

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

Follow 3xx answers hop by hop and record each one (url, status, location). Off: the first answer is the result, even a 301.

## `checkSsl` (type: `boolean`):

For https final URLs, read the server's TLS certificate: expiry date, days left, issuer, and whether it is trusted (expired, self-signed or wrong-name certificates are reported, not hidden).

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

Stop with FAILED "too many redirects" after this many hops. A loop (the same URL twice) stops at once.

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

Per request (each hop of a redirect chain). A URL that does not answer in time is FAILED "timed out" (free) after one more try through the proxy.

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

Optional. Sent as the User-Agent header. Default: highbrow-fame-url-status-checker/1.0 (+https://apify.com/highbrow_fame). Some sites answer bots and browsers differently — paste a browser UA to see what a visitor gets.

## `customHeaders` (type: `object`):

Optional. Extra request headers as a JSON object, e.g. {"Accept-Language": "de"} — sent with every request.

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

How many URLs to check at the same time. Lower it when many URLs point to the same server.

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

Used only to try again after a network error (timeout, dropped connection): the first try of every URL goes out directly, so most runs cost no proxy traffic at all.

## Actor input object example

```json
{
  "urls": [
    "https://example.com",
    "http://github.com",
    "https://example.com/this-page-does-not-exist",
    "https://en.wikipedia.org/wiki/Web_scraping"
  ],
  "method": "HEAD",
  "followRedirects": true,
  "checkSsl": true,
  "maxRedirects": 10,
  "timeoutSecs": 20,
  "customHeaders": {},
  "maxConcurrency": 20,
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}
```

# Actor output Schema

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

Every result as a JSON record, with a status for each input.

## `resultsCsv` (type: `string`):

The same records as CSV, for a spreadsheet.

# 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",
        "http://github.com",
        "https://example.com/this-page-does-not-exist",
        "https://en.wikipedia.org/wiki/Web_scraping"
    ],
    "customHeaders": {},
    "proxyConfiguration": {
        "useApifyProxy": true
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("highbrow_fame/url-status-checker-api").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",
        "http://github.com",
        "https://example.com/this-page-does-not-exist",
        "https://en.wikipedia.org/wiki/Web_scraping",
    ],
    "customHeaders": {},
    "proxyConfiguration": { "useApifyProxy": True },
}

# Run the Actor and wait for it to finish
run = client.actor("highbrow_fame/url-status-checker-api").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",
    "http://github.com",
    "https://example.com/this-page-does-not-exist",
    "https://en.wikipedia.org/wiki/Web_scraping"
  ],
  "customHeaders": {},
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}' |
apify call highbrow_fame/url-status-checker-api --silent --output-dataset

```

## MCP server setup

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

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/bEp54gHHB4GELPWXA/builds/Wya4MaDpI6VxQ9tz5/openapi.json
