# HTTP API Request Runner (`luxe_tasks/http-runner`) Actor

Call APIs you are authorized to use with your own key in an encrypted secret field. $2 per 1,000 requests answered 2xx/3xx; $0.005-$0.01 start fee per run.

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

## Pricing

from $2.00 / 1,000 successful 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 API Request Runner

Run bulk HTTP API requests (GET, POST, PUT, PATCH, DELETE, HEAD, OPTIONS) against **REST APIs you are authorized to use**, with **your own API key** kept in an encrypted Apify secret field. List the request URLs, set one request profile (method, optional JSON body, optional headers, key placement), and get one dataset record per URL with the status, selected headers and the response body.

**Cost:** from $2 per 1,000 requests answered 2xx or 3xx, plus a start fee of $0.005 per run at the default 1 GB of memory ($0.01 at the 2 GB maximum). Failed, refused and skipped requests have no per-request charge, but the run's start fee still applies.

**Try it:** the input is prefilled with a public test endpoint. Press Start and open the Results view.

This is an API tool, not a scraper. There is no crawling, no link following, no login flow, no cookie import and no proxy.

### Your credentials

> You may supply only credentials for accounts you own or are authorized to use, for APIs whose terms permit access through automation tools. Your credentials are encrypted by Apify and used only to make the requests you specify in this run; we do not log or store them. You are responsible for complying with the target API's terms and rate limits. Do not use this Actor to access systems you are not authorized to access.

How the Actor protects them:

- **Secret inputs only.** Keys may be given only in `apiKey` or `secretHeaders`, both encrypted Apify secret inputs. A run is refused when:
  - a plain header looks like auth (`Authorization`, `Cookie`, `X-Api-Key`, or any name containing auth/token/key/secret/cookie/session);
  - a URL carries a credential-named query parameter;
  - a body field named like a credential holds a literal value instead of `{{apiKey}}`.
- **Never logged or stored.** Logs show only hostnames, statuses, timings and sizes, never URLs, query strings, headers or bodies. The key is never written to the dataset, the key-value store or an error message. If the API echoes your key back, it is masked as `[secret]`; a binary body containing it is omitted.
- **Never forwarded to another origin.** Auth headers, the query key and the body placeholder are used only on the origin of the URL you gave. On a redirect to another origin they are dropped. If the redirect URL or body would carry the key to that origin, the request is refused.

### How to call a REST API with a bearer token

1. Put the endpoint URLs in **Request URLs** (one request per URL).
2. Paste your key into **API key (secret)** (encrypted by Apify) and keep **API key placement** = `bearer`, or choose `header`, `query`, `basic` or `bodyPlaceholder`.
3. Tick the credential attestation (required whenever you supply a key).
4. Optional: set **HTTP method** and a JSON **Request body**. Write methods also need the attestation.
5. Press **Start**. Each record shows `httpStatus`, `response.body` and whether it was charged.

### Input

| Field | Meaning |
|---|---|
| `requests` | List of request URLs (http/https; up to the kit's per-run limit; duplicates removed). |
| `method` | GET (default), POST, PUT, PATCH, DELETE, HEAD or OPTIONS, for every URL. Write methods (POST, PUT, PATCH, DELETE, and OPTIONS with a body) need `credentialOwnershipAttestation`, may reach at most 3 registered domains per run (redirects included; `a.example.com` and `b.example.com` count as one), and never send form bodies (`application/x-www-form-urlencoded` or `multipart/form-data`; a form `Content-Type` is refused with every method). GET and HEAD keep the normal per-run URL limit; the 1 request per second per origin limit applies to every method. |
| `requestBody`, `bodyContentType` | Optional body (not with GET or HEAD; up to 100,000 characters). `{{apiKey}}` is accepted only in a body that parses as JSON, whatever the content type says, and a `Content-Type` header must match `bodyContentType`. Credential-named fields must be `{{apiKey}}`, except password-like fields, which may never hold `{{apiKey}}`. A name is password-like when, case-insensitively, it contains `pass` (so also `password`, `passcode`, `passwd`, `passphrase`, `motdepasse`, `mot_de_passe`), `pwd`, `pswd`, `kennwort`, `contrasena` or `contraseña`, `senha`, `parola` or `wachtwoord` anywhere, or has `pw` as a separate token (`pw`, `user_pw`, `pw-hash`, `pw.x`; not inside a word such as `upward` or `npw`). Names such as `passport` or `bypass` are refused too (a known false positive: rename the field or send the key another way). These checks can refuse a legitimate request; rename the field, or send the key in a header instead. A JSON string holding `{{apiKey}}` is also refused when that string contains a password-like name anywhere (for example a GraphQL `password:` argument, or JSON or form data inside the string), or when another value in the same JSON object is password-like (for example `{"type": "password", "value": "{{apiKey}}"}`). |
| `customHeaders` | Optional plain headers: content negotiation, caching, or `X-*`. No auth, cookies, User-Agent, Host, forwarding, client-IP or method-override headers, and no password-like names (see `requestBody`). |
| `apiKey` *(secret)* | Your key. |
| `apiKeyIn` | `bearer` (default, `Authorization: Bearer …`), `header` (name in `apiKeyHeaderName`, default `X-API-Key`), `query` (name in `apiKeyQueryParam`, default `api_key`), `basic` (key = `user:password`, sent as the `Authorization` header), or `bodyPlaceholder` (replaces `{{apiKey}}` in a JSON body). The key's query-parameter name (`apiKeyQueryParam`) or header name (`apiKeyHeaderName`) cannot be a password-like name (see `requestBody`); the run is refused. |
| `secretHeaders` *(secret)* | Optional JSON object of other auth headers for your own account. Cookies, session and method-override headers, and password-like names (see `requestBody`), are refused. |
| `credentialOwnershipAttestation` | Required when you supply a secret, and with POST, PUT, PATCH or DELETE (or OPTIONS with a body): confirms the first sentence of the credentials paragraph above and Acceptable use item 6. |
| `followRedirects`, `maxRedirects` | Follow up to 0–5 redirects (default: on, 5). Every hop is re-checked. |
| `timeoutSecs` | 1–60 s per request hop (default 30). |
| `maxResponseBytes` | 1 KiB–5 MiB (default 1 MiB), after decompression. Larger responses become `response_too_large`. |
| `responseFormat` | `auto` (JSON, else text, else base64), `json`, `text`, `base64` or `none`. |
| `includeResponseHeaders` | Include response headers (default on; Set-Cookie never kept; URL headers query-redacted). |
| `maxConcurrency` | Parallel requests across different origins (one at a time per origin). |

### Output

There is one dataset record per URL:

- **Common fields:** `index, url, status, ok, charged, chargedEvents, httpStatus, error, robots, timingMs`. `url` is query-redacted (see Known limitations).
- **`request`:** `{method, usedSecret}`. Request headers are never echoed.
- **`response`:** `{status, statusText, contentType, bytes, wireBytes, headers?, bodyFormat, body, secretInResponse}`.
- **Redirect fields:** `finalUrl` and `redirects` (query-redacted).
- **`stopCause`:** only when the response stopped the domain.

Statuses:

| Status | Meaning | Charged |
|---|---|---|
| `success` | final response 2xx or 3xx (for example 200, 204, 304, or a 301/302 when **Follow redirects** is off) | **yes** |
| `http_4xx`, `http_5xx` | other client and server errors; 5xx is retried up to 2 times for GET/HEAD/OPTIONS/PUT/DELETE only | no |
| `http_401_unauthorized`, `http_403_forbidden`, `captcha_detected`, `http_429_rate_limited` | the API refused | no; stops that registered domain for the rest of the run |
| `rate_limited` | the site's robots.txt answered 429 (see robots.txt below); the URL is not requested | no; stops that registered domain for the rest of the run |
| `skipped_blocked` | later URLs on a stopped domain | no |
| `timeout`, `response_too_large`, `network_error`, `dns_failure` | transport outcomes | no |
| `ssrf_blocked`, `apify_host_blocked`, `redirect_refused`, `robots_disallowed`, `robots_unreachable`, `invalid_input` | refusals | no |
| `uncharged_work_cap` | after 100 failed requests or unreachable robots.txt lookups (or 300 s spent on them, robots.txt time included) the rest of the run is not sent | no |
| `budget_reached` | your max-charge limit was reached | no |

`RUN_SUMMARY` in the key-value store has counts, charged events and stopped domains. `HTTP_RUNNER_NET` has request and byte counters.

### Limits and politeness (may only be tightened)

- **Rate:** at most about 1 request per second per origin, one at a time per origin. No faster override is offered.
- **robots.txt** is honoured for every origin. APIs whose robots.txt disallows the path are refused (`robots_disallowed`), with no override. If robots.txt redirects (up to 5 hops, each checked by the address guard and the Apify-host block, so a robots.txt redirect to an Apify platform host is never requested), its rules still apply to the host you asked for. When the rules give no clear answer the request is refused as `robots_disallowed` (the error text says it failed closed); when robots.txt cannot be read (5xx, network error, too many redirects, a refused hop) the request is skipped as `robots_unreachable`. A robots.txt that returns 4xx (except 429) means no restrictions; a 429 is treated as a rate limit and stops the domain; 5xx or an unreachable robots.txt means nothing is fetched. In detail: when robots.txt answers 429 with a `Retry-After` of 60 s or less (seconds or a date), the Actor waits and requests robots.txt once more; when there is no usable `Retry-After`, it is over 60 s, or the retry is also 429, the URL becomes `rate_limited` and the whole registered domain is stopped for the rest of the run (its later URLs become `skipped_blocked`). No page on that domain is requested and nothing is charged. None of these outcomes is charged.
- **Stops:** a 401, 403, CAPTCHA or 429 response stops the whole registered domain:
  - a 429 with `Retry-After` of 60 s or less is retried once, then the domain is stopped;
  - a longer or missing `Retry-After` stops the domain at once.
- **User-Agent:** fixed and honest, `HttpRunnerActor/0.1.0 (+https://apify.com/luxe_tasks/http-runner; honours robots.txt)`. No proxies and no IP rotation.
- **Blocked targets:**
  - private, loopback, link-local and cloud-metadata addresses (including 169.254.169.254 and IPv6 equivalents) are refused, re-checked after DNS and on every redirect;
  - Apify's own hosts (apify.com and its subdomains, and the platform hosts of the run) are refused as targets.
- **Response cap:** responses are limited by `maxResponseBytes`, including compressed bombs. Requests are limited by `timeoutSecs` per hop.

### Acceptable use

By running this Actor you confirm that:

1. You will call only APIs and accounts **you own or are authorized to use**, with **your own credentials**, and only where the API's terms permit access through automation tools.
2. You will **not** use it on websites or systems behind a login you are not authorized to use, and you will not supply someone else's key. The Actor has no login flow and does not accept cookies, browser sessions or passwords outside the secret fields.
3. You will **not** use it to get around CAPTCHAs, rate limits, IP blocks, or any other technical measure an API or site uses to restrict access. The Actor stops when it meets one.
4. You will comply with the target API's terms, rate limits and robots.txt, and with all applicable law, including data-protection law (GDPR, CCPA and similar). You are the controller of any personal data in your requests and results.
5. You will **not** use it to send spam, attack or overload a service, or harass, stalk or profile individuals.
6. You will not use write methods to submit forms, sign-ups, comments, messages or webhook calls to services you do not own or have permission to use, or to attempt logins.

We may refuse or block uses that violate these rules. Report problems through the Actor's **Issues** tab.

### Privacy

Results are written only to your run's default Apify storage. They are not copied, viewed or retained by the developer. Your credentials are decrypted only inside your run and are never logged or stored by this Actor.

If your requests or results contain personal data, you are the controller and the developer acts only as your processor under Apify's Standard Actor Contract. Apify's retention settings for your storages apply.

### Pricing

The price is pay per event, `http-request`: **$2.00 per 1,000 successful requests** (a request is successful when its final response is HTTP 2xx or 3xx), plus a start fee per run ($0.005 at up to 1 GB of memory, +$0.005 for each extra GB; max 2 GB, so at most $0.01).

- Only `success` records are charged. Failures, refusals, skipped and capped items have no per-request charge; the per-run start fee still applies.
- A final 2xx or 3xx answer is a successful request and is charged at $2 per 1,000 successful requests plus the $0.005-$0.01 per-run start fee, even with an empty body (for example 204 No Content or 304 Not Modified). A body cut off before its declared length is a `network_error` and has no per-request charge.
- **What you pay even when every request fails:** the start fee only, $0.005–$0.01 per run depending on memory. Apify charges it through its synthetic `apify-actor-start` event on every run: "Up to and including 1 GB of RAM, the event is charged once. Then, it's charged once for each extra GB of memory" ([Apify docs](https://docs.apify.com/platform/actors/publishing/monetize/pay-per-event#synthetic-start-event)). At the default 1024 MB (or the 512 MB minimum) that is $0.005 per run; at the 2048 MB maximum, $0.01.
- The synthetic `apify-default-dataset-item` event must stay unpriced. The Actor refuses to start if it is priced.

### Known limitations

- The record `url` field never shows a query string: if your URL has one, the whole query string (every parameter, not only credential-like ones) is replaced by `?[redacted]`, and any `#fragment` is dropped. For example, `https://api.example.com/v1/items?page=2&limit=50#top` is recorded as `https://api.example.com/v1/items?[redacted]`. The request itself is still sent with your full query string. To match records to your input, use the `index` field (the position of the URL in `requests`, starting at 0). Logs never show URLs.
- The method, body and headers are the same for every URL in a run. For a different request shape, use another run.
- Method-override headers (`X-HTTP-Method-Override`, `X-Method-Override`, `X-HTTP-Method`) and a `_method` query parameter (also spelled with `;` separators, a `[]` suffix, percent-encoding, `+`, a space or a dot for the underscore, in any case) are refused with every method; send the real method instead.
- robots.txt is honoured, so an API host whose robots.txt disallows all paths cannot be called.
- The Apify-host block works by hostname. Private and metadata IPs are blocked by address.
- Only `isSecret` input is protected from storage by Apify. Do not paste secrets into plain fields; the Actor refuses the ones it can recognise.

### Runtime

- Node 24 (Dockerfile `apify/actor-node:24`). Default memory is 1024 MB.

### FAQ

**How much do 1,000 API requests cost?** From $2 per 1,000 requests answered 2xx or 3xx plus the $0.005 start fee per run at the default 1 GB, so 1,000 requests answered 2xx or 3xx in one run cost $2.005 in total.

**Is my API key stored or logged?** No. HTTP API Request Runner accepts keys only in encrypted secret fields, never logs them, never writes them to results and masks them as `[secret]` if an API echoes them back.

**Can I scrape websites with it?** No. HTTP API Request Runner makes exactly one request per URL you list, with no crawling or link following, and it honours robots.txt. It is for APIs you are authorized to use.

**Which responses are charged?** Only `success` records (final 2xx or 3xx). Errors, refusals, rate-limited, skipped and capped requests have no per-request charge; the per-run start fee still applies.

### More tools from Luxe Tasks

Checking what a website (not an API) is built with? Use Tech Stack Detector.

| Tool | What it does |
|---|---|
| [Website Screenshot & URL to PDF Converter](https://apify.com/luxe_tasks/screenshot-pdf) | PNG/JPEG screenshots (viewport or full page) and PDFs of public web pages you choose. |
| [Tech Stack Detector](https://apify.com/luxe_tasks/tech-stack-detector) | Finds the CMS, JavaScript frameworks, analytics, CDN and web server a public website uses, with evidence. |
| [OCR Text Extractor](https://apify.com/luxe_tasks/ocr-text-extractor) | Extracts English text from scanned PDFs, PNG and JPEG images with Tesseract OCR that runs inside the Actor. |

Each tool is priced per result plus a per-run start fee; see that tool's Pricing section.

Developer: Luxe_tasks.

Contact: https://apify.com/luxe_tasks · Issues tab on the actor page (only once published).

# Actor input Schema

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

One HTTP request is made per URL (http/https; no crawling, links are never followed). Keys go in the secret fields below, never in the URL.

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

Method for every request. POST/PATCH are never retried. POST, PUT, PATCH and DELETE (and OPTIONS with a body) need the attestation below, reach at most 3 registered domains per run and never send form bodies (urlencoded or multipart).

## `requestBody` (type: `string`):

Optional body (not with GET/HEAD). Credential-named fields must hold {{apiKey}} (with "API key placement" = bodyPlaceholder).

## `bodyContentType` (type: `string`):

Content-Type of the request body.

## `customHeaders` (type: `object`):

Allowlist: Accept, Accept-Language, Content-Type, Cache-Control, Pragma, If-*, Prefer, Content-Language, DNT, or any X-* name. Identity, cookie, proxy, forwarding and transport headers are refused. Authentication headers (Authorization, Cookie, tokens, keys) are refused here; use the secret field.

## `apiKey` (type: `string`):

Your own key for the API you call. You may supply only credentials for accounts you own or are authorized to use, for APIs whose terms permit access through automation tools. Your credentials are encrypted by Apify and used only to make the requests you specify in this run; we do not log or store them. Encrypted by Apify (secret input); never logged, stored or returned by this Actor.

## `apiKeyIn` (type: `string`):

bearer = "Authorization: Bearer <key>"; header = the header named below; query = the query parameter named below; basic = key is user:password (HTTP Basic); bodyPlaceholder = replaces {{apiKey}} in the body. The key is sent only to the origin of each input URL, never across a redirect to another origin.

## `apiKeyHeaderName` (type: `string`):

Used when placement = header.

## `apiKeyQueryParam` (type: `string`):

Used when placement = query.

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

Optional JSON object of header name -> value for other auth headers of your own account (Cookie and session headers are refused). Encrypted by Apify (secret input); never logged, stored or returned by this Actor.

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

Each hop is re-checked (private networks, Apify hosts, robots.txt, rate limit). Secrets are dropped on a cross-origin hop.

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

0-5.

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

Per request (each redirect hop), headers and body included. Max 60.

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

Larger responses are refused, not charged. Max 5242880 (5 MiB).

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

auto = parsed JSON for JSON responses, text for text, base64 otherwise.

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

Set-Cookie headers are never included.

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

Items processed in parallel (each site still gets at most 1 request per second).

## `credentialOwnershipAttestation` (type: `boolean`):

Required with a secret, and with POST, PUT, PATCH or DELETE (or OPTIONS with a body): you may supply only credentials for accounts you own or are authorized to use, for APIs whose terms permit access through automation tools; and you will not use write methods to submit forms, sign-ups, comments, messages or webhook calls to services you do not own or have permission to use, or to attempt logins.

## Actor input object example

```json
{
  "requests": [
    "https://httpbin.org/get"
  ],
  "method": "GET",
  "requestBody": "",
  "bodyContentType": "application/json",
  "apiKeyIn": "bearer",
  "apiKeyHeaderName": "X-API-Key",
  "apiKeyQueryParam": "api_key",
  "followRedirects": true,
  "maxRedirects": 5,
  "timeoutSecs": 30,
  "maxResponseBytes": 1048576,
  "responseFormat": "auto",
  "includeResponseHeaders": true,
  "maxConcurrency": 3,
  "credentialOwnershipAttestation": false
}
```

# Actor output Schema

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

Default dataset, one record per request URL, shown in the 'Results' view: index, url (query string redacted), status (success or the reason, e.g. http_4xx, robots_disallowed, rate_limited, skipped_blocked, timeout), ok, httpStatus, charged, chargedEvents, request (method, usedSecret), response (status, contentType, bytes, headers, bodyFormat, body, secretInResponse), finalUrl, redirects, robots, timingMs and error. Only success records are charged.

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

RUN_SUMMARY record in the default key-value store: total URLs, counts by status, charged events, budget reached and domains stopped for the rest of the run.

## `network` (type: `string`):

HTTP_RUNNER_NET record in the default key-value store: request and byte counters for the run.

# 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": [
        "https://httpbin.org/get"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("luxe_tasks/http-runner").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": ["https://httpbin.org/get"] }

# Run the Actor and wait for it to finish
run = client.actor("luxe_tasks/http-runner").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": [
    "https://httpbin.org/get"
  ]
}' |
apify call luxe_tasks/http-runner --silent --output-dataset

```

## MCP server setup

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

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/JlHLXqA3PMmq8vJZb/builds/R57Q63XPUlpE3NWif/openapi.json
