HTTP Request & API Calls: Bulk, Pagination, JSON to Rows avatar

HTTP Request & API Calls: Bulk, Pagination, JSON to Rows

Pricing

from $3.50 / 1,000 request sents

Go to Apify Store
HTTP Request & API Calls: Bulk, Pagination, JSON to Rows

HTTP Request & API Calls: Bulk, Pagination, JSON to Rows

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.

Pricing

from $3.50 / 1,000 request sents

Rating

0.0

(0)

Developer

Charles Markette

Charles Markette

Maintained by Community

Actor stats

0

Bookmarked

2

Total users

1

Monthly active users

a day ago

Last modified

Categories

Share

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.

{
"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.

{
"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.

{
"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"}.

{
"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

ActorUse it to
Dataset DeliveryDeliver the API results as CSV, JSON or Excel to SFTP, FTP, Dropbox or Google Drive.
Google Sheets Import & ExportSend the rows to a Google Sheet, or read a sheet to build the requests.
Notify StepEmail or Slack a report when a scheduled API check finds errors.
Merge & Deduplicate DatasetsKeep only new records between scheduled API exports.
Dataset Filter & TransformFilter the rows, rename and flatten the JSON fields into clean columns.
Dataset Join & VLOOKUPMerge the API responses back into another dataset by a key such as an ID or email.
Bulk URL Status CheckerCheck that web pages load, with their redirect chains and SSL certificates, instead of calling an API.