# HTTP Request (`data_xplorer/http-request`) Actor

Call any public API, one endpoint or thousands per run, and get one tidy row per call: status, headers, decoded body, timing and typed errors. Retries, proxy and webhook delivery included. Works with Clay, n8n, Make, Zapier and AI agents.

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

## Pricing

from $0.20 / 1,000 responses

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

**HTTP Request** calls any public API for you and hands back a tidy table: one row per call, with the status, the headers, the decoded body, the time it took and, when it fails, the exact reason. Make a single call or 10,000 in one run. Works with Clay, n8n, Make, Zapier and AI agents.

https://www.youtube.com/watch?v=fQS74jc_1Gs

### 🌐 What is HTTP Request?

It is an HTTP client hosted on Apify. Instead of writing a script, you describe the calls you want (address, method, headers, body) and the Actor performs them in parallel, retries the ones that fail for a temporary reason, and stores every answer in a dataset.

From there you can download the results as JSON, CSV or Excel, read them from your code, or have each row pushed to a webhook.

### ✨ What can this HTTP request tool do?

- 📦 **Thousands of calls per run:** up to 10,000, with up to 50 running in parallel
- 🔢 **Results in your order:** the first row is your first request, whatever finished first
- 🧰 **All the usual methods:** GET, POST, PUT, PATCH, DELETE, HEAD and OPTIONS, with a JSON or text payload
- 🗂️ **Defaults for the whole list:** write an API key once, and let any request replace or drop it
- 🔗 **List parameters done right:** `{"tag": ["a", "b"]}` goes out as `tag=a&tag=b`
- 🔁 **Retries with judgement:** temporary failures (network trouble, 429, 5xx) get new tries with growing pauses, and `Retry-After` is honoured. Final answers such as 404 or 401 come back immediately
- 📄 **Any kind of answer:** JSON becomes structured data, text stays text, files and images arrive as base64
- ↪️ **Redirects made visible:** each row shows where the call ended up and how many hops it took
- 🚨 **Named failures:** `dns_error`, `timeout`, `connection_error`, `tls_error`, `proxy_error`... so you know what to fix
- 📬 **Webhook delivery:** rows can be posted to your endpoint one by one, while the run is still going
- 🌍 **Proxy on demand:** appear in the country you choose or vary the outgoing IP address
- 🔒 **Credentials masked:** keys, tokens and cookies never show in the dataset, yet the API receives them intact
- 💸 **Billing on answers only:** a call that gets no HTTP response costs nothing, and neither do retries

### 💡 What is it used for?

- 🧱 **Enrichment in Clay:** query an API that Clay has no connector for, once per row, and bring the answer back into the table
- ⚙️ **Loops in n8n, Make and Zapier:** replace a slow loop of HTTP modules by one Actor run that returns every answer together
- 📈 **Uptime and contract checks:** put your endpoints on a schedule and watch `statusCode` and `durationMs` over time
- 📣 **Notifications to several systems:** deliver one event to a list of webhooks, each with its own credentials
- 🗺️ **Localised data:** read the price, stock or ranking an API serves to visitors of a given country
- 🤖 **Tools for AI agents:** let an agent reach any REST API through a single, predictable tool

### 🚀 How to send HTTP requests in bulk

1. Click **Try for free** and open the Input tab.
2. List your calls in **Requests**. A URL is enough for a GET.
3. Put what the calls have in common (API key, shared parameters) in the section below, once.
4. Press **Start**.
5. Open the Output tab: there is one row per call, in the order of your list.
6. Export the table, fetch it through the API, or fill in **Webhook URL** before starting to receive rows live.

For a single call, leave **Requests** empty and fill in **URL**.

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

The price is per HTTP response received, and it includes platform usage.

| Event | Free | Bronze | Silver | Gold |
|-------|------|--------|--------|------|
| 1,000 responses | $0.30 | $0.27 | $0.24 | $0.20 |
| 1,000 responses through a proxy | $1.00 | $0.90 | $0.80 | $0.70 |
| Run start | $0.00005 | | | |

The proxy price is the same for datacenter and residential proxies.

🎁 Never billed:

