# HTTP Request - Batch API Calls \[$4/1K] (`automa-flow/http-request-batch`) Actor

Send GET, POST, PUT, PATCH and DELETE API calls from Apify in batches for Clay, n8n and Make: encrypted secrets, retries, lookup keys, structured results. $4 per 1,000 HTTP responses; blocks, timeouts and retries are never charged.

- **URL**: https://apify.com/automa-flow/http-request-batch.md
- **Developed by:** [Vadim Bezrukov](https://apify.com/automa-flow) (community)
- **Categories:** Automation, Developer tools, Integrations
- **Stats:** 3 total users, 2 monthly users, 0.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $4.00 / 1,000 http 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?

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 Request - Batch API Calls \[$4/1K]

Send a list of API requests and get one result per request, including the HTTP
status and response body. Use `lookupKey` to match each response back to its
Clay row, n8n item or Make step. Put shared credentials in encrypted `secretHeaders`.

You pay only for HTTP responses that actually arrived. Security blocks, timeouts,
transport failures, retries, redirects and callbacks are never charged, and the
bill is predictable before the run: $0.004 × responses plus a $0.00005 start.

### Try one request

```json
{
  "requests": [
    {
      "lookupKey": "demo-post-1",
      "method": "GET",
      "url": "https://jsonplaceholder.typicode.com/posts/1",
      "headers": { "Accept": "application/json" }
    }
  ]
}
```

Expected output: one Dataset row with `status`, `httpStatus`, parsed JSON `body`,
`durationMs` and `bytesReceived`. Typical first useful result under normal conditions
is under 10 seconds and about $0.004 (one `http-request` plus the $0.00005 start).

### Use from AI agents with MCP

Direct tool URL (authenticated OAuth or API token required to run):
`https://mcp.apify.com?tools=automa-flow/http-request-batch`

Ask your agent something like: *Call GET https://jsonplaceholder.typicode.com/posts/1
with lookupKey demo-post-1 through automa-flow/http-request-batch, keep
maxRequests at 10 or lower, set maxTotalChargeUsd to 0.05, then report RUN\_SUMMARY
status, billedRequests and nextAction before reading the Dataset.*

Anonymous discovery (no token) only supports Store search tools:
`https://mcp.apify.com?tools=search-actors,fetch-actor-details`. Direct Actor
execution always needs the caller's own Apify account.

Estimate the charge before running:

| Size | Input | Total charge (platform usage included) |
| --- | --- | ---: |
| Small | 1 request | $0.00405 |
| Small batch | 10 requests | $0.04005 |
| Cap | `maxRequests` (default 100, hard max 1000) | $0.00005 + N × $0.004 |

Each billable Dataset write and `http-request` event are atomic. At
`maxTotalChargeUsd`, the executor stops scheduling new target calls; any in-flight
response refused by the charge limit becomes an uncharged `CANCELLED` row with no
response payload. Read `RUN_SUMMARY.nextAction` first.

### Related tools for API and file workflows

- **Walk every page of one API:** [Paginated REST API Fetcher](https://apify.com/automa-flow/paginated-rest-api-fetcher) follows a configured page, offset, cursor or next-link pattern. Choose it when later requests depend on the previous response; use this HTTP batch Actor for an explicit list of independent calls.
- **Store files from response URLs:** [Parallel Bulk File Downloader](https://apify.com/automa-flow/parallel-bulk-file-downloader) takes a list of public file URLs and returns stored files plus a manifest. Extract the permitted URLs from your API results and map them into its input.
- **Extract website text:** [Website Content Crawler](https://apify.com/automa-flow/website-content-crawler) turns authorized public HTML into Markdown and optional chunks. Supply website URLs and the required rights declaration; an API response is not a crawl configuration.

Each tool runs separately with its own input and pricing. Your workflow must
select successful results, map the next input and start the next run.

### Batch example

```json
{
  "requests": [
    {
      "lookupKey": "customer-1",
      "method": "GET",
      "url": "https://api.example.com/customers/1"
    },
    {
      "lookupKey": "customer-2",
      "method": "POST",
      "url": "https://api.example.com/customers",
      "headers": { "Content-Type": "application/json" },
      "body": { "name": "Ada" }
    }
  ],
  "secretHeaders": {
    "Authorization": "Bearer YOUR_TOKEN"
  },
  "maxConcurrency": 20,
  "maxRetries": 2,
  "maxRequests": 100
}
```

### Clay integration

1. Create an Apify account and subscribe to this Actor.
2. In Clay, use the Apify integration and select **HTTP Request - Batch API Calls \[$4/1K]**.
3. Map Clay columns into `requests` (or run a single-item batch per row).
4. Map Dataset fields (`body`, `httpStatus`, `lookupKey`) back into Clay columns.
5. Optionally set `callbackUrl` to POST each final row to another Clay webhook table.

### n8n / Make / API usage

- **n8n / Make:** call the Apify Actor node / HTTP module with the JSON input above.
- **API:** `POST https://api.apify.com/v2/acts/automa-flow~http-request-batch/runs`
  with the same input. Read the default Dataset and `RUN_SUMMARY` when the run
  succeeds; read `CALLBACK_REPORT` when callbacks are enabled.

Schedule recurring batches whenever your automation has a new set of rows/events.
To push results into n8n or Make as soon as a run finishes, add a run webhook
(`POST https://api.apify.com/v2/webhooks`):

```json
{
  "eventTypes": ["ACTOR.RUN.SUCCEEDED", "ACTOR.RUN.FAILED"],
  "condition": { "actorId": "<this Actor's ID>" },
  "requestUrl": "https://your-n8n-or-make-webhook.example.com/hook",
  "payloadTemplate": "{\"runId\": {{resource.id}}, \"datasetId\": {{resource.defaultDatasetId}}, \"status\": {{resource.status}}}"
}
```

Then read `RUN_SUMMARY` (status, `billedRequests`, `nextAction`) and the Dataset
by `datasetId` in the receiving workflow.

### Auth / secrets

Put credentials in `secretHeaders` (and `callbackSecretHeaders` for callbacks).
These fields are marked `isSecret: true` so Apify encrypts them at rest.

- Secret values are **never** written to Dataset rows or logs.
- Redirects never forward `secretHeaders`, Authorization, cookies or API-key
  headers to a different origin. Reflected secret values are redacted from body
  and response-header output.
- Prefer headers over query-string secrets (`token`, `api_key`, …). Every query
  value is redacted in logs, Dataset URLs and callback payload URLs.
- Per-request distinct secrets are not supported in this version - use shared
  run-level `secretHeaders`.

### Output example

```json
{
  "inputIndex": 0,
  "lookupKey": "demo-post-1",
  "method": "GET",
  "url": "https://jsonplaceholder.typicode.com/posts/1",
  "status": "SUCCESS",
  "httpStatus": 200,
  "ok": true,
  "contentType": "application/json; charset=utf-8",
  "responseHeaders": { "content-type": "application/json; charset=utf-8" },
  "body": { "userId": 1, "id": 1, "title": "…", "body": "…" },
  "bodyText": null,
  "truncated": false,
  "bytesReceived": 292,
  "attempts": 1,
  "durationMs": 180,
  "errorType": null,
  "errorMessage": null,
  "retried": false,
  "billable": true,
  "callbackStatus": "SKIPPED",
  "callbackError": null,
  "source": "http-request-batch",
  "scrapedAt": "2026-09-17T00:00:00Z",
  "schemaVersion": 1
}
```

### Retries / failure semantics

| Status | Meaning |
| --- | --- |
| `SUCCESS` | Target returned an HTTP response. `ok` is true for 2xx. With `treatNon2xxAsError=false` (default), non-2xx stays `SUCCESS` with `ok=false`. |
| `INVALID_INPUT` | This request item was invalid. Other valid items still run. |
| `HTTP_ERROR` | Non-2xx response when `treatNon2xxAsError=true`. |
| `TRANSPORT_ERROR` | Network/protocol failure - never presented as an empty success. |
| `TIMEOUT` | Attempt timed out. |
| `RESPONSE_TOO_LARGE` | Streaming byte cap hit (default 256 KiB, max 1 MiB). |
| `SECURITY_BLOCKED` | URL failed SSRF checks (private/loopback/metadata/credentials/…). |
| `CANCELLED` | Skipped or unpaid after `maxTotalChargeUsd` / billing stop. Response body is cleared. |

Retries (not billed): connection resets, timeouts, HTTP 408/429/500/502/503/504 -
**GET/HEAD/OPTIONS only** by default. Set `retryNonIdempotent=true` to retry
POST/PUT/PATCH/DELETE. Ordinary 4xx such as 400/401/403/404 are never retried.
Redirects are revalidated at every hop (max 5). HTTPS-to-HTTP downgrades and
cross-origin redirects that would preserve a request body are blocked. POST may
become GET on 301/302; 303 becomes GET except for HEAD; same-origin 301/302
methods and 307/308 preserve method and body. Dataset `method` reports the method
of the final attempt.

One bad request never kills the batch: N inputs → N Dataset rows.

The primary Dataset row is atomically stored and charged before any callback is
sent. Callback retries within a run reuse an `Idempotency-Key`. A callback-enabled
Dataset row has `callbackStatus=PENDING`; final per-item delivery status is in
`CALLBACK_REPORT`. Callback failure keeps the primary result and makes
`RUN_SUMMARY.status` `PARTIAL`.

### Pricing

Pay-per-event:

| Event | Price |
| --- | --- |
| `apify-actor-start` | $0.00005 / run (Apify's default synthetic start; one event per GB of run memory) |
| `http-request` | **$0.004** / billable HTTP response (**$4 / 1,000**) |

**Billable** means the target server produced a real HTTP response (including 4xx/5xx
and `RESPONSE_TOO_LARGE`). Not billed: SSRF blocks, transport failures, timeouts,
retries, redirects, callbacks, validation. Platform usage (compute, storage,
transfer) is included in the event price; there is no separate usage charge.

Examples (total charge):

- 1 request → $0.00005 + $0.004 = **$0.00405**
- 100 requests → $0.00005 + $0.40 = **$0.40005**
- 1,000 requests → $0.00005 + $4.00 = **$4.00005**

Minimum `maxTotalChargeUsd` per run: $0.005 (one start plus one response).

### Limits

For POST/PUT/PATCH/DELETE use one of `body` (JSON object), `bodyArray` (JSON
array), or `bodyText` (raw text). They are mutually exclusive.

| Limit | Default | Hard max |
| --- | --- | --- |
| Requests / run (`maxRequests`) | 100 | 1,000 |
| Concurrency | 20 | 50 |
| Timeout | 30 s | 120 s |
| Retries | 2 | 5 |
| Response size | 256 KiB | 1 MiB |

### Security / SSRF policy

Only `http` / `https` URLs. Rejected:

- `file:`, `ftp:`, `data:`, `javascript:`, malformed URLs
- embedded credentials in the URL
- loopback, RFC1918 private, link-local, multicast/reserved IPv4
- IPv6 loopback / link-local / unique-local
- cloud metadata (`169.254.169.254`, metadata hostnames)
- hostnames that resolve to blocked addresses (DNS rebinding)
- redirect hops to blocked destinations

Callback URLs use the same policy. Use only APIs and data you are authorized to
access; do not use this Actor to bypass authentication, paywalls or access
controls. API responses may contain personal, regulated or confidential data and
are retained in the Apify Dataset until you delete them. Minimize requested data,
configure Dataset retention for your obligations, and remember that
`callbackUrl` discloses the full stored response row to that destination.

### FAQ

**Does this include Apify Proxy?**\
Not in this version. Plain outbound HTTPS only.

**Are retries charged?**\
No.

**What about binary responses?**\
Metadata is returned; arbitrary binary is not inlined into the Dataset by default.

**Can I use distinct secrets per request?**\
Not in this version - use shared `secretHeaders` for the run.

### Example workflows

1. Call a REST API from Clay - single GET + `lookupKey`
2. POST JSON to a webhook/API - demo POST; secrets via `secretHeaders`
3. Send 100 API requests in one batch - concurrency + per-row Dataset identity

# Actor input Schema

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

1-1000 HTTP requests to execute in this run. Each item becomes one Dataset row with matching inputIndex / lookupKey; malformed items become uncharged INVALID\_INPUT rows without stopping valid items.

## `secretHeaders` (type: `object`):

Encrypted shared headers merged into every request (for example Authorization). Values are never written to Dataset rows or logs.

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

Default per-request timeout when a request does not set timeoutSecs.

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

How many requests to run at once (1-50). Default 20.

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

Extra attempts after the first try for timeouts, network errors and retryable HTTP statuses (408/429/5xx). By default only safe methods (GET/HEAD/OPTIONS) are retried. Retries are not billed.

## `retryNonIdempotent` (type: `boolean`):

When false (default), only GET/HEAD/OPTIONS are retried. Enable only when your target tolerates duplicate non-idempotent requests.

## `maxResponseBytes` (type: `integer`):

Hard streaming cap per response (1 KiB - 1 MiB). Oversized responses are marked RESPONSE\_TOO\_LARGE.

## `treatNon2xxAsError` (type: `boolean`):

When false (default), a reached HTTP response is status SUCCESS with ok=false for non-2xx. When true, non-2xx rows use status HTTP\_ERROR.

## `callbackUrl` (type: `string`):

Optional public URL that receives one POST per completed valid request with the Dataset row JSON. Same SSRF policy as target URLs. Retries reuse an Idempotency-Key and are not billed.

## `callbackSecretHeaders` (type: `object`):

Encrypted headers for the optional callback POST only.

## `maxRequests` (type: `integer`):

Reject the run when the requests array is longer than this cap (1-1000). Default 100 for safe first runs.

## Actor input object example

```json
{
  "requests": [
    {
      "lookupKey": "demo-post-1",
      "method": "GET",
      "url": "https://jsonplaceholder.typicode.com/posts/1",
      "headers": {
        "Accept": "application/json"
      }
    }
  ],
  "timeoutSecs": 30,
  "maxConcurrency": 20,
  "maxRetries": 2,
  "retryNonIdempotent": false,
  "maxResponseBytes": 262144,
  "treatNon2xxAsError": false,
  "maxRequests": 100
}
```

# Actor output Schema

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

No description

## `runSummary` (type: `string`):

No description

## `billingReceipt` (type: `string`):

No description

## `callbackReport` (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": [
        {
            "lookupKey": "demo-post-1",
            "method": "GET",
            "url": "https://jsonplaceholder.typicode.com/posts/1",
            "headers": {
                "Accept": "application/json"
            }
        }
    ],
    "timeoutSecs": 30,
    "maxConcurrency": 20,
    "maxRequests": 100
};

// Run the Actor and wait for it to finish
const run = await client.actor("automa-flow/http-request-batch").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": [{
            "lookupKey": "demo-post-1",
            "method": "GET",
            "url": "https://jsonplaceholder.typicode.com/posts/1",
            "headers": { "Accept": "application/json" },
        }],
    "timeoutSecs": 30,
    "maxConcurrency": 20,
    "maxRequests": 100,
}

# Run the Actor and wait for it to finish
run = client.actor("automa-flow/http-request-batch").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": [
    {
      "lookupKey": "demo-post-1",
      "method": "GET",
      "url": "https://jsonplaceholder.typicode.com/posts/1",
      "headers": {
        "Accept": "application/json"
      }
    }
  ],
  "timeoutSecs": 30,
  "maxConcurrency": 20,
  "maxRequests": 100
}' |
apify call automa-flow/http-request-batch --silent --output-dataset

```

## MCP server setup

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

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/nwNi920wMdlAXn9GO/builds/vOsrd8w3qcjOZjZSu/openapi.json
