# Bulk HTTP Request: send many API calls from a list (`ucchiii/bulk-http-request`) Actor

Send many HTTP requests from a list (GET, POST, PUT, PATCH, DELETE, HEAD with your headers and body) and get status, headers, JSON or text body, timing and errors for each in a dataset. Internal addresses are blocked. Empty input = free demo. $1 per 1,000 requests.

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

## Pricing

from $0.70 / 1,000 request completeds

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

## Bulk HTTP Request — send many API calls from a list

**For developers, data and ops teams who have a list of API calls to make and don't want to write a loop.** Give it
URLs (or full requests with method, headers and body) and get one dataset row per request: **status code, response
headers, JSON or text body, timing and errors**. Use it to call an API for hundreds of IDs, fire webhooks, POST many
records, check endpoints or HEAD a list of URLs. **$1 per 1,000 completed requests.** Leave the input empty for a free demo.

### Use cases

- Call an API once per ID or record (GET/POST/PUT/PATCH/DELETE) and collect every response in a dataset
- Send many webhooks or notifications and see which ones were accepted (2xx) or rejected
- Check endpoints, status codes and response headers with HEAD or GET across a long list
- Fetch many JSON endpoints and get the parsed JSON, ready for a spreadsheet or the next Actor

### How it works

- **Two input styles:** `requests` — a list of `{url, method, headers, body}` objects — or a simple `urls` list that shares one `method`, `headers` and `body`. Per-request values override the shared ones.
- Body can be a string (sent as is) or any JSON value (sent as JSON with `Content-Type: application/json`)
- `concurrency` up to 20, `timeoutSecs`, `retries` (up to 5; POST and PATCH are only retried when no connection was made, so nothing is sent twice) and `followRedirects` (up to 10 hops)
- Response body is cut at `maxBodyBytes` (default 100 KB) and flagged with `bodyTruncated`; `parseJson` returns valid JSON as the `json` field
- Response headers: `selected`, `all` or `none`
- Up to 10,000 requests per run. A per-host limit (5 connections, 0.05 s between starts) keeps it polite
- Run totals are saved as `SUMMARY` in the key-value store

### Safe by design

- **Internal addresses are never contacted.** Hostnames are resolved and anything that points to localhost, private ranges, link-local (such as the cloud metadata address 169.254.169.254), IPv6 loopback/unique-local and similar is refused — also at every redirect hop. Such rows have `error: "blocked_private_address"`
- **Secrets stay out of logs and output.** Logs show only host names. `Authorization`, `Cookie`, API-key and token headers are shown as `[REDACTED]` in the output's `requestHeaders`, and `Set-Cookie` is redacted in response headers. Authorization and Cookie are dropped on redirects to another host
- Note: headers you enter are part of the run input; use your own credentials and keep the run private

### Pricing

**$0.001 per completed request.** A request is completed when the server answers — any HTTP status counts (a 404 or 500 is charged, a retry is not). Network errors (timeouts, DNS failures), invalid requests and requests blocked by the internal-address guard are **not charged**. Free demo on empty input (not charged).

### Input

```json
{
  "requests": [
    { "url": "https://httpbin.org/get", "method": "GET" },
    { "url": "https://httpbin.org/post", "method": "POST", "body": { "hello": "world" } }
  ],
  "headers": { "Accept": "application/json" },
  "parseJson": true,
  "concurrency": 5
}
```

Or `{ "urls": ["https://example.com/a", "https://example.com/b"], "method": "HEAD" }`.

### Output

```json
{
  "index": 1, "url": "https://httpbin.org/post", "method": "POST", "statusCode": 200, "ok": true,
  "headers": { "content-type": "application/json", "content-length": "512" },
  "body": null, "json": { "json": { "hello": "world" } }, "bodyBytes": 512, "bodyTruncated": false,
  "elapsedMs": 431, "finalUrl": "https://httpbin.org/post", "redirectCount": 0, "attempts": 1,
  "requestHeaders": { "Accept": "application/json", "Content-Type": "application/json" }, "error": null
}
```

`ok` is true for a 2xx final status. When no response arrives, `statusCode` is `null` and `error` is e.g. `timeout`,
`dns_failure`, `connection_error: ...`, `too_many_redirects`, `redirect_loop`, `invalid_request` or `blocked_private_address`.
Rows are written as they finish; use `index` to restore the input order.

### Notes

You are responsible for what you send: only call servers and APIs you are allowed to use, and follow their terms of
service and rate limits. The Actor sends exactly the requests you list; it does not crawl or follow links.
Known limit: the internal-address check happens just before connecting; an attacker-controlled DNS server that changes its answer between the check and the connection (DNS rebinding) is not covered.

### More tools from the same developer