- extra tries of the same call
- calls that end without an HTTP response (unknown domain, timeout, refused connection, address not allowed)
- entries that could not be sent (invalid URL, run timeout or spending cap reached)

| Calls answered | Free plan, no proxy |
|----------------|---------------------|
| 1 | $0.00035 |
| 100 | $0.03 |
| 1,000 | $0.30 |
| 10,000 | $3.00 |

Every Apify account receives free credits each month, which cover thousands of calls. If you set a maximum cost in the run options, the Actor stops sending when the cap is reached and marks the remaining entries as `skipped_charge_limit`.

### ⬇️ Input

| Field | Description |
|-------|-------------|
| Requests | Your list of calls. Each entry is a URL or `{ url, method, headers, params, body, id }`. |
| URL | Address for a single call, or default address of the entries. |
| Method | GET by default. |
| Headers | Sent with every call. An entry can replace one, or remove it with `null`. |
| Query parameters | Appended to the address. A list repeats the name. |
| Body | JSON payload for write calls. A string is accepted through the API. |
| Parallel calls | 1 to 50, 10 by default. |
| Timeout | 1 to 300 seconds per try. |
| Retries | 0 to 10 new tries, 2 by default. |
| Statuses worth retrying | 408, 425, 429, 500, 502, 503, 504 by default. |
| Follow redirects, Redirect limit | Up to 20 hops, or none at all. |
| Decode JSON | Structured data, or raw string. |
| Size limit per response | 1 MB by default, 10 MB at most. With a residential proxy: 100 kB. |
| Mask credentials in results | Enabled by default. |
| Webhook URL | Receives each row as it is produced. |
| Proxy | Apify Proxy (country and group) or your own proxy servers. |

A list of calls with a shared API key:

```json
{
  "headers": { "X-API-Key": "YOUR_KEY" },
  "requests": [
    { "id": "acme", "url": "https://api.example.com/companies", "params": { "domain": "acme.com" } },
    { "id": "globex", "url": "https://api.example.com/companies", "params": { "domain": "globex.com" } },
    { "id": "new-contact", "url": "https://api.example.com/contacts", "method": "POST", "body": { "email": "jane@acme.com", "tags": ["lead", "2026"] } }
  ],
  "maxConcurrency": 5
}
```

One call:

```json
{
  "url": "https://jsonplaceholder.typicode.com/todos/1"
}
```

A call seen from the United States, with rows pushed to a webhook:

```json
{
  "requests": [
    { "id": "sku-1042", "url": "https://shop.example.com/api/products/1042" }
  ],
  "proxy": { "useApifyProxy": true, "apifyProxyCountry": "US" },
  "webhookUrl": "https://your-app.example.com/hooks/http-results"
}
```

### ⬆️ Output

| Field | Description |
|-------|-------------|
| `id` | The id of your entry, or `request-1`, `request-2`... |
| `index` | Rank of the entry in your list, from 0 |
| `url` | Address called, parameters included |
| `finalUrl` | Address that answered, once redirects were followed |
| `method` | HTTP method |
| `statusCode`, `statusText` | Status returned by the server |
| `ok` | `true` when the status is between 200 and 299 |
| `requestHeaders` | Headers you supplied, credentials masked |
| `responseHeaders` | Headers of the answer |
| `body` | The answer: structured JSON, text, or base64 |
| `bodyEncoding` | `json`, `text`, `base64` or `none` |
| `bodyBytes` | Size of the answer in bytes |
| `truncated` | `true` when the size limit cut the answer |
| `durationMs` | Time spent on the call, retries included |
| `attempts` | Number of tries |
| `redirectCount` | Number of redirects followed |
| `proxyUsed` | Whether the call went through a proxy |
| `errorType`, `errorMessage` | Reason, when there is no HTTP response |
| `webhookStatus` | What your webhook answered, when one is set |
| `fetchedAt` | Date and time of the call |

A row for a successful POST:

