# HTTP Request & Bulk API Calls — Clay, n8n, Webhooks · $1/1k (`tidydata/http-request`) Actor

Send HTTP requests to any API: one call (Clay-ready, with callback) or thousands from a list, CSV, Google Sheet or Apify dataset with {{column}} templates. Retries, rate limits, JSON field extraction, pagination and webhooks. $1 per 1,000 requests.

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

## Pricing

from $0.80 / 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.
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 & Bulk API Calls — one call or thousands, Clay-ready

![A request template plus four GitHub repositories turned into four API calls with stars, language and license extracted into columns (real output)](https://raw.githubusercontent.com/tidydata-dev/tidydata-dev/main/assets/apify/http-request.png)

Send **HTTP requests to any API** from Apify: **one call** (perfect for **Clay**, n8n, Make and Zapier, with a callback URL and lookup key) or **thousands in one run**, from a list of URLs or by filling one request template with the **`{{columns}}` of an Apify dataset, CSV, Excel or Google Sheet**. Retries, rate limits, JSON field extraction and automatic pagination included. **$1 per 1,000 requests** — and requests that get no response are free.

- ✅ **GET, POST, PUT, PATCH, DELETE, HEAD, OPTIONS** with headers, query parameters and JSON, form or text bodies
- ✅ **Bulk API calls**: one request per row of a dataset, CSV, Excel, JSON file or Google Sheet, with `{{column}}` placeholders in the URL, headers, params and body
- ✅ **Enrich datasets**: each result keeps the row's own fields, plus the response or just the **columns you extract** (`data.items[0].price`)
- ✅ **Automatic pagination**: page numbers, offsets, next-page URLs (JSON or `Link` header, GitHub style) and cursors
- ✅ **One row per item**: turn an API list (`data.results`) into a clean dataset, page after page
- ✅ **Reliable**: parallel requests, per-host rate limit, retries with back-off on 429/5xx and `Retry-After`, timeouts
- ✅ **Callbacks**: every result can be POSTed to a **Clay webhook table**, n8n, Make, Zapier, Slack or Discord URL
- ✅ **Optional Apify Proxy** (datacenter or residential, by country) or your own proxies
- ✅ **Safe by default**: private, local and cloud-metadata addresses are always blocked

### Who uses it

- **Clay users** — call any API from a Clay table without the HTTP API plan: one run per row, the answer comes back with your *lookup key*.
- **Automation builders (n8n, Make, Zapier)** — run hundreds of API calls in one go, with retries and rate limits you don't have to build.
- **Data teams** — enrich a scraped dataset with your own API, a CRM, a geocoder or an LLM endpoint, row by row.
- **Developers** — pull every page of a REST API into a dataset and export it as CSV, Excel or JSON.
- **Ops & monitoring** — hit a list of endpoints on a schedule and get status codes, timings and alerts (*mark the run as failed on errors*).

### How to use it

#### 1. One request (Clay, n8n, Make, Zapier)

Fill **URL**, **Method** and, if needed, **Headers**, **Query parameters** and **Body (data)**. Add a **Lookup key** to recognise the answer, and a **Callback URL** if the result should be POSTed somewhere. Leave everything empty for a free demo call.

#### 2. Many requests at once

- **Or a list of URLs**: one URL per line, all with the same method, headers and body.
- **Several requests (list)**: a JSON list where each request has its own `url`, `method`, `headers`, `params`, `data` and `lookupKey`.

#### 3. One request per row (dataset, CSV, Excel, Google Sheet)

Pick an Apify **dataset** (for example the output of a scraper — as an integration use `{{resource.defaultDatasetId}}`), or paste links to **CSV / Excel / JSON files** or a **Google Sheet**, or type the rows as JSON. Then write the request once with placeholders:

```text
URL:     https://api.example.com/v1/companies?domain={{website|raw}}
Headers: {"Authorization": "Bearer YOUR_KEY"}
Body:    {"name": "{{name}}", "employees": "{{employees}}"}
```

Placeholders: `{{column}}`, nested `{{address.city}}` or `{{tags[0]}}`. In the URL values are URL-encoded (spaces, `&`, `=`, `?`, `#`… — slashes are kept, so `{{repo}}` = `owner/name` works); add `|raw` to insert a value exactly as it is (for example a whole URL). In JSON bodies a field that is exactly `"{{column}}"` keeps the column's type (number, list, object). Other filters: `|json`, `|lower`, `|upper`, `|trim`.

### Pagination

| API style | Settings |
| --- | --- |
| `?page=1, 2, 3…` | Pagination = *Page number*, Page parameter = `page` (or `{{page}}` in the URL) |
| `?offset=0, 100, 200…` | *Page number*, Page parameter = `offset`, First page = `0`, Page step = `100` |
| `"next": "https://…"` in the JSON | *Next-page URL*, Next-page URL path = `next` |
| `Link: <…>; rel="next"` header (GitHub) | *Next-page URL*, path empty |
| `"next_cursor": "abc"` | *Cursor*, Next cursor path = `meta.next_cursor`, Cursor parameter = `cursor` |

Pages stop at *Max pages*, at the first error, or when the list in *Stop when this list is empty* (or the items path) comes back empty. Each page is one request.

### Output

One row per request (or one row per item with *One row per item*). Example of a GET to `https://jsonplaceholder.typicode.com/posts/1`:

```json
{
  "lookupKey": "demo",
  "method": "GET",
  "requestUrl": "https://jsonplaceholder.typicode.com/posts/1",
  "status": 200,
  "ok": true,
  "durationMs": 108,
  "contentType": "application/json",
  "responseBytes": 292,
  "truncated": false,
  "attempts": 1,
  "error": null,
  "response": {"userId": 1, "id": 1, "title": "sunt aut facere repellat provident occaecati…", "body": "quia et suscipit…"}
}
```

`status` is the HTTP status code (`null` when there was no response), `ok` is true for 2xx/3xx, `error` explains failures (`HTTP 404`, timeouts, blocked addresses). With *Extract fields into columns* you also get your columns; with *Drop the full response* only those.

### Use it from code

```python
from apify_client import ApifyClient

client = ApifyClient("<YOUR_API_TOKEN>")
run = client.actor("tidydata/http-request").call(run_input={
    "url": "https://api.example.com/v1/users/{{id}}",
    "rows": [{"id": 1}, {"id": 2}, {"id": 3}],
    "headers": {"Authorization": "Bearer YOUR_KEY"},
    "extract": {"email": "data.email"},
})
for item in client.dataset(run["defaultDatasetId"]).iterate_items():
    print(item["id"], item["status"], item.get("email"))
```

### Use it from Clay

1. In your Clay table add an **Apify** enrichment → *Run Actor* → `tidydata/http-request`.
2. Map the input: `url`, `method`, `headers`, `params`, `data` (use Clay's `/` to insert column values) and `lookupKey` (a row ID).
3. Run the column: each row runs one request and gets back `status`, `response` and your extracted fields. To receive results in a webhook table instead, set **Callback URL** (and *Callback headers* such as `x-clay-webhook-auth`).

### Pricing

**$0.001 per HTTP request answered** (= **$1 per 1,000**), any status code included; Apify Store discounts apply (−10 % Bronze, −15 % Silver, −20 % Gold and above). Each page of a paginated call counts as one request.

- **Requests without a response are free**: timeouts, DNS errors and blocked addresses after the retries.
- **Through Apify Proxy** (optional): +$0.001 per request on datacenter IPs, +$0.008 per request on residential IPs (responses capped at 2 MB there).
- Set a **maximum cost per run**: the Actor stops sending before going over it.

### FAQ

**Is it really one run per call for Clay?**
Yes, that's how Clay's Apify integration works. Runs start in a few seconds with 256 MB of memory, so they are cheap and fast. For big batches, send all rows in one run (dataset, CSV or list).

**Can I send API keys safely?**
Put them in *Headers* (or the URL/params if the API wants that). Inputs are private to your Apify account. Don't share your run links publicly.

**Does it run JavaScript or bypass anti-bot pages?**
No: it is a fast HTTP client for APIs, webhooks and simple pages. For full web pages use a browser scraper such as our [AI Web Scraper](https://apify.com/tidydata/ai-web-scraper).

**How do I send each row of a dataset to a webhook (Slack, Discord, Make, Zapier, n8n)?**
Method `POST`, URL = your webhook, Body = the fields you want, e.g. `{"content": "New lead: {{name}} — {{email}}"}` for Discord or `{"text": "…"}` for Slack, and pick the dataset.

**What if the API is slow or rate limited?**
Lower *Parallel requests* and *Max requests per second per host*. 429 and 5xx answers are retried with back-off (respecting `Retry-After`).

### Notes

- Responses over *Max response size* are cut and marked `truncated` (JSON is then returned as text).
- POST and PATCH are not retried unless you allow it (they could create duplicates).
- Use it only with APIs and sites you are allowed to call, and respect their terms and rate limits.
- If a run crashes, the Actor sends us only an anonymous error code (error type and line of our code, no input, no data) so we can fix it fast.

### Related Actors

- [Merge, Filter, Deduplicate & Transform Datasets](https://apify.com/tidydata/merge-dedupe-datasets) — clean and combine the results, export CSV or Excel.
- [Bulk ChatGPT & Claude: Classify, Extract & Translate Datasets](https://apify.com/tidydata/ai-dataset-enricher) — enrich rows with AI instead of an API.
- [Google Sheets Export & Import](https://apify.com/tidydata/google-sheets-export-import) — send the results to a Google Sheet.
- [URL Status Checker](https://apify.com/tidydata/url-status-checker) — check thousands of links for status codes and redirects.

# Actor input Schema

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

The API endpoint or page to call, e.g. https://api.example.com/v1/users/{{id}}. With rows (dataset, CSV, Google Sheet) write {{column}} placeholders: values are URL-encoded, slashes kept ({{column|raw}} to insert as is). Leave empty for a free demo.

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

HTTP method.

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

Request headers as JSON, e.g. {"Authorization": "Bearer YOUR_KEY", "Accept": "application/json"}. Values can use {{column}}. Put secrets here: the input is private to your account.

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

Added to the URL as ?key=value (URL-encoded for you), e.g. {"q": "{{company}}", "limit": 10}.

## `data` (type: `object`):

Request body for POST/PUT/PATCH. A JSON object is sent as application/json (or as a form with Body type = form). A field that is exactly "{{column}}" keeps the column's type (number, list, object).

## `bodyType` (type: `string`):

How the body is encoded.

## `lookupKey` (type: `string`):

Any text returned unchanged in the result and in the callback, so Clay, n8n or Make can match the answer to the right row.

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

A list of requests, each like {"url": "…", "method": "POST", "headers": {…}, "params": {…}, "data": {…}, "lookupKey": "row-1"}. Missing fields use the values above. Plain URLs work too.

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

One URL per line, all sent with the method, headers, params and body above.

## `datasetIds` (type: `array`):

Pick datasets from your storages (or paste their IDs): the URL, headers, params and body above are filled with each row's {{columns}}. As an integration after another Actor use {{resource.defaultDatasetId}}.

## `fileUrls` (type: `array`):

Public links to .csv, .xlsx or .json files, or Google Sheets shared as 'Anyone with the link can view'. One request per row.

## `rows` (type: `array`):

Rows typed directly, e.g. \[{"id": 1}, {"id": 2}]. One request per row.

## `maxRows` (type: `integer`):

Safety limit on rows read from datasets and files.

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

Return JSON responses as data (not as text).

## `extract` (type: `object`):

New column → path in the JSON response, e.g. {"price": "data.items\[0].price", "total": "meta.total"}. Great for clean tables and Clay columns.

## `itemsPath` (type: `string`):

If the response contains a list (e.g. data.results), give its path to get one dataset row per item. Use . when the response itself is a list.

## `dropResponse` (type: `boolean`):

Keep only the extracted columns (smaller, cleaner rows).

## `keepInputFields` (type: `boolean`):

With rows from a dataset/file, copy each row's fields into its result (enrichment). Same-name fields get an input\_ prefix.

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

Add the response headers to each result.

## `maxResponseKb` (type: `integer`):

Longer responses are cut (marked truncated). 2 MB max through residential proxies.

## `saveFiles` (type: `boolean`):

Store non-text responses in the run's key-value store and return a fileUrl.

## `paginate` (type: `string`):

Follow the API's pages automatically. Each page is one request (one event).

## `pageParam` (type: `string`):

Query parameter for the page number (or write {{page}} in the URL).

## `pageStart` (type: `integer`):

First page number (use 0 for zero-based APIs, or an offset).

## `pageStep` (type: `integer`):

Add this to the page number each time (e.g. 100 for offset pagination).

## `nextUrlPath` (type: `string`):

Path of the next page's URL in the JSON, e.g. next or links.next. Empty = use the Link header (GitHub style).

## `nextCursorPath` (type: `string`):

Path of the next cursor in the JSON, e.g. meta.next_cursor (cursor pagination).

## `cursorParam` (type: `string`):

Query parameter that receives the cursor.

## `stopWhenEmptyPath` (type: `string`):

Path of the list that ends the pagination when empty (defaults to the items path).

## `maxPages` (type: `integer`):

Safety limit of pages per starting request.

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

How many requests run at the same time.

## `perHostPerSecond` (type: `integer`):

Be kind to APIs and avoid 429 errors (0 = no limit).

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

Give up on a request after this time.

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

Retries on network errors, 429 and 5xx answers, with back-off and Retry-After. GET, HEAD, PUT, DELETE and OPTIONS only, unless you allow POST/PATCH below.

## `retryUnsafe` (type: `boolean`):

Only if repeating the call is safe for your API (it may create duplicates).

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

Follow 301/302/307/308 redirects (up to 10).

## `ignoreTlsErrors` (type: `boolean`):

Only for test servers with self-signed certificates.

## `failOnErrorStatus` (type: `boolean`):

Fail the run if any request got an HTTP error or no response (useful to trigger alerts). Results are saved anyway.

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

Each result is also POSTed as JSON to this URL (e.g. a Clay webhook table, n8n/Make/Zapier webhook, Slack or Discord incoming webhook).

## `callbackHeaders` (type: `object`):

Extra headers for the callback, e.g. {"x-clay-webhook-auth": "…"}.

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

Optional: send the requests through Apify Proxy (datacenter or residential, by country) or your own proxies. Datacenter and residential requests have their own small extra price.

## Actor input object example

```json
{
  "url": "https://jsonplaceholder.typicode.com/posts/1",
  "method": "GET",
  "bodyType": "auto",
  "maxRows": 100000,
  "parseJson": true,
  "dropResponse": false,
  "keepInputFields": true,
  "includeHeaders": false,
  "maxResponseKb": 1024,
  "saveFiles": false,
  "paginate": "none",
  "pageParam": "page",
  "pageStart": 1,
  "pageStep": 1,
  "cursorParam": "cursor",
  "maxPages": 10,
  "maxConcurrency": 10,
  "perHostPerSecond": 10,
  "timeoutSecs": 30,
  "maxRetries": 2,
  "retryUnsafe": false,
  "followRedirects": true,
  "ignoreTlsErrors": false,
  "failOnErrorStatus": false,
  "proxyConfiguration": {
    "useApifyProxy": false
  }
}
```

# Actor output Schema

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

One row per result.

# 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 = {
    "url": "https://jsonplaceholder.typicode.com/posts/1",
    "proxyConfiguration": {
        "useApifyProxy": false
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("tidydata/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 = {
    "url": "https://jsonplaceholder.typicode.com/posts/1",
    "proxyConfiguration": { "useApifyProxy": False },
}

# Run the Actor and wait for it to finish
run = client.actor("tidydata/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 '{
  "url": "https://jsonplaceholder.typicode.com/posts/1",
  "proxyConfiguration": {
    "useApifyProxy": false
  }
}' |
apify call tidydata/http-request --silent --output-dataset

```

## MCP server setup

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