# HTTP Request & API Calls: Bulk, Pagination, JSON to Rows (`charlesmarkette/http-request-api-calls-bulk-pagination`) Actor

Send GET, POST, PUT, PATCH or DELETE requests to any REST API in bulk: headers, JSON body, a secret auth header, per-row templates from a dataset, automatic pagination, retries, JSON fields to columns and a callback URL. Made for Clay, n8n and Make.

- **URL**: https://apify.com/charlesmarkette/http-request-api-calls-bulk-pagination.md
- **Developed by:** [Charles Markette](https://apify.com/charlesmarkette) (community)
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $3.50 / 1,000 request sents

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 & API Calls** sends HTTP requests to any REST API in bulk and saves each response as a dataset row. You can use **GET, POST, PUT, PATCH, DELETE**, HEAD or OPTIONS, with headers, query parameters and a JSON or text body. API keys go in a **secret auth header** that's stored encrypted and never written to the output. You can also build one request per item of an Apify dataset from a **URL and body template**, fetch **every page** of a paginated API (next URL, page number, offset, cursor or `Link` header), and turn the JSON into rows and columns (**split an items array into rows**, **extract fields to columns**). Failed requests are retried with backoff, and each row can be **POSTed to a callback URL** (an n8n, Make or Zapier webhook, or your own server).

### Why this Actor

- **Many requests per run, one price.** Paste 10 URLs or 100,000 request objects. They run in parallel with a per-host limit and an optional delay between calls to the same host, and the rows come out **in input order**.
- **Your API key stays secret.** The auth header value is an encrypted input field. Header values are never written to the dataset, log, run summary or status message. Secret-looking query parameters (`api_key`, `token`, `signature`…) are shown as `***` in the URL columns. If an API echoes your key back in its response, the key is replaced with `***` there too. Credentials aren't forwarded when a redirect or a next-page link points to another host.
- **Pagination built in.** Follow `next` links in the JSON, count `?page=1, 2, 3…` or `?offset=0, 100, 200…`, pass a cursor, or follow RFC 8288 `Link: <…>; rel="next"` headers (GitHub-style). Pagination stops at an empty page, a missing or repeated next link or cursor, a non-2xx response, a page identical to an earlier one, or *Max pages*.
- **JSON to rows.** Set *Items path* (`data.items`) and *Split items* to get one row per element, and use *Extract fields* to make columns from paths like `address.city` or `items.0.id`.
- **Dataset enrichment.** Pick a dataset (leads, companies, products…) and a template such as `https://api.example.com/companies/{{domain}}`. Each item gets one request, and its fields stay next to the response columns.
- **Reliable.** Network errors, `429` and `5xx` responses are retried with exponential backoff, and `Retry-After` is honoured (up to 60 s). Requests that never got a response are **free**.
- **Safe by default.** Requests to private, loopback, link-local, CGNAT and cloud-metadata addresses are refused, and so are redirects to them.

### Use cases

- **Clay, n8n, Make or Zapier users:** call an API from a no-code flow (one run per row or one run per batch) and receive each response at a webhook, without writing a server.
- **Enrich a scraped dataset** with your CRM, an enrichment API or your own backend: one call per lead, company or product.
- **Export a whole paginated API** (all orders, tickets, contacts, issues) to a dataset, then to CSV, Excel, Google Sheets or SFTP.
- **Bulk create or update records** with POST/PUT/PATCH from a list of JSON bodies.
- **Scheduled API polling:** fetch a status or metrics endpoint every hour and keep the history in datasets.

### How to use it

1. Add your calls: **URLs** (one per line, sent with the shared settings) and/or **Requests** (a JSON array of request objects). Or pick a **Dataset to enrich** and write a **URL template**.
2. Set the shared **Method**, **Headers**, **Query parameters** and **JSON body** if your calls have them in common. A request object's own `method`, `headers`, `params`, `json` and `body` override them for that request.
3. Put your API key in **Auth header name** and **Auth header value** (for example `Authorization` and `Bearer sk_live_…`, or `X-API-Key` and the key).
4. Optional: **Pagination**, **Items path**, **Split items into rows**, **Extract fields**, **Callback URL**.
5. Run it, then export the dataset or connect it to an integration.

**Input example**: POST to an API for each request, with a shared secret key.

```json
{
  "requests": [
    {"url": "https://api.example.com/v1/contacts", "method": "POST", "json": {"email": "ada@example.com"}, "label": "ada"},
    {"url": "https://api.example.com/v1/contacts/123", "method": "PATCH", "json": {"tags": ["vip"]}, "label": "123"},
    {"url": "https://api.example.com/v1/contacts/456", "method": "DELETE"}
  ],
  "authHeaderName": "Authorization",
  "authHeaderValue": "Bearer sk_live_…",
  "extractFields": {"contactId": "data.id"}
}
```

**Dataset enrichment example**: one call per company in a dataset.

```json
{
  "datasetId": "<dataset ID>",
  "urlTemplate": "https://api.example.com/companies/{{domain}}?fields=size,industry",
  "authHeaderName": "X-API-Key",
  "authHeaderValue": "…",
  "extractFields": {"employees": "company.size", "industry": "company.industry"}
}
```

`{{field}}` takes the item's field (dotted paths work: `{{company.website}}`, `{{tags.0}}`). `{{field|url}}` percent-encodes the value for a query string or path. A *Body template* such as `{"name": "{{name}}", "employees": {{size}}}` inserts the value as escaped text inside quotes and as JSON (number, `true`, `null`, object) outside quotes. Items missing a field used in the URL template become free error rows. With *Keep the item's original fields* on, the item's fields come first in each row. An item field with the same name as one of the response columns is kept as `item_<name>`.

**Paginated export example**: every page of an API, one row per item.

```json
{
  "urls": ["https://api.example.com/v1/orders"],
  "queryParams": {"status": "paid"},
  "pagination": "cursorFromJson",
  "cursorPath": "meta.next_cursor",
  "cursorParam": "cursor",
  "itemsPath": "data",
  "splitItems": true,
  "maxPages": 200,
  "extractFields": {"orderId": "id", "total": "amount.total"}
}
```

**Output example**: GET `https://jsonplaceholder.typicode.com/users/1` with `"extractFields": {"name": "name", "city": "address.city"}`.

```json
{
  "requestUrl": "https://jsonplaceholder.typicode.com/users/1",
  "method": "GET",
  "label": "user-1",
  "page": 1,
  "statusCode": 200,
  "ok": true,
  "finalUrl": "https://jsonplaceholder.typicode.com/users/1",
  "contentType": "application/json; charset=utf-8",
  "durationMs": 87,
  "responseBytes": 509,
  "truncated": false,
  "json": {"id": 1, "name": "Leanne Graham", "address": {"city": "Gwenborough"}},
  "text": null,
  "name": "Leanne Graham",
  "city": "Gwenborough",
  "error": null,
  "requestedAt": "2026-10-19T12:00:00.000Z",
  "requestIndex": 0
}
```

- `json` holds the parsed response, or the array element when *Split items* is on (then `itemIndex` gives its position).
- `text` holds the body when it isn't JSON.
- `error` is `null` for a 2xx response, `"HTTP 404 Not Found"` (etc.) for other status codes, or the reason a request got no response (`Timed out`, `Connection refused`, `DNS lookup failed (host not found)`, `Blocked: private or local network address`, or an input problem).
- Optional columns: `itemCount` (with *Items path*), `responseHeaders` (with *Include response headers*; Set-Cookie is left out) and `callbackStatus` / `callbackError` (with a *Callback URL*).

A run summary (requests sent, extra pages, rows, failures, charged events, and why the run stopped) is saved as `OUTPUT` in the key-value store.

### Use it from Clay, n8n or Make

You don't need a server or an HTTP step on a higher plan. The tool you use only has to start an Apify Actor run and pass the input JSON, either through its Apify integration if it has one, or with an HTTP call to Apify's API. Then pick one of two ways to get the responses back:

- **Read the dataset.** Start the run and read its dataset when it finishes. Apify's API has an endpoint that runs an Actor and returns its dataset items in the same call (`run-sync-get-dataset-items`), which suits short runs of up to a few minutes. For longer runs, start the run, wait for it to finish (or use an Apify webhook on run success), and then fetch the dataset items.
- **Callback per row.** Set *Callback URL* to a webhook URL your tool gives you (an n8n Webhook trigger, a Make custom webhook, a Zapier catch hook, or a table or endpoint that accepts incoming webhooks). Each output row is POSTed there as JSON as soon as it's ready, with `label` and `requestIndex` so you can match it to the record that triggered it. Your auth header is never sent to the callback.

**One run per row or one run per batch.** Every run has a small run-start charge, so if your tool can collect rows, send them as one `requests` array (or put them in a dataset and use the URL template) rather than starting a run per row.

### How much does it cost?

Pay per event: **$0.005 per request sent**, plus Apify's **$0.00005 run start**. Apify platform usage is included.

- A **request sent** is one HTTP request that got an HTTP response. Any status code counts, including 404 and 500. Each page of a paginated request is one request sent. Retries and redirect hops of the same request aren't charged extra.
- In ***Split items into rows*** mode only, each item row also costs **$0.0001**. A page with 100 items costs $0.005 + 100 × $0.0001 = $0.015.
- **Free:** rows for invalid input (a bad URL, a missing template field), requests to blocked private addresses, and requests that failed without a response after all retries (DNS errors, refused connections, timeouts). The $0.00005 run start applies to every run, even one where no request gets a response.

**Apify Store discounts:** the price above is for Apify's Free plan. Paid plans pay less per request sent: $0.0045 on Starter, $0.004 on Scale, $0.0035 on Business. The "from" price at the top of the Store page is the Business price.

**Worked examples:**

*Examples use the Free-plan rate. Where an example doesn't add it, each run also has its run-start charge, counted per GB of Actor memory (minimum one; one at the default memory).*

- 1,000 API calls in one run: 1,000 × $0.005 + $0.00005 = **$5.00005**.
- Enrich a dataset of 300 companies: 300 × $0.005 = $1.50, plus the run start = **$1.50005**.
- Export 40 pages of 100 orders as one row per order (*Split items*): 40 × $0.005 + 4,000 × $0.0001 = $0.20 + $0.40 = **$0.60** (+ run start). The same export as one row per page: 40 × $0.005 = **$0.20**.
- An hourly status poll of 3 endpoints: 3 × $0.005 + $0.00005 = $0.01505 per run, about **$10.84 per month** (720 runs).

If you set a maximum cost per run, the Actor stops **before** sending a request it can't pay for. Rows already written stay in the dataset, and the summary says `"stoppedBy": "max cost"`. If a run is restarted (for example after a migration or a timeout), it continues after the rows already in its dataset, and pages already charged aren't charged again.

### FAQ and limits

#### Does it use proxies?

No, not in this version. Requests go out from Apify's servers directly. Proxy traffic would be billed to the Actor's developer, so it isn't offered at this price. Most APIs don't need a proxy. Websites with bot protection may answer 403 or 429, and this Actor reports the status it actually got.

#### Does it render JavaScript or scrape web pages?

No. It's a plain HTTP client: it doesn't run a browser or JavaScript, and it doesn't solve captchas. For an HTML page you get the raw HTML in `text`.

#### What happens with big responses?

Bodies are read up to *Max response size* (default 5 MB, up to 20 MB). Anything beyond that is cut off and the row gets `truncated: true`. A cut JSON response no longer parses, so it's stored as `text`. A single row is also kept under about 8 MB so it fits in the dataset; a bigger body is stored as cut-off `text`. Large responses and high parallelism need more memory, so raise the run's memory if you fetch many large responses at once.

#### Is POST retried?

Yes. Network errors, 429 and 5xx responses are retried for every method (*Retries*, default 2). If your API isn't safe to call twice, set *Retries* to 0.

#### Can I call internal, localhost or private-network APIs?

No. Private, loopback, link-local, CGNAT (100.64.0.0/10), multicast, reserved and cloud-metadata addresses (IPv4 and IPv6, including IPv4-mapped IPv6) are refused, for the first request, every redirect hop and the callback URL. The host is resolved first and every address it returns is checked. The connection is then opened to the address that was checked (the host name is still used for TLS), so a DNS answer that changes between the check and the connection can't reach a private address. Refused requests are free rows with `Blocked: private or local network address`.

#### How are my credentials protected?

- The auth header value is a secret input, stored encrypted by Apify.
- Request header values are never written to the output, log, `OUTPUT` summary or status message.
- In `requestUrl` and `finalUrl`, the values of query parameters whose names look like secrets (`key`, `api_key`, `apikey`, `token`, `access_token`, `secret`, `password`, `sig`, `signature`, names ending in `_key`, `_token` or `_secret`, and a few others) and any `user:password@` are replaced with `***`.
- If a response contains your auth value, a key-like header value or a secret query value (8 characters or longer), that value is replaced with `***` in `json`, `text` and `responseHeaders`.
- Credentials (`Authorization`, `Cookie` and your auth header) aren't sent when a redirect or a next-page URL goes to a different host.

#### Other limits

- Pagination parameters (page, offset, cursor) are added to the URL's query string. Pagination inside a POST body isn't supported, and the same body is sent for every page.
- Pagination settings apply to every request in the run, because the page, offset or cursor parameter is added to each request's URL. An API that rejects unknown query parameters (or a signed URL) can fail on that. To leave a request out, give it `"paginate": false` in the *Requests* list, or run unpaginated calls separately.
- HTTP/1.1 only. Responses compressed with gzip or deflate are decoded; the Actor doesn't ask for Brotli or zstd.
- No cookie jar: cookies set by a response aren't sent with later requests. Send a `Cookie` header yourself if an API needs one.
- No multipart file uploads. Binary responses (images, PDFs, archives) aren't stored; you get the status, content type and size.
- At most 100,000 requests per run, 1,000 pages per request, 50 parallel requests and 10 per host.
- Response times are measured from Apify's servers.

### Other tools by the same author

| Actor | Use it to |
|---|---|
| [Dataset Delivery](https://apify.com/charlesmarkette/dataset-delivery-export-to-sftp-ftp-dropbox-google-drive) | Deliver the API results as CSV, JSON or Excel to SFTP, FTP, Dropbox or Google Drive. |
| [Google Sheets Import & Export](https://apify.com/charlesmarkette/google-sheets-sync) | Send the rows to a Google Sheet, or read a sheet to build the requests. |
| [Notify Step](https://apify.com/charlesmarkette/send-email-report-slack-discord-teams-telegram-alerts) | Email or Slack a report when a scheduled API check finds errors. |
| [Merge & Deduplicate Datasets](https://apify.com/charlesmarkette/merge-deduplicate-datasets-only-new-items) | Keep only new records between scheduled API exports. |
| [Dataset Filter & Transform](https://apify.com/charlesmarkette/dataset-filter-transform-rename-flatten-fields) | Filter the rows, rename and flatten the JSON fields into clean columns. |
| [Dataset Join & VLOOKUP](https://apify.com/charlesmarkette/dataset-join-vlookup-merge-by-key) | Merge the API responses back into another dataset by a key such as an ID or email. |
| [Bulk URL Status Checker](https://apify.com/charlesmarkette/bulk-url-status-checker-redirects-ssl) | Check that web pages load, with their redirect chains and SSL certificates, instead of calling an API. |

# Actor input Schema

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

A JSON array of requests. Each one is an object: {"url": "https://…", "method": "POST", "headers": {…}, "params": {…}, "json": {…}, "body": "text", "label": "my-id", "paginate": false}. Only url is required; "paginate": false leaves that request out of the Pagination setting; the other keys override the shared settings below for that request. A plain URL string also works.

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

Shortcut: one URL per line. Each is sent with the shared method, headers, params and body below.

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

HTTP method for every request that doesn't set its own.

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

Headers for every request, like {"Accept": "application/json"}. Put API keys in the secret auth header below instead, so they're stored encrypted. Header values are never written to the output or the log.

## `queryParams` (type: `object`):

Added to every URL, like {"limit": 50, "lang": "en"}. A list value repeats the parameter (?id=1\&id=2). A parameter already in the URL with the same name is replaced.

## `jsonBody` (type: `object`):

Sent as the request body (Content-Type: application/json) for every request without its own body. Leave empty for GET. For a JSON array body, use the 'json' key of a request object.

## `textBody` (type: `string`):

Raw body text (XML, form data, GraphQL…) instead of a JSON body. Set the content type below.

## `contentType` (type: `string`):

Content-Type for request bodies. Default: application/json for a JSON body, text/plain for a text body. With application/x-www-form-urlencoded, a JSON object body is sent as form fields.

## `authHeaderName` (type: `string`):

Name of the header that carries your API key, like Authorization, X-API-Key or api-key.

## `authHeaderValue` (type: `string`):

The full header value, like Bearer sk_live\_… or just the key for X-API-Key. A request object that sets the same header itself keeps its own value.

## `datasetId` (type: `string`):

Optional. One request per item of this dataset (leads, companies, products…), built from the URL template below. Use {{resource.defaultDatasetId}} in an integration.

## `urlTemplate` (type: `string`):

Like https://api.example.com/companies/{{domain}} or https://api.example.com/search?q={{name|url}}. {{field}} takes the item's field (dotted paths work: {{company.website}}, {{tags.0}}); |url percent-encodes the value. Items missing a field used here become a free error row.

## `bodyTemplate` (type: `string`):

Optional JSON body with placeholders, like {"name": "{{name}}", "employees": {{size}}}. Inside quotes the value is inserted as escaped text; outside quotes it's inserted as JSON (number, true/false, null, object).

## `keepItemFields` (type: `boolean`):

On: each row starts with the item's fields, followed by the response columns. An item field with the same name as a response column (or an extracted field) is kept as item\_<name>.

## `pagination` (type: `string`):

Fetch more pages of each request. It stops at an empty items array, a missing or repeated next link or cursor, a non-2xx response, a page identical to an earlier one, or Max pages.

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

For 'Next URL from a JSON field': dotted path to the next page's URL in the response, like next or links.next or paging.next. Relative URLs work.

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

For 'Page number parameter': the query parameter name. Default: page.

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

For 'Page number parameter'. Default: 1.

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

For 'Page number parameter': how much the page number grows each time. Default: 1.

## `offsetParam` (type: `string`):

For 'Offset parameter': the query parameter name. Default: offset.

## `offsetStart` (type: `integer`):

For 'Offset parameter'. Default: 0.

## `limitParam` (type: `string`):

For 'Offset parameter': optional parameter that sets the page size, like limit or per_page.

## `pageSize` (type: `integer`):

For 'Offset parameter': how many items one page holds. The offset grows by this much, and a page with fewer items (found with Items path) is the last one.

## `cursorPath` (type: `string`):

For 'Cursor from a JSON field': dotted path to the next cursor in the response, like meta.next_cursor or response_metadata.next_cursor.

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

For 'Cursor from a JSON field': the query parameter that takes the cursor. Default: cursor.

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

Including the first page. Each page is one request sent (charged as one request).

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

Dotted path to the array of results in the JSON response, like data or data.items or results. Used to stop pagination at an empty page, for Split items, and adds an itemCount column. Leave empty when the response itself is an array.

## `splitItems` (type: `boolean`):

On: each element of the items array becomes its own row (with the request and page details). Off: one row per response. Split rows cost a small extra per row; see the pricing.

## `extractFields` (type: `object`):

Turn JSON fields into columns: {"column name": "dotted.path"}, like {"name": "name", "city": "address.city", "firstId": "items.0.id"}. Applied to each response (or each item with Split items).

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

Auto: parsed into the json column when the content type is JSON or the body parses as JSON, else stored in text. JSON: always parse (an error is noted if it isn't JSON). Text: always store the raw text.

## `includeResponseHeaders` (type: `boolean`):

Add a responseHeaders column (lowercase names; Set-Cookie is left out).

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

Bigger responses are cut at this size and marked truncated: true (a cut JSON response is stored as text). Up to 20 MB (20971520).

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

Optional. Each output row is also POSTed as JSON to this URL (an n8n, Make or Zapier webhook, a Clay webhook table, your server). The result is noted in callbackStatus / callbackError; a failed callback never stops the run. Your auth header is never sent to it.

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

Seconds per attempt, from connecting to the last byte.

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

Retries on network errors, 429 and 5xx responses, with backoff (Retry-After is honoured up to 60 s). This applies to POST too: set 0 if the API isn't safe to call twice. Retries aren't charged.

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

Requests in flight at the same time, across all hosts (1–50).

## `perHostConcurrency` (type: `integer`):

At most this many at a time to any one host (1–10). Keep it low for rate-limited APIs.

## `delayBetweenRequestsMs` (type: `integer`):

Minimum time between two requests to the same host, for APIs with a requests-per-second limit (1000 = one per second).

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

Up to 10 hops. Off: a 3xx response is stored as it is.

## `maxResults` (type: `integer`):

Optional cap on output rows. The run stops once it's reached.

## Actor input object example

```json
{
  "requests": [
    {
      "url": "https://jsonplaceholder.typicode.com/users/1",
      "label": "user-1"
    }
  ],
  "urls": [
    "https://httpbin.org/get"
  ],
  "method": "GET",
  "authHeaderName": "Authorization",
  "keepItemFields": true,
  "pagination": "none",
  "pageStart": 1,
  "pageStep": 1,
  "offsetStart": 0,
  "pageSize": 100,
  "maxPages": 10,
  "splitItems": false,
  "responseFormat": "auto",
  "includeResponseHeaders": false,
  "maxResponseBytes": 5242880,
  "timeoutSecs": 30,
  "maxRetries": 2,
  "concurrency": 5,
  "perHostConcurrency": 2,
  "delayBetweenRequestsMs": 0,
  "followRedirects": true
}
```

# Actor output Schema

## `items` (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://jsonplaceholder.typicode.com/users/1",
            "label": "user-1"
        }
    ],
    "urls": [
        "https://httpbin.org/get"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("charlesmarkette/http-request-api-calls-bulk-pagination").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://jsonplaceholder.typicode.com/users/1",
            "label": "user-1",
        }],
    "urls": ["https://httpbin.org/get"],
}

# Run the Actor and wait for it to finish
run = client.actor("charlesmarkette/http-request-api-calls-bulk-pagination").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://jsonplaceholder.typicode.com/users/1",
      "label": "user-1"
    }
  ],
  "urls": [
    "https://httpbin.org/get"
  ]
}' |
apify call charlesmarkette/http-request-api-calls-bulk-pagination --silent --output-dataset

```

## MCP server setup

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

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/v6ltkdymGuwSz1WZw/builds/5Z698ebvCG6rEr5x0/openapi.json