```json
{
  "id": "create-post",
  "index": 1,
  "url": "https://jsonplaceholder.typicode.com/posts",
  "finalUrl": "https://jsonplaceholder.typicode.com/posts",
  "method": "POST",
  "statusCode": 201,
  "statusText": "Created",
  "ok": true,
  "requestHeaders": { "content-type": "application/json" },
  "responseHeaders": { "content-type": "application/json; charset=utf-8" },
  "body": { "title": "Hello", "userId": 1, "id": 101 },
  "bodyEncoding": "json",
  "bodyBytes": 50,
  "truncated": false,
  "durationMs": 212,
  "attempts": 1,
  "redirectCount": 0,
  "proxyUsed": false,
  "errorType": null,
  "errorMessage": null,
  "fetchedAt": "2026-10-06T21:40:07.115Z"
}
```

A row for a call that never got an answer:

```json
{
  "id": "old-partner",
  "url": "https://api.partner-that-closed.example/v2/status",
  "statusCode": null,
  "ok": false,
  "attempts": 1,
  "errorType": "dns_error",
  "errorMessage": "getaddrinfo ENOTFOUND api.partner-that-closed.example"
}
```

| `errorType` | What happened | New try? |
|-------------|---------------|----------|
| `dns_error` | The domain name does not resolve | Only if the resolver itself was unavailable |
| `timeout` | The answer did not arrive in time | Yes |
| `connection_error` | The server refused or dropped the connection | Yes |
| `tls_error` | The HTTPS certificate is not valid | No |
| `proxy_error` | The proxy failed, or your plan has no access to it | Yes, unless access was denied |
| `network_error` | Another transport problem | Yes |
| `blocked_url` | The address is not on the public internet, or uses a port reserved for another protocol | No |
| `too_many_redirects` | The redirect limit was exceeded | No |
| `invalid_request` | The entry has no valid URL or an unknown method | No |
| `skipped_timeout` | The run was about to time out before this entry | No |
| `skipped_charge_limit` | Your spending cap was reached before this entry | No |

### 🧩 How to use HTTP Request with Clay, n8n, Make and Zapier

Any tool able to send a POST can drive the Actor. This endpoint launches a run and replies with the rows once they are ready:

```
POST https://api.apify.com/v2/acts/data_xplorer~http-request/run-sync-get-dataset-items
```

- **Clay:** make one run per table row with `url`, your headers and an `id`, then map fields of the returned `body` to columns.
- **n8n, Make, Zapier:** turn your items into the `requests` list, launch one run, then iterate over the rows. Since the order is kept, row N belongs to item N.
- **Large lists:** fill in **Webhook URL** and let your scenario be triggered by each row, instead of keeping a connection open until the end.

n8n, Make and Zapier also have an Apify app, if you prefer modules to raw calls.

### 🔌 Can I use HTTP Request as an API?

