# HTTP Request Runner — Bulk & Proxied (`seemuapps/http-request-runner`) Actor

Send hundreds of GET, POST, PUT or DELETE requests through datacenter or residential proxies and get status, headers, body and timing per URL.

- **URL**: https://apify.com/seemuapps/http-request-runner.md
- **Developed by:** [Andrew](https://apify.com/seemuapps) (community)
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

$3.00 / 1,000 successful requests

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?

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

## HTTP Request Runner — Bulk & Proxied

Send hundreds or thousands of GET, POST, PUT, PATCH, DELETE or HEAD requests in one run — through Apify datacenter or residential proxies, with browser-like TLS fingerprints and headers, automatic retries with IP rotation, and configurable concurrency. Every request comes back as one dataset row with status code, response headers, body and timing.

**Blocked and failed requests are free.** You only pay for rows that return a 2xx or 3xx status. A 403, 429, 5xx, timeout or DNS failure is still recorded in the dataset so you can see what happened, but it is never charged.

### What you get

One row per request, containing:

- `statusCode`, `statusText`, `ok` — the HTTP outcome (`ok` is true for 2xx/3xx)
- `headers` — the full response header object (optional)
- `body` — the response as text, parsed JSON or base64 (for images, PDFs and other binaries)
- `bodyLength`, `bodyTruncated`, `bodyParseError` — so you always know whether you got the whole thing
- `durationMs` (final attempt) and `totalDurationMs` (including retries), `attempts`, `redirectCount`, `finalUrl`
- `proxyUsed` — `datacenter`, `residential`, `custom` or `none`
- `error` — a clear message for blocked, timed-out, invalid or refused requests
- `keyValueStoreUrl` — a link to the full, untruncated body when you enable saving to the key-value store
- `index` and `label` — to match rows back to your input, even though results stream in as they finish

### Use cases

- **API scraping and bulk data pulls** — hit paginated JSON endpoints, sitemaps or feeds in parallel and export straight to CSV, JSON or Google Sheets
- **Uptime, geo and block testing** — check how a list of URLs responds from datacenter vs residential IPs, or from a specific country
- **Webhook and form submission** — POST JSON or form bodies to hundreds of endpoints with per-request headers and payloads
- **Link checking and redirect auditing** — collect status codes, redirect chains and final URLs for every link on your site
- **Pre-flight for scrapers** — see which targets serve real content, which ones block, and how long they take before you build a full crawler

### How to use

1. **Simple mode:** paste your URLs into **URLs** — one request per line, all using the shared **Method**, **Headers** and **Body**.
2. **Advanced mode:** put an array of request objects into **Requests**:
   ```json
   [
     { "url": "https://api.example.com/items?page=1", "label": "page-1" },
     { "url": "https://api.example.com/items", "method": "POST", "headers": { "Authorization": "Bearer …" }, "body": { "name": "test" }, "label": "create" }
   ]
   ```
   A JSON object `body` is sent as `application/json` automatically. Both lists can be used together.
3. Pick a **Proxy**. Apify datacenter proxy is the default; choose the `RESIDENTIAL` group (and optionally a country) for targets that block datacenter IPs, or paste your own proxy URLs. Every retry rotates to a fresh proxy session.
4. Tune **Concurrency** (1–50), **Max retries**, **Retry on statuses** and **Request timeout** for your target.
5. Choose a **Response format**: `text`, `json` (parsed into an object) or `base64` (binary files).
6. Run the actor. Rows appear in the **Dataset** tab as requests complete — sort by `index` to restore input order.

#### Large or binary responses

Bodies are truncated at **Max body characters** (default 500,000). Turn on **Save bodies to key-value store** to also keep every complete response as a record named `<index>-<url-slug>` in the run's key-value store; the row's `keyValueStoreUrl` links straight to it.

#### Long runs

If the run approaches its timeout, the actor stops starting new requests, logs which indexes were not attempted and writes them to the `NOT_ATTEMPTED` record in the key-value store so you can re-run just those. Up to 10,000 requests are accepted per run; use **Max requests** to process only the first N.

### Output format

```json
{
  "index": 0,
  "label": "page-1",
  "url": "https://api.example.com/items?page=1",
  "finalUrl": "https://api.example.com/items?page=1",
  "method": "GET",
  "statusCode": 200,
  "statusText": "OK",
  "ok": true,
  "headers": { "content-type": "application/json", "cache-control": "no-cache" },
  "contentType": "application/json",
  "body": { "items": [ { "id": 1 } ], "next": 2 },
  "bodyLength": 4821,
  "bodyTruncated": false,
  "bodyParseError": null,
  "durationMs": 412,
  "totalDurationMs": 412,
  "attempts": 1,
  "redirectCount": 0,
  "proxyUsed": "datacenter",
  "error": null,
  "keyValueStoreUrl": null,
  "fetchedAt": "2026-09-06T03:21:44.120Z"
}
```

A blocked request looks like this and is **not charged**:

```json
{
  "index": 1,
  "url": "https://example.com/protected",
  "statusCode": 403,
  "statusText": "Forbidden",
  "ok": false,
  "attempts": 3,
  "proxyUsed": "residential",
  "error": "HTTP 403 Forbidden"
}
```

### Pricing

Pay per successful result: one `request-result` event for each row with a 2xx or 3xx status. Rows with 4xx/5xx responses, network errors, timeouts, invalid URLs or refused targets cost nothing.

### Limits and safety

- Only `http://` and `https://` URLs are accepted.
- Requests to localhost, private networks (10/8, 172.16/12, 192.168/16, 169.254/16, IPv6 loopback and ULA) and cloud metadata hosts are refused with a clear per-row error.
- GET and HEAD requests never send a body.
- Redirects are followed up to 10 hops.

# Actor input Schema

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

One request per URL, all using the shared Method, Headers and Body below. Combine with Requests (advanced) if you need per-request settings. Only http:// and https:// URLs are accepted.

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

Array of request objects: { "url": "https://…", "method": "POST", "headers": { … }, "body": "…" | { … }, "label": "my-tag" }. Every field except url is optional and falls back to the shared settings below. A JSON object body is stringified and sent as application/json. Merged with URLs; both lists are processed.

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

HTTP method used for every URL in simple mode, and for advanced requests that do not set their own method.

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

Shared request headers as a JSON object, e.g. { "Authorization": "Bearer …", "Accept": "application/json" }. Per-request headers in Requests override these. When Browser-like headers is on, these are layered on top of the generated browser headers.

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

Shared request body sent with POST, PUT, PATCH and DELETE requests (ignored for GET and HEAD). Set a Content-Type header to describe it, e.g. application/json or application/x-www-form-urlencoded.

## `useBrowserHeaders` (type: `boolean`):

Generate a realistic Chrome/Firefox header set and TLS fingerprint for each request so anti-bot systems see a normal browser. Turn off to send only the headers you specify (plus a generic user agent).

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

Route requests through Apify Proxy (datacenter by default; pick the RESIDENTIAL group and a country for hard targets) or your own proxy URLs. Each retry rotates to a fresh proxy session. Turn off to request directly from the actor's IP.

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

How many requests run in parallel (1-50). Lower it if the target rate-limits you.

## `maxRetries` (type: `integer`):

Extra attempts after a network error, timeout, or a status code listed in Retry on statuses. Each retry uses a new proxy session and backs off (1s, 2s, 3s…).

## `retryOnStatuses` (type: `array`):

HTTP status codes that trigger a retry with a fresh proxy session. Network errors and timeouts are always retried.

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

Maximum time for a single attempt, including connection, redirects and body download.

## `responseFormat` (type: `string`):

How the response body is stored in the dataset. JSON parses the body into an object (falls back to text with bodyParseError if it is not valid JSON). Base64 is for images, PDFs and other binary responses.

## `maxBodyChars` (type: `integer`):

Truncate the stored body to this many characters (bodyTruncated is set to true). 0 = no truncation (dataset items are still limited to ~9 MB). Use Save bodies to key-value store to keep the full response.

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

Store the full response header object on each row.

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

Follow 3xx redirects (up to 10 hops) and report the final URL. Turn off to capture the redirect response itself.

## `saveToKeyValueStore` (type: `boolean`):

Also save every full, untruncated response body as a record named <index>-<url-slug> in the run's default key-value store, and put its URL in keyValueStoreUrl on the row. Handy for large pages and binary files.

## `maxItems` (type: `integer`):

Only process the first N requests (URLs first, then advanced requests). 0 = all. Hard cap of 10,000 requests per run.

## Actor input object example

```json
{
  "urls": [
    "https://httpbin.org/get",
    "https://example.com",
    "https://api.ipify.org?format=json"
  ],
  "requests": [
    {
      "url": "https://httpbin.org/post",
      "method": "POST",
      "body": {
        "hello": "world"
      },
      "label": "post-example"
    }
  ],
  "method": "GET",
  "headers": {},
  "useBrowserHeaders": true,
  "proxyConfiguration": {
    "useApifyProxy": true
  },
  "concurrency": 10,
  "maxRetries": 2,
  "retryOnStatuses": [
    "403",
    "429",
    "500",
    "502",
    "503",
    "504"
  ],
  "timeoutSecs": 30,
  "responseFormat": "text",
  "maxBodyChars": 500000,
  "includeHeaders": true,
  "followRedirects": true,
  "saveToKeyValueStore": false,
  "maxItems": 0
}
```

# Actor output Schema

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

One row per request. Fields: index, label, url, finalUrl, method, statusCode, statusText, ok, headers, contentType, body, bodyLength, bodyTruncated, bodyParseError, durationMs, totalDurationMs, attempts, redirectCount, proxyUsed, error, keyValueStoreUrl, fetchedAt. Rows are pushed as requests finish; sort by index to restore input order.

## `bodies` (type: `string`):

When Save bodies to key-value store is on, every full response body is stored here as <index>-<url-slug>.

# 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://httpbin.org/get",
        "https://example.com",
        "https://api.ipify.org?format=json"
    ],
    "requests": [
        {
            "url": "https://httpbin.org/post",
            "method": "POST",
            "body": {
                "hello": "world"
            },
            "label": "post-example"
        }
    ],
    "headers": {},
    "proxyConfiguration": {
        "useApifyProxy": true
    },
    "concurrency": 10,
    "maxRetries": 2,
    "retryOnStatuses": [
        "403",
        "429",
        "500",
        "502",
        "503",
        "504"
    ],
    "timeoutSecs": 30,
    "maxBodyChars": 500000,
    "maxItems": 0
};

