# HTTP Request Runner (`cyber1nj3ct0r/http-request-runner`) Actor

Send many HTTP requests to public web addresses and APIs in one run: GET, POST, PUT, PATCH, DELETE, HEAD or OPTIONS with your own headers and body. Each request gives one row with status, headers, body, parsed JSON, redirect chain and timing. Secret field for API keys.

- **URL**: https://apify.com/cyber1nj3ct0r/http-request-runner.md
- **Developed by:** [Sanira Botheju](https://apify.com/cyber1nj3ct0r) (community)
- **Categories:** Developer tools, Automation, Integrations
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.35 / 1,000 requests

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

### What does HTTP Request Runner do?

HTTP Request Runner sends a list of HTTP requests to public web addresses and APIs, and saves what came back for each one. Use **GET, POST, PUT, PATCH, DELETE, HEAD or OPTIONS**, add your own headers and a JSON or text body, and get one row per request with the **status, headers, body, parsed JSON, redirect chain and response time**, ready to export to Excel, CSV or JSON, or to pass on to another app.

- Sends hundreds or thousands of requests in one run, ten at a time by default (up to 50)
- Plain list of addresses, or a list of request objects where every request has its own method, headers, query and body
- Shared defaults for method, headers and body that each request can override
- A secret field for your API key or token, stored encrypted and never written to the run log or the results
- Follows redirects and shows every step; requests that fail are repeated with a pause (rules below)
- Parses JSON answers for you, and cuts very long answers at a size limit you choose
- Requests go only to the addresses you list, and only to public addresses: localhost, private networks and cloud metadata addresses are refused, also after a redirect

### What data do you get?

| Field | Example |
|---|---|
| Place in your list | `2` |
| Address and method | `https://api.example.com/items`, `POST` |
| Status | `201`, with `ok: true` for any 2xx |
| Response headers | `{"content-type": "application/json", ...}` |
| Answer text | the body as text, cut at your size limit |
| Parsed JSON | `{"id": 17, "name": "test"}` |
| Redirects | each step with its status and target, and the final address |
| Size, time, tries | `212` bytes, `184` ms, `1` try |
| Error | `null`, or a code such as `TIMEOUT`, `DNS_NOT_FOUND` or `BLOCKED_ADDRESS` with a message |

The request's own headers and body are never copied into the results.

### How to send many HTTP requests

1. Under **Web addresses**, add the addresses one by one, paste many with **Bulk edit**, or link a text file with one address per line. For different methods, headers or bodies per request, fill **Requests (JSON)** instead (see below).
2. Choose the **Method** and, if every request needs them, **Headers**, **Authorization (secret)** and **Body**.
3. Press **Start**, then open the **Responses** table and export it, or read it through the API.

The pre-filled example sends a GET and a POST with a JSON body to `https://api.apify.com/v2/browser-info`, an Apify endpoint that answers with a description of the request it received. Use it to see what the tool sends.

### How much does it cost to send requests?

You pay for results, not for time: **$0.50 per 1,000 requests**. Apify subscribers on the Silver plan pay $0.43 and on the Gold plan $0.35 per 1,000 requests. Each run also has a start fee of $0.00005. Every request in your list gives one row and is charged once, including a request that failed or whose address was not valid. An address that appears twice in your list is sent twice.

| Requests sent | Cost |
|---|---|
| 100 | $0.05 |
| 1,000 | $0.50 |
| 10,000 | $5.00 |
| 100,000 | $50.00 |

Apify's free plan includes $5 of credit each month, which covers about 10,000 requests. Set a maximum cost for the run in Apify Console and the tool stops there and keeps what it has done. Requests that were never started are not sent.

### Input

You need at least one address, under **Web addresses** or in **Requests (JSON)**. Everything else has a default.

- **Web addresses**: sent first, with the method, headers and body below. An entry may also carry its own `method` (other than GET), `payload` (the body) and `headers`. A `method` of `GET` on an entry is read as "not set", because the Console's address editor can write it into every entry; use **Requests (JSON)** to force GET for one request.
- **Requests (JSON)**: a list of objects, sent after the addresses. Each object has a `url` and may have `method`, `headers`, `query`, `body` and `userData`:

```json
[
    { "url": "https://api.example.com/items", "method": "POST", "body": { "name": "test" } },
    { "url": "https://api.example.com/search", "query": { "q": "shoes", "page": 2 }, "headers": { "Accept": "application/json" } },
    { "url": "https://api.example.com/items/17", "method": "DELETE", "userData": { "id": 17 } }
]
```

- **Method**, **Headers** and **Body** apply to every request that does not set its own. A request removes a shared header by giving `null` for it. The shared body is sent only with POST, PUT, PATCH and DELETE.
- **Authorization (secret)**: the full value of the `Authorization` header, for example `Bearer YOUR_TOKEN`. It is encrypted, left out of the log and the results, and not passed on to another website after a redirect.
- **Follow redirects**, **Maximum redirects** (up to 10) and **Time limit per request** (30 seconds by default).
- **Retries**: how many more tries after a network error, a 429 or a server error. Default 1, up to 5.
- **Largest answer kept** (1 MB by default, up to 4 MB), **Include the answer text**, **Parse JSON answers** and **Include response headers**.
- **Requests at the same time** (10 by default, up to 50) and **per website** (4 by default, up to 10).

A body that is an object or a list is sent as JSON (`application/json`). Text is sent exactly as written; text that is a JSON object or list also gets `application/json` unless you set a Content-Type. Set `Content-Type: application/x-www-form-urlencoded` and give an object to send a form.

### Output example

```json
{
    "position": 2,
    "url": "https://api.example.com/items",
    "method": "POST",
    "status": 201,
    "statusText": "Created",
    "ok": true,
    "finalUrl": "https://api.example.com/items",
    "redirectCount": 0,
    "redirectChain": [],
    "contentType": "application/json",
    "contentLength": 31,
    "bodyBytes": 31,
    "truncated": false,
    "timeMs": 184,
    "attempts": 1,
    "requestedAt": "2026-10-16T09:30:00.000Z",
    "error": null,
    "headers": { "content-type": "application/json; charset=utf-8", "content-length": "31" },
    "body": "{\"id\":17,\"name\":\"test\"}",
    "json": { "id": 17, "name": "test" }
}
```

A request that gets no answer has `status: null` and an `error`, for example `DNS_NOT_FOUND`, `CONNECTION_REFUSED`, `TIMEOUT`, `TLS_CERTIFICATE_EXPIRED`, `TOO_MANY_REDIRECTS` or `BLOCKED_ADDRESS`. Entries that cannot be sent at all (not an address, an unknown method, a GET with a body) become a row with `attempts: 0` and an error such as `INVALID_URL`, `INVALID_METHOD` or `INVALID_REQUEST`. A short summary of the run is saved as `SUMMARY`.

### What can you use it for?

- Test an API: call many endpoints with different parameters and compare status codes, headers and answers
- Send bulk POST, PUT or DELETE requests to your own API, for example to import records from a spreadsheet, with `userData` to match each row to your record
- Trigger webhooks and automation endpoints, one request per item
- Check that a list of endpoints is up, and how fast they answer, on a schedule in Apify Console
- Fetch the JSON of many API addresses into one table for analysis

### Use it from the API

```bash
curl -X POST "https://api.apify.com/v2/acts/cyber1nj3ct0r~http-request-runner/run-sync-get-dataset-items?token=YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"requests": [{"url": "https://api.example.com/items", "method": "POST", "body": {"name": "test"}}], "authorization": "Bearer YOUR_API_KEY"}'
```

The same works from Python, JavaScript, Make, Zapier and n8n through Apify's integrations. When you start a run through the API, put keys in the `authorization` field and not in `headers`: only the `authorization` field is stored encrypted.

### FAQ

#### Can I set my own User-Agent?

No. The tool always sends `Mozilla/5.0 (compatible; http-request-runner/1.0; +https://apify.com/cyber1nj3ct0r/http-request-runner)`, so the owner of a website or API can see who is calling and where to complain. A request or a shared header with a `User-Agent` is refused. All other headers can be set: `Authorization`, `Content-Type`, `Accept`, API key headers and custom headers. Headers that belong to the connection itself (`Host`, `Content-Length`, `Connection` and similar) are set by the tool.

#### Are my API keys safe?

Put the key in **Authorization (secret)**; Apify stores that field encrypted. The tool never writes header values or bodies to the run log, and the result rows do not contain the request's headers or body. Two things to keep in mind. Fields such as **Headers** and **Requests (JSON)** are stored as plain input, so do not put secrets there. And the address of each request is saved in the results, so do not put keys in the query string if you can send them as a header.

#### Does the key follow a redirect to another website?

No. When a redirect leads to another website, only `Accept` and `Accept-Language` are passed on. The key, cookies and your other headers stay with the website you named. If a POST, PUT or DELETE with a body is redirected with 307 or 308 to another website, the body is not sent there: the row shows the redirect and the error `REDIRECT_NOT_FOLLOWED`. A redirect from `http` to `https` on the same host counts as the same website.

#### Why can the tool not reach my local or internal server?

It runs in a data centre, and it refuses `localhost`, private ranges such as `10.x.x.x`, `172.16.x.x` and `192.168.x.x`, link-local and cloud metadata addresses (`169.254.x.x`) and names that point to them, including after a redirect. Ports that are not used for web traffic, such as 25 or 6667, are refused too. That protects your own network and others. The API you call must be reachable from the public internet.

#### When is a request repeated?

After a network error, a 429 and the server errors 500, 502, 503 and 504, up to the number under **Retries**, with a pause that doubles each time. A `Retry-After` header is respected up to 30 seconds; a longer wait is not waited for and the 429 is returned. POST and PATCH requests may already have been carried out when an answer is lost, so they are only repeated after 429, 503, or when no connection could be made. The `attempts` field shows how often a request was tried.

#### Why is the order different from my list?

Requests run several at a time, so rows arrive in the order the requests finish. Sort by **Place in list** (`position`) to get your order back, or give each request a `userData` value.

#### Why is the answer cut or empty?

Answers longer than **Largest answer kept** are cut and marked `truncated: true`; a cut JSON answer cannot be parsed. Files such as images, PDFs and archives are not read: the row has a `warning` and the size in `contentLength`. HEAD requests and 204 answers have no body.

#### Is it allowed?

You decide where the requests go. Send them only to services you own or run, or that allow it in their terms, and respect rate limits. The tool is not meant for load testing: it sends at most 50 requests at once and 10 to the same website, it does not use proxies, and it does not try to get round blocks, logins or CAPTCHAs. You are responsible for what you send and for how you use the answers.

#### Something does not work

Open an issue on the **Issues** tab with the request that shows the problem (leave out keys and private data). Every report is read and answered.

# Actor input Schema

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

The addresses to send requests to, for example <code>https://api.example.com/items/1</code>. Add them one by one, paste many at once with <b>Bulk edit</b>, or link a text file with one address per line. They are sent with the method, headers and body set below. An entry can also carry its own <code>method</code> (other than GET), <code>payload</code> (the body) and <code>headers</code>, as in the example. Every entry is sent, also when it appears twice. For many requests that differ from each other, use <b>Requests (JSON)</b> instead.

## `requests` (type: `array`):

A list of request objects, sent after the addresses above. Each object has a <code>url</code> and may have <code>method</code>, <code>headers</code>, <code>query</code> (an object that is added to the address), <code>body</code> (text, or any JSON value that is sent as JSON) and <code>userData</code> (anything you want back in the result row). Settings you leave out come from the fields below. Example: <code>\[{"url": "https://api.example.com/items", "method": "POST", "body": {"name": "test"}}]</code>

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

The HTTP method for every request that does not set its own.

## `headers` (type: `object`):

Headers sent with every request, for example <code>{"Accept": "application/json", "X-Api-Version": "2"}</code>. A request can replace a header, or remove it by giving <code>null</code>. The <code>User-Agent</code> cannot be changed: the tool always tells the site who is calling. This field is not encrypted: put keys and passwords in <b>Authorization</b> below.

## `authorization` (type: `string`):

The complete value of the <code>Authorization</code> header for every request, for example <code>Bearer YOUR_TOKEN</code>. It is stored encrypted, is never written to the run log or the results, and is not passed on when a redirect leads to another website.

## `body` (type: `string`):

The body sent with every POST, PUT, PATCH and DELETE request that does not set its own. Text is sent exactly as written. Text that is a JSON object or list is sent with the content type <code>application/json</code> unless you set another one under Headers. GET and HEAD requests never carry a body.

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

Follow 301, 302, 303, 307 and 308 redirects and report each step. Turn it off to get the redirect itself as the result. Redirects to private or local addresses are never followed.

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

How many redirects are followed for one request before it is reported as TOO_MANY_REDIRECTS.

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

How long one request may take, redirects and reading the answer included, before it is counted as not answering.

## `retries` (type: `integer`):

How many more times a request is tried after a network error, a 429 (too many requests) or a server error (500, 502, 503, 504), with a growing pause between tries. A pause the server asks for with <code>Retry-After</code> is respected up to 30 seconds. POST and PATCH requests are only repeated after 429, 503 or when no connection could be made, because the first try may already have been carried out. Set 0 to try each request once.

## `maxBodyKb` (type: `integer`):

The part of an answer that is kept, in kilobytes. A longer answer is cut and the result says <code>truncated: true</code>. A cut JSON answer cannot be parsed.

## `includeBody` (type: `boolean`):

Add the body of the answer as text. Turn it off together with <b>Parse JSON answers</b> to send requests and keep only the status, which also keeps the results small.

## `parseJson` (type: `boolean`):

When an answer is JSON, add it as a parsed value in <code>json</code>, ready to use in a table or in your own code.

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

Add every header of the answer to the result, for example <code>content-type</code>, <code>x-ratelimit-remaining</code> or <code>set-cookie</code>.

## `concurrency` (type: `integer`):

How many requests are in flight at once, in total. A higher number finishes a long list sooner. Large answers need more memory when this is high.

## `perHostConcurrency` (type: `integer`):

How many of those may go to the same website at once. Keep it low for sites you do not run. The default is 4.

## Actor input object example

```json
{
  "urls": [
    {
      "url": "https://api.apify.com/v2/browser-info?source=http-request-runner",
      "method": "GET"
    },
    {
      "url": "https://api.apify.com/v2/browser-info",
      "method": "POST",
      "headers": {
        "Content-Type": "application/json"
      },
      "payload": "{\"hello\": \"world\"}"
    }
  ],
  "method": "GET",
  "followRedirects": true,
  "maxRedirects": 10,
  "timeoutSecs": 30,
  "retries": 1,
  "maxBodyKb": 1024,
  "includeBody": true,
  "parseJson": true,
  "includeHeaders": true,
  "concurrency": 10,
  "perHostConcurrency": 4
}
```

# Actor output Schema

## `responses` (type: `string`):

One record per request with the status, headers, body, parsed JSON, redirect chain and timing of the answer.

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

Counts for the run: requests sent, succeeded, redirected, client errors, server errors, failed and invalid.

# 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": [
        {
            "url": "https://api.apify.com/v2/browser-info?source=http-request-runner",
            "method": "GET"
        },
        {
            "url": "https://api.apify.com/v2/browser-info",
            "method": "POST",
            "headers": {
                "Content-Type": "application/json"
            },
            "payload": "{\"hello\": \"world\"}"
        }
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("cyber1nj3ct0r/http-request-runner").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": [
        {
            "url": "https://api.apify.com/v2/browser-info?source=http-request-runner",
            "method": "GET",
        },
        {
            "url": "https://api.apify.com/v2/browser-info",
            "method": "POST",
            "headers": { "Content-Type": "application/json" },
            "payload": "{\"hello\": \"world\"}",
        },
    ] }

# Run the Actor and wait for it to finish
run = client.actor("cyber1nj3ct0r/http-request-runner").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": [
    {
      "url": "https://api.apify.com/v2/browser-info?source=http-request-runner",
      "method": "GET"
    },
    {
      "url": "https://api.apify.com/v2/browser-info",
      "method": "POST",
      "headers": {
        "Content-Type": "application/json"
      },
      "payload": "{\\"hello\\": \\"world\\"}"
    }
  ]
}' |
apify call cyber1nj3ct0r/http-request-runner --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,cyber1nj3ct0r/http-request-runner"
        }
    }
}
```

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/xPigAjk0u7E9seo0j/builds/DdEa3A8t50PdgEJ3L/openapi.json