- [Bulk URL Status Checker](https://apify.com/ucchiii/url-status-checker) — bulk HTTP status and redirect-chain checker for URL lists
- [Website Technology Detector](https://apify.com/ucchiii/website-technology-detector) — tech stack of websites in bulk: CMS, frameworks, analytics and tag IDs, CDN, consent tools
- [Sitemap URL Extractor](https://apify.com/ucchiii/sitemap-urls) — list every URL of a website from its sitemaps
- [HTML to Markdown Converter for LLM and RAG](https://apify.com/ucchiii/html-to-markdown) — clean Markdown from URLs, raw HTML or datasets for LLM and RAG
- [Dataset Transform](https://apify.com/ucchiii/dataset-transform) — flatten, filter, rename and convert any Apify dataset, export CSV / JSON / JSONL with a link

# Actor input Schema

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

List of requests to send. Each item is an object with url (required), method (GET, POST, PUT, PATCH, DELETE or HEAD), headers (object) and body (string, or any JSON value that is sent as JSON). Missing method, headers and body fall back to the shared settings below. Leave this and the URL list empty for a free demo.

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

Optional shortcut: plain URLs that are all sent with the shared method, headers and body below. Added after the requests above.

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

HTTP method for the simple URL list, and for request items that do not set their own.

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

Optional headers sent with every request, for example {"Authorization": "Bearer ..."}. Values such as Authorization and Cookie are never written to logs and are shown as \[REDACTED] in the output. Use your own credentials only; they are part of the run input, so keep the run private.

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

Optional request body for the simple URL list and for items without their own body. Sent as text; if it is valid JSON and you set no Content-Type, it is sent as application/json.

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

How many requests are in flight at the same time (1 to 20). At most 5 connections per host are used at a time.

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

Timeout for each request attempt.

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

How many times a failed request is retried (connection errors and timeouts; HTTP 429/502/503/504 for GET, HEAD, PUT and DELETE). POST and PATCH are retried only when no connection could be made, so nothing is sent twice. Retries are not charged.

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

Follow redirects (up to 10). Every hop is checked against the internal-address block. Authorization and Cookie headers are dropped when a redirect goes to another host.

## `maxBodyBytes` (type: `integer`):

Response bodies longer than this are cut off and bodyTruncated is set to true. 100000 is about 100 KB; 0 stores no body.

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

If on, a complete response body that is valid JSON is returned as the json field (and body is left empty). Otherwise the body is returned as text.

## `responseHeaders` (type: `string`):

Which response headers to include in the output. Set-Cookie and similar secret headers are always shown as \[REDACTED].

## Actor input object example

```json
{
  "requests": [
    {
      "url": "https://httpbin.org/get",
      "method": "GET"
    },
    {
      "url": "https://httpbin.org/post",
      "method": "POST",
      "body": {
        "hello": "world"
      }
    },
    {
      "url": "https://api.github.com/zen",
      "method": "GET"
    }
  ],
  "method": "GET",
  "headers": {
    "Accept": "application/json"
  },
  "concurrency": 5,
  "timeoutSecs": 30,
  "retries": 2,
  "followRedirects": true,
  "maxBodyBytes": 100000,
  "parseJson": false,
  "responseHeaders": "selected"
}
```

# Actor output Schema

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

No description

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

No description

# 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 = {
    "requests": [
        {
            "url": "https://httpbin.org/get",
            "method": "GET"
        },
        {
            "url": "https://httpbin.org/post",
            "method": "POST",
            "body": {
                "hello": "world"
            }
        },
        {
            "url": "https://api.github.com/zen",
            "method": "GET"
        }
    ],
    "headers": {
        "Accept": "application/json"
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("ucchiii/bulk-http-request").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 = {
    "requests": [
        {
            "url": "https://httpbin.org/get",
            "method": "GET",
        },
        {
            "url": "https://httpbin.org/post",
            "method": "POST",
            "body": { "hello": "world" },
        },
        {
            "url": "https://api.github.com/zen",
            "method": "GET",
        },
    ],
    "headers": { "Accept": "application/json" },
}

# Run the Actor and wait for it to finish
run = client.actor("ucchiii/bulk-http-request").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 '{
  "requests": [
    {
      "url": "https://httpbin.org/get",
      "method": "GET"
    },
    {
      "url": "https://httpbin.org/post",
      "method": "POST",
      "body": {
        "hello": "world"
      }
    },
    {
      "url": "https://api.github.com/zen",
      "method": "GET"
    }
  ],
  "headers": {
    "Accept": "application/json"
  }
}' |
apify call ucchiii/bulk-http-request --silent --output-dataset

```

## MCP server setup

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

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/hIFzr1mmSIMJSix55/builds/dRMfDYcdyrYnZvxCH/openapi.json