// Run the Actor and wait for it to finish
const run = await client.actor("seemuapps/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": [
        "https://httpbin.org/get",
        "https://example.com",
        "https://api.ipify.org?format=json",
    ],
    "requests": [{
            "url": "https://httpbin.org/post",
            "method": "POST",
            "body": { "hello": "world" },
            "label": "post-example",
        }],
    "headers": {},
    "proxyConfiguration": { "useApifyProxy": True },
    "concurrency": 10,
    "maxRetries": 2,
    "retryOnStatuses": [
        "403",
        "429",
        "500",
        "502",
        "503",
        "504",
    ],
    "timeoutSecs": 30,
    "maxBodyChars": 500000,
    "maxItems": 0,
}

# Run the Actor and wait for it to finish
run = client.actor("seemuapps/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": [
    "https://httpbin.org/get",
    "https://example.com",
    "https://api.ipify.org?format=json"
  ],
  "requests": [
    {
      "url": "https://httpbin.org/post",
      "method": "POST",
      "body": {
        "hello": "world"
      },
      "label": "post-example"
    }
  ],
  "headers": {},
  "proxyConfiguration": {
    "useApifyProxy": true
  },
  "concurrency": 10,
  "maxRetries": 2,
  "retryOnStatuses": [
    "403",
    "429",
    "500",
    "502",
    "503",
    "504"
  ],
  "timeoutSecs": 30,
  "maxBodyChars": 500000,
  "maxItems": 0
}' |
apify call seemuapps/http-request-runner --silent --output-dataset

```

## MCP server setup

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