# HTTP Status and Headers Checker (`gridlinetools/http-status-headers-checker`) Actor

Audit a list of URLs in one run: status code, every hop of the redirect chain and the final URL, response time, server and content type, and six key security headers. Plain GET requests only, no proxies, no page content stored.

- **URL**: https://apify.com/gridlinetools/http-status-headers-checker.md
- **Developed by:** [Gridline Tools](https://apify.com/gridlinetools) (community)
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.50 / 1,000 url 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

## HTTP Status and Headers Checker: status codes, redirect chains and security headers for a list of URLs

Paste a list of URLs and get one row per URL with the HTTP status code, every hop of the redirect chain and the final URL, the response time, the `Server` and `Content-Type` headers and six security headers that matter most in audits. The Actor sends a single plain `GET` request per URL, keeps the headers and drops the body, so there are no browsers, no proxies and no stored page content. The table is a regular Apify dataset: download it as JSON, CSV or Excel, or read it through the API.

### What it is good for

- **Broken link and uptime audits:** find 404/410/5xx pages in a sitemap or a list of backlinks.
- **Redirect checks after a migration:** see every hop (301 → 302 → 200), spot loops and chains longer than one hop, confirm that `http://` lands on `https://`.
- **Security header inventory:** list which domains send `Strict-Transport-Security`, `Content-Security-Policy`, `X-Frame-Options`, `X-Content-Type-Options`, `Referrer-Policy` and `Permissions-Policy`, and which do not.
- **Server fingerprinting for SEO or ops:** `Server` and `Content-Type` of the final response for each URL, plus response time in milliseconds.
- **Feeding other tools:** the output is flat JSON, one record per input URL, easy to join with your own spreadsheets or to schedule as a monitoring run.

### How to use it

1. Open the Actor and paste your list of URLs into the `urls` field, one per line (or pass a JSON array through the API).
2. Adjust the options if you need them (`followRedirects`, `includeHeaders`, `timeoutSecs`, `maxConcurrency`); the defaults work for most runs.
3. Click Start. When the run finishes, the status message reads `Checked N of M URLs (X with errors, Y invalid)`.
4. Download the results from the Dataset tab as JSON, CSV or Excel, or read them through the API.

### Input

| Field | Type | Default | Meaning |
|---|---|---|---|
| `urls` | array of strings | small self-test list | Absolute URLs starting with `http://` or `https://`. Exact duplicates (same string) are skipped. Up to 1,000 per run (extra entries are dropped and counted in `SUMMARY`). |
| `followRedirects` | boolean | `true` | Follow up to 10 redirects and report the chain. Set to `false` to record only the first response. |
| `timeoutSecs` | integer 1–60 | `15` | Time limit per URL, including all redirects. |
| `maxConcurrency` | integer 1–50 | `10` | How many URLs are checked in parallel. |
| `includeHeaders` | boolean | `true` | Add the full header map of the final response (`headers` field) to each record. |

Example input:

```json
{
  "urls": [
    "https://example.com",
    "http://github.com",
    "https://example.com/does-not-exist"
  ],
  "followRedirects": true,
  "timeoutSecs": 15
}
```

### Output

One JSON record per input URL in the default dataset (nested fields are expanded into columns in the CSV/Excel export). Example (values illustrative, headers shortened):

```json
{
  "url": "http://github.com",
  "finalUrl": "https://github.com/",
  "statusCode": 200,
  "statusText": "OK",
  "ok": true,
  "redirectCount": 1,
  "redirectChain": [
    { "url": "http://github.com/", "statusCode": 301, "location": "https://github.com/" }
  ],
  "responseTimeMs": 184,
  "server": "GitHub.com",
  "contentType": "text/html; charset=utf-8",
  "contentLength": null,
  "securityHeaders": {
    "strictTransportSecurity": "max-age=31536000; includeSubdomains; preload",
    "contentSecurityPolicy": "default-src 'none'; ...",
    "xFrameOptions": "deny",
    "xContentTypeOptions": "nosniff",
    "referrerPolicy": "origin-when-cross-origin, strict-origin-when-cross-origin",
    "permissionsPolicy": null
  },
  "headers": { "content-type": "text/html; charset=utf-8", "server": "GitHub.com" },
  "error": null,
  "errorMessage": null,
  "checkedAt": "2026-09-06T12:00:00.000Z"
}
```

Field notes:

- `url` is your input exactly as given (credentials, if any, are masked as `user:***@`; a user name without a password as `***@`; a password containing a raw `/`, `\`, `?` or `#` is not recognized as credentials, so percent-encode such characters); `finalUrl` is always the parser-normalized form of the last URL requested (lower-case scheme and host, trailing slash on a bare host).
- `ok` is `true` only for a final status in the 2xx range.
- `statusCode` is `null` when no final response was received (timeout or network error); hops that were already followed stay in `redirectChain`. Look at `error` then.
- `error` is one of `INVALID_URL`, `TIMEOUT`, `NETWORK_ERROR`, `TOO_MANY_REDIRECTS`, or `null` on success. `errorMessage` carries the detail (for example the DNS error text). With `TOO_MANY_REDIRECTS` the record still carries the status and headers of the last `3xx` response.
- `INVALID_URL` (never charged) covers: anything that is not an absolute `http(s)://` URL; URLs with embedded credentials (`https://user:pass@host/`); ports that the Fetch standard refuses to connect to (for example 22, 25, 53, 6000, 10080); and private, loopback, link-local or localhost addresses (`127.0.0.1`, `10.x`, `192.168.x`, `169.254.x`, `localhost`, `[::1]`, …). The Actor checks publicly reachable URLs only. Note that a public host name that resolves to a private address is not detected.
- Redirects to anything other than a public `http(s)://` target (for example `data:`, `ftp:` or a private address) are not followed; the `3xx` response is recorded as the final one.
- `responseTimeMs` covers the whole redirect chain, from the first request to the final response; `checkedAt` is the time the check started.
- `contentLength` is the `Content-Length` of the final response as sent to this client. Requests advertise `Accept-Encoding: gzip, deflate`, so servers that compress report the compressed size (`null` for chunked responses).
- `securityHeaders` values are the raw header strings; `null` means the server did not send that header.
- Header names in `headers` are lower-cased. `Set-Cookie` headers are dropped on purpose: cookie values are session identifiers and the Actor does not collect them.

A `SUMMARY` record is saved to the run's key-value store: `inputEntries` (rows you provided), `inputUrls` (after trimming, de-duplication and the 1,000 cap), `skippedDuplicates`, `droppedEmptyOrNonString`, `truncatedOverLimit`, `processed`, `checked`, `failed`, `invalid`, `notSavedAfterLimit`, `unexpectedErrors`, `stoppedByChargeLimit`. The run's status message repeats the key numbers (for example `Checked 7 of 7 URLs (2 with errors, 1 invalid)`).

### Pricing

This Actor uses Apify pay per event pricing. Each run costs a small fixed start fee plus a price per URL checked; the exact amounts are shown in the Pricing tab. A URL is charged when it was actually requested (timeouts and network errors count, because a request was made). Invalid entries (`error = "INVALID_URL"`, see field notes) are written to the dataset but are **not** charged. There are no additional platform usage fees.

If you set a maximum total charge for the run, the Actor stops when the budget is reached, finishes successfully and says in its status message how many URLs were checked; the remaining URLs are not requested. A limit lower than one Actor start plus one URL makes the run fail immediately with an explanatory message. If the platform migrates the run to another server, the Actor resumes from its saved state, URLs already saved are not checked or charged again.

### Limits and behavior

- Up to 1,000 URLs per run; up to 10 redirects per URL; timeout 1–60 seconds per URL.
- Requests are sent from Apify's shared infrastructure without proxies. Some servers answer `403` or `429` to automated clients; that is reported as a normal result, not retried.
- The Actor sends a descriptive `User-Agent` (`GridlineTools-HTTPStatusChecker`) so site owners can identify it.
- Response bodies are never downloaded, stored or parsed. Only response metadata is saved.
- No login, cookies or JavaScript execution: pages behind authentication return whatever the server sends to an anonymous request.

### FAQ

**Does it follow `meta refresh` or JavaScript redirects?** No. Only HTTP redirects (301, 302, 303, 307, 308 with a `Location` header) are followed.

**Why do I see `403` for a site that opens fine in my browser?** The site blocks automated clients or requires cookies. The status you get is exactly what the server returned to a plain request, useful information in itself.

**Can I check the same list every day?** Yes, schedule the Actor in Apify Console and download the dataset, or connect a webhook to your own system.

**Is the order of results the same as the input?** Records are written as checks finish (parallel processing). Each record carries the original `url`, so you can sort or join on it.

**What counts as one "URL checked"?** One input URL, regardless of how many redirects it took to reach the final response. Timeouts and network errors do count (the request was made); invalid URLs (including private addresses and blocked ports) do not.

**Can I check servers on my own network or on `localhost`?** No. The Actor runs in Apify's cloud and only checks publicly reachable addresses; private, loopback and link-local addresses are rejected as `INVALID_URL` without a request.

### Other Actors by Gridline Tools

Other checks from the same publisher, each with one row per address and the same pricing rule (start fee plus price per checked item):

- [Robots.txt Checker](https://apify.com/gridlinetools/robots-txt-checker): Test a list of URLs against each site's robots.txt for a chosen user agent (RFC 9309).
- [RDAP Domain Lookup and Expiry Checker](https://apify.com/gridlinetools/rdap-domain-lookup): Query RDAP registries for a list of domains and get registrar, creation and expiry dates, EPP statuses, name servers and DNSSEC in one table.
- [AI Crawler Access Audit](https://apify.com/gridlinetools/ai-crawler-access-audit): See which AI and search crawlers (GPTBot, ClaudeBot, Google-Extended, CCBot and 150+ more) each site allows or blocks in robots.txt, and whether llms.txt is published.

### Disclaimer

This Actor provides technical metadata about publicly reachable URLs. You are responsible for making sure that your use complies with applicable law and with the terms of service of the sites you check. The Actor comes without any availability guarantee. Output data is provided as-is for your own use; it does not contain personal data by design (cookies are never stored and credentials in URLs are masked).

# Actor input Schema

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

Absolute, publicly reachable URLs starting with http:// or https://, one per line. Exact duplicates are skipped. Maximum 1,000 per run. Private/localhost addresses, embedded credentials and blocked ports are reported as INVALID_URL and not charged. The default list is a small self-test (Apify also runs it automatically every day to verify the Actor works).

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

Follow 3xx redirects (up to 10 hops) and report the whole chain plus the final URL. Turn off to record only the first response.

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

Maximum time for one URL including all redirects. On timeout the record gets error = TIMEOUT.

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

How many URLs are checked in parallel. Lower it if you check many URLs on one host.

## `includeHeaders` (type: `boolean`):

Add the full header map of the final response to each record (field headers). Security headers and Server/Content-Type are always included.

## Actor input object example

```json
{
  "urls": [
    "https://example.com",
    "http://github.com",
    "https://example.com/does-not-exist"
  ],
  "followRedirects": true,
  "timeoutSecs": 15,
  "maxConcurrency": 10,
  "includeHeaders": true
}
```

# Actor output Schema

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

Default dataset: one record per input URL.

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

SUMMARY record in the default key-value store: input, processed, checked, failed and invalid counters, charge-limit flags.

# 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 = {};

// Run the Actor and wait for it to finish
const run = await client.actor("gridlinetools/http-status-headers-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 = {}

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

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,gridlinetools/http-status-headers-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/jHBtxJAvr2d84zLWs/builds/4VyiB3wFGj11OJUez/openapi.json