Yes, through the [Apify API](https://docs.apify.com/api/v2) or the Apify clients.

cURL:

```bash
curl -X POST "https://api.apify.com/v2/acts/data_xplorer~http-request/run-sync-get-dataset-items" \
  -H "Authorization: Bearer <YOUR_API_TOKEN>" \
  -H "Content-Type: application/json" \
  -d '{"requests":["https://jsonplaceholder.typicode.com/todos/1","https://jsonplaceholder.typicode.com/users/1"]}'
```

Python:

```python
from apify_client import ApifyClient

client = ApifyClient("<YOUR_API_TOKEN>")
run = client.actor("data_xplorer/http-request").call(run_input={
    "requests": [
        {"id": "todo", "url": "https://jsonplaceholder.typicode.com/todos/1"},
        {"id": "user", "url": "https://jsonplaceholder.typicode.com/users/1"},
    ],
})
for row in client.dataset(run["defaultDatasetId"]).iterate_items():
    print(row["id"], row["statusCode"], row["durationMs"])
```

Node.js:

```javascript
import { ApifyClient } from 'apify-client';

const client = new ApifyClient({ token: '<YOUR_API_TOKEN>' });
const run = await client.actor('data_xplorer/http-request').call({
    requests: [
        { id: 'todo', url: 'https://jsonplaceholder.typicode.com/todos/1' },
        { id: 'user', url: 'https://jsonplaceholder.typicode.com/users/1' },
    ],
});
const { items } = await client.dataset(run.defaultDatasetId).listItems();
for (const row of items) console.log(row.id, row.statusCode, row.durationMs);
```

### 🤖 Use HTTP Request with AI agents (MCP)

Through the [Apify MCP server](https://docs.apify.com/platform/integrations/mcp), assistants such as Claude, Cursor or VS Code can use this Actor as a tool. The agent decides which endpoints to call, and gets back structured rows it can reason on, including a named error when a call fails.

Server URL restricted to this Actor:

```
https://mcp.apify.com?tools=data_xplorer/http-request
```

Claude Code:

```bash
claude mcp add --transport http http-request "https://mcp.apify.com?tools=data_xplorer/http-request"
```

MCP config file of Cursor, VS Code and similar clients:

```json
{
  "mcpServers": {
    "http-request": {
      "url": "https://mcp.apify.com?tools=data_xplorer/http-request"
    }
  }
}
```

Things you can ask:

- "Fetch these 15 product endpoints and list the ones whose price changed since my last note."
- "Are all the URLs in this sitemap answering with a 200? Give me the broken ones and why."
- "Send this JSON to my staging webhook and show me what it replies."

### 🛡️ Safety

- Only addresses on the public internet can be called. Loopback, private networks and reserved ranges are refused, and the check is repeated on every redirect and on the IP address the name resolves to.
- When a redirect leaves the original site, your `Authorization` header and cookies stay behind.
- Results are safe to share: credentials are masked unless you decide otherwise.

### ❓ FAQ

#### 📦 How many calls fit in one run?

10,000. Rows are written to the dataset in groups while the run is going, so a long list is never held back until the end.

#### 🔢 Is the order of the results guaranteed?

Yes. Calls are made in parallel, but rows are stored in the order of your list. `index` and `id` are there as well if you sort or filter later.

#### 🔁 What if the API replies 429 or 503?

Those two are treated as temporary. The Actor pauses, longer after each failure, and follows `Retry-After` when the API provides it (15 seconds at most). After the last try you receive the answer as it is, billed as one response.

#### 🔑 How do I pass an API key?

In **Headers** (for instance `X-API-Key` or `Authorization`) or in **Query parameters**, depending on the API. It reaches the API unchanged and is masked in the stored results.

#### 🍪 Can I read the cookies a server sets?

Yes: disable **Mask credentials in results** and `set-cookie` appears in `responseHeaders`.

#### 📎 Can it download a file?

Yes, within the size limit. Its content is in `body` as base64, and `bodyEncoding` says so.

#### 🖥️ Does it run JavaScript or sign in to websites?

No. It speaks plain HTTP, which is all an API needs and the reason it is quick and inexpensive. Pages that depend on a browser call for a scraper built for them.

#### 🌍 Which proxy countries can I pick?

The ones offered by the proxy groups of your Apify plan. If a country is not available to you, the row says so with a `proxy_error`.

#### 🛟 Something is missing or broken. What now?

Tell us in the **Issues** tab. Every report gets an answer.

### 🔗 Other tools you may like

- [Google Flights Scraper](https://apify.com/data_xplorer/google-flights-scraper): flight prices, schedules and booking links from Google Flights
- [Google Trends Scraper](https://apify.com/data_xplorer/google-trends-fast-scraper): daily trending searches by country and keyword interest over time
- [Google News Scraper](https://apify.com/data_xplorer/google-news-scraper-fast): Google News articles in real time, with titles, sources, dates, images and URLs
- [TikTok Ads Library FAST](https://apify.com/data_xplorer/tiktok-ads-library-fast): competitor ad campaigns and creatives from the TikTok Ads Library

# Actor input Schema

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

One entry per call. An entry is either a URL, or an object with url and, when needed, method, headers, params, body and id. The id comes back in the result row, which makes it easy to match answers with your own records.

## `url` (type: `string`):

Address of the API endpoint, starting with https:// or http://.

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

HTTP verb used when an entry does not name one.

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

Name and value pairs sent with each call, typically an API key or an Accept header. An entry can replace one of them, or drop it by setting it to null.

## `params` (type: `object`):

Name and value pairs appended to the address after the question mark. Give several values as a list and the name is repeated, for example {"tag": \["a", "b"]} produces tag=a\&tag=b.

## `body` (type: `object`):

Payload of write calls (POST, PUT, PATCH, DELETE), serialized as JSON. Ignored by GET and HEAD. When you start the Actor from the API, a plain string is also accepted and goes out untouched.

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

Number of calls in flight simultaneously. Reduce it for APIs with a strict rate limit.

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

Seconds allowed for one try, from connection to the last byte of the answer.

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

Number of new tries after a network failure or a status listed below. The pause grows between tries and honours the Retry-After header. A retried call still counts as one billed response. Use 0 for a single try.

## `retryOnStatusCodes` (type: `array`):

Status codes treated as temporary. Anything else, a 404 or a 401 for instance, is final and returned at once.

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

Enabled: the Actor goes to the new location and records where it ended up. Disabled: you receive the 301, 302 or 307 answer itself, with its Location header.

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

Hops allowed before the call is reported as too_many_redirects.

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

Enabled: JSON answers are stored as structured data, ready to map field by field. Disabled: they are stored as a string.

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

Bytes kept from each answer. Anything beyond is dropped and the row is marked truncated.

## `redactSensitive` (type: `boolean`):

Enabled: API keys, tokens and cookies appear as \[REDACTED] in the dataset, so results can be shared safely. The API you call still receives the real values. Disable it to read Set-Cookie values.

## `webhookUrl` (type: `string`):

Optional address that receives every row as a JSON POST the moment it is available, without waiting for the run to finish.

## `proxy` (type: `object`):

Leave empty to call from Apify servers, which suits nearly every API. Select a proxy to appear in a chosen country or to vary the outgoing IP address. Proxied calls have their own price, and residential ones keep at most 100 kB per answer.

## Actor input object example

```json
{
  "requests": [
    {
      "id": "read-todo",
      "url": "https://jsonplaceholder.typicode.com/todos/1"
    },
    {
      "id": "create-post",
      "url": "https://jsonplaceholder.typicode.com/posts",
      "method": "POST",
      "body": {
        "title": "Hello",
        "userId": 1
      }
    }
  ],
  "method": "GET",
  "headers": {},
  "params": {},
  "body": {},
  "maxConcurrency": 10,
  "timeoutSecs": 30,
  "maxRetries": 2,
  "retryOnStatusCodes": [
    408,
    425,
    429,
    500,
    502,
    503,
    504
  ],
  "followRedirects": true,
  "maxRedirects": 5,
  "parseJson": true,
  "maxResponseBytes": 1000000,
  "redactSensitive": true
}
```

# Actor output Schema

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

No description

## `allFields` (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": [
        {
            "id": "read-todo",
            "url": "https://jsonplaceholder.typicode.com/todos/1"
        },
        {
            "id": "create-post",
            "url": "https://jsonplaceholder.typicode.com/posts",
            "method": "POST",
            "body": {
                "title": "Hello",
                "userId": 1
            }
        }
    ],
    "headers": {},
    "params": {},
    "body": {}
};

// Run the Actor and wait for it to finish
const run = await client.actor("data_xplorer/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": [
        {
            "id": "read-todo",
            "url": "https://jsonplaceholder.typicode.com/todos/1",
        },
        {
            "id": "create-post",
            "url": "https://jsonplaceholder.typicode.com/posts",
            "method": "POST",
            "body": {
                "title": "Hello",
                "userId": 1,
            },
        },
    ],
    "headers": {},
    "params": {},
    "body": {},
}

# Run the Actor and wait for it to finish
run = client.actor("data_xplorer/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": [
    {
      "id": "read-todo",
      "url": "https://jsonplaceholder.typicode.com/todos/1"
    },
    {
      "id": "create-post",
      "url": "https://jsonplaceholder.typicode.com/posts",
      "method": "POST",
      "body": {
        "title": "Hello",
        "userId": 1
      }
    }
  ],
  "headers": {},
  "params": {},
  "body": {}
}' |
apify call data_xplorer/http-request --silent --output-dataset

```

## MCP server setup

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