# Proxy Rotation Manager - Pool Health Check & Rotate (`apricot_blackberry/proxy-rotation-manager`) Actor

Reliability layer for proxy-backed jobs. Health-checks every proxy in your pool (status, latency, exit IP), ranks the healthy ones, then fetches your targets with round-robin, least-recently-used, or sticky rotation and automatic retry. Credentials are always masked.

- **URL**: https://apify.com/apricot\_blackberry/proxy-rotation-manager.md
- **Developed by:** [Creator Fusion](https://apify.com/apricot_blackberry) (community)
- **Categories:** Developer tools, Automation
- **Stats:** 1 total users, 0 monthly users, 0.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

Pay per event

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/platform/actors/running/actors-in-store#pay-per-event

## What's an Apify Actor?

Actors are web data automations that power AI and operations. They run on the Apify platform to scrape websites, process data, connect APIs, and automate workflows.
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.
Actors are written with capital "A".

## 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.
The best way to integrate Actors is as follows.

- **AI agents and MCP clients** — the [Apify MCP server](https://docs.apify.com/integrations/mcp.md) at `https://mcp.apify.com` (remote, streamable HTTP, OAuth on first use).
- **Agentic workflows and local Actor development** — [Agent Skills](https://apify.com/.well-known/agent-skills/index.json) with the [Apify CLI](https://docs.apify.com/cli/docs.md): `npm install -g apify-cli`, then `apify login`.
- **JavaScript/TypeScript projects** — the official [JS/TS client](https://docs.apify.com/api/client/js/docs.md): `npm install apify-client`.
- **Python projects** — the official [Python client](https://docs.apify.com/api/client/python/docs.md): `pip install apify-client`.
- **Any other language** — the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).

# README

## Proxy Rotation Manager — Pool Health Check & Rotating Batch Fetch

Point this actor at a proxy pool and it does two things: **health-checks every proxy** against a test URL (latency, exit IP, up/down), then — if you give it target URLs — **fetches your batch through the healthy proxies** with automatic rotation and retry-on-next-proxy. It is a reliability tool for anyone who runs a proxy pool and wants to know which endpoints are alive, how fast they are, and have their requests spread across the good ones instead of hammering a dead node.

Bring your own proxies (`proxyUrls`) or use Apify Proxy (billed to your account). **Proxy passwords are masked in every output row and log line — raw credentials are never emitted.**

### Why agents use this actor

- **Deterministic typed output.** Every row matches the published dataset schema; a `type` field (`proxy-health` / `target-fetch`) tells the two row kinds apart. Every field is nullable, so partial results never break your pipeline.
- **Per-event pricing.** You pay per proxy checked and per target fetched — predictable before the run starts.
- **Your proxy, your bill.** All proxy traffic runs on your own `proxyUrls` or your Apify Proxy configuration. This actor never carries proxy cost.
- **Credential-safe.** Passwords are stripped to `***` everywhere. Nothing this actor writes contains a usable secret.
- **Clear error semantics.** No proxies, or all proxies dead, fails fast with a descriptive message and exit code 1 — never a silent empty dataset. A single dead proxy is recorded as `healthy:false` and the run continues.

### What it does

1. **Phase 1 — health check.** Each proxy in the pool requests `testUrl`. For every proxy you get a `proxy-health` row: masked label, HTTP status, latency, exit IP (when the test URL echoes it), and `healthy`. Healthy proxies are ranked fastest-first.
2. **Phase 2 — rotating fetch (optional).** If you pass `targetUrls`, each URL is fetched through the healthy pool using your `rotationStrategy`. If an attempt fails, it retries on the next proxy up to `maxRetriesPerTarget`. Each target yields a `target-fetch` row: final status, masked proxy used, attempt count, latency, and `ok`.

### Rotation strategies

| Strategy | Behavior |
| --- | --- |
| `round-robin` | Cycle through healthy proxies in latency order (default) |
| `least-recently-used` | Always pick the proxy idle the longest — evens out load |
| `sticky-per-host` | Pin each target host to one proxy; move on only when it fails |

### Input schema

| Field | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| `testUrl` | string | no | `https://api.ipify.org?format=json` | URL each proxy is probed against. Use an IP-echo endpoint to capture exit IPs. |
| `proxyConfiguration` | object | no | — | Apify Proxy config, billed to your account. Each rotation is a distinct session. |
| `proxyUrls` | string\[] | no | `[]` | Your own proxy endpoints (`http://user:pass@host:port`). |
| `poolSize` | integer | no | `5` | Distinct Apify Proxy sessions to form the pool. Ignored when `proxyUrls` is set. |
| `targetUrls` | string\[] | no | `[]` | URLs to fetch through the healthy pool. Empty = health-check only. |
| `rotationStrategy` | string enum | no | `round-robin` | `round-robin`, `least-recently-used`, `sticky-per-host`. |
| `maxRetriesPerTarget` | integer | no | `2` | Extra proxies to try after the first attempt fails. |
| `requestTimeoutMs` | integer | no | `30000` | Per-request timeout in milliseconds. |

### Output schema

Two row kinds in the default dataset, distinguished by `type`. All fields nullable.

**`proxy-health`** (phase 1, one per proxy)

| Field | Type | Description |
| --- | --- | --- |
| `proxyLabel` | string | null | Masked proxy identifier (password → `***`) |
| `status` | integer | null | HTTP status for the test URL |
| `latencyMs` | integer | null | Round-trip time in ms |
| `exitIp` | string | null | Exit IP echoed by the test URL |
| `healthy` | boolean | null | True on a 2xx/3xx response |

**`target-fetch`** (phase 2, one per URL)

| Field | Type | Description |
| --- | --- | --- |
| `targetUrl` | string | null | The fetched URL |
| `finalStatus` | integer | null | Status of the final attempt |
| `proxyUsed` | string | null | Masked proxy used on the final attempt |
| `attempts` | integer | null | Proxies tried (1 = first-try success) |
| `latencyMs` | integer | null | Round-trip time in ms |
| `ok` | boolean | null | True on a 2xx/3xx response |

A `SUMMARY` key-value record holds pool size, healthy count, the fastest proxy, and the targets requested.

### Use from AI agents (MCP)

```json
{
  "mcpServers": {
    "apify": {
      "url": "https://mcp.apify.com/?tools=apricot_blackberry/proxy-rotation-manager",
      "headers": { "Authorization": "Bearer <YOUR_APIFY_TOKEN>" }
    }
  }
}
```

Works in Claude, Cursor, ChatGPT connectors, and any MCP client; the input schema above is the tool's parameter schema.

### Use from code

**curl**

```bash
curl -X POST "https://api.apify.com/v2/acts/apricot_blackberry~proxy-rotation-manager/run-sync-get-dataset-items?token=$APIFY_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"proxyConfiguration":{"useApifyProxy":true,"apifyProxyGroups":["DATACENTER"]},"poolSize":3,"targetUrls":["https://httpbin.org/ip"]}'
```

**JavaScript**

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

const client = new ApifyClient({ token: process.env.APIFY_TOKEN });
const run = await client.actor('apricot_blackberry/proxy-rotation-manager').call({
    proxyUrls: ['http://user:pass@proxy1.example:8000', 'http://user:pass@proxy2.example:8000'],
    targetUrls: ['https://example.com/a', 'https://example.com/b'],
    rotationStrategy: 'least-recently-used',
});
const { items } = await client.dataset(run.defaultDatasetId).listItems();
console.log(items.filter((i) => i.type === 'proxy-health' && i.healthy));
```

**Python**

```python
from apify_client import ApifyClient

client = ApifyClient("<YOUR_APIFY_TOKEN>")
run = client.actor("apricot_blackberry/proxy-rotation-manager").call(run_input={
    "proxyConfiguration": {"useApifyProxy": True, "apifyProxyGroups": ["RESIDENTIAL"]},
    "poolSize": 5,
    "targetUrls": ["https://httpbin.org/ip"],
    "rotationStrategy": "round-robin",
})
for item in client.dataset(run["defaultDatasetId"]).iterate_items():
    print(item["type"], item.get("proxyLabel") or item.get("targetUrl"), item.get("latencyMs"))
```

### Use from automation platforms

- **n8n / Make / Zapier** — native Apify integration, "Run Actor", pick `proxy-rotation-manager`. Schedule the health check and alert when `healthy` drops.
- **LangChain / LlamaIndex** — wrap with the Apify Actor tool wrappers; the input schema becomes the tool signature.
- **Apify Schedules + webhooks** — run the pool health check on a cron and fire `ACTOR.RUN.SUCCEEDED` into your monitoring.

### Pricing

Pay per event. You are charged only for work a run actually completes.

| Event | When it fires |
| --- | --- |
| `actor-start` | Once per run |
| `proxy-checked` | Once per proxy health-checked |
| `request` | Once per target URL fetched successfully |

Proxy bandwidth is billed separately to you by your proxy provider or by Apify Proxy on your account — this actor never carries it.

### FAQ

**Do you ever see or store my proxy passwords?**
The actor uses them to make requests, but every value it writes — dataset rows, logs, the SUMMARY record — has the password masked to `***`. No output contains a usable credential.

**Who pays for the proxy traffic?**
You do. Either your own `proxyUrls` (your provider bills you) or your Apify Proxy configuration (billed to your Apify account). The actor's own charge events cover only orchestration, never bandwidth.

**How do I capture exit IPs?**
Use an IP-echo `testUrl`. The default (ipify) returns `{"ip":"..."}`; `https://httpbin.org/ip` returns `{"origin":"..."}`. Both are parsed automatically.

**Can I run just the health check?**
Yes — leave `targetUrls` empty. You get one `proxy-health` row per proxy and nothing else.

### Changelog

**1.0** — Initial release. Two-phase pool health check + rotating batch fetch, three rotation strategies, retry-on-next-proxy, Apify Proxy and bring-your-own support, credential masking everywhere, per-event pricing.

# Actor input Schema

## `testUrl` (type: `string`):

URL each proxy is probed against in the health-check phase. Use an IP-echo endpoint so the response reveals the proxy's exit IP. Defaults to ipify.

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

Apify Proxy configuration. Each rotation is a distinct session, so the pool gets distinct exit IPs. All proxy traffic is billed to YOUR Apify account. Leave empty if you supply your own proxyUrls.

## `proxyUrls` (type: `array`):

Your own proxy endpoints, e.g. http://user:pass@host:port. Used as the pool alongside (or instead of) Apify Proxy. Credentials are never emitted — passwords are masked in every output row and log line.

## `poolSize` (type: `integer`):

How many distinct Apify Proxy sessions to spin up as the pool when using proxyConfiguration. Ignored when you supply your own proxyUrls. Each session is one health-checked proxy.

## `targetUrls` (type: `array`):

Optional. URLs to fetch through the healthy proxy pool in phase 2. Leave empty to run health-check only. Each URL is fetched once, retrying on the next proxy if it fails.

## `rotationStrategy` (type: `string`):

How targets are distributed across the healthy pool. round-robin cycles through proxies in latency order; least-recently-used picks the proxy idle longest; sticky-per-host pins each target host to one proxy (moving on only when it fails).

## `maxRetriesPerTarget` (type: `integer`):

How many additional proxies to try for a target after the first attempt fails. 2 means up to 3 attempts total, each on a different proxy.

## `requestTimeoutMs` (type: `integer`):

Timeout for each individual health-check and target request, in milliseconds.

## Actor input object example

```json
{
  "testUrl": "https://api.ipify.org?format=json",
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "DATACENTER"
    ]
  },
  "proxyUrls": [],
  "poolSize": 5,
  "targetUrls": [
    "https://httpbin.org/ip"
  ],
  "rotationStrategy": "round-robin",
  "maxRetriesPerTarget": 2,
  "requestTimeoutMs": 30000
}
```

# Actor output Schema

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

Proxy-health rows and per-target fetch rows in the default dataset. The `type` field is `proxy-health` or `target-fetch`.

# 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 = {
    "testUrl": "https://api.ipify.org?format=json",
    "proxyConfiguration": {
        "useApifyProxy": true,
        "apifyProxyGroups": [
            "DATACENTER"
        ]
    },
    "proxyUrls": [],
    "poolSize": 5,
    "targetUrls": [
        "https://httpbin.org/ip"
    ],
    "rotationStrategy": "round-robin",
    "maxRetriesPerTarget": 2,
    "requestTimeoutMs": 30000
};

// Run the Actor and wait for it to finish
const run = await client.actor("apricot_blackberry/proxy-rotation-manager").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 = {
    "testUrl": "https://api.ipify.org?format=json",
    "proxyConfiguration": {
        "useApifyProxy": True,
        "apifyProxyGroups": ["DATACENTER"],
    },
    "proxyUrls": [],
    "poolSize": 5,
    "targetUrls": ["https://httpbin.org/ip"],
    "rotationStrategy": "round-robin",
    "maxRetriesPerTarget": 2,
    "requestTimeoutMs": 30000,
}

# Run the Actor and wait for it to finish
run = client.actor("apricot_blackberry/proxy-rotation-manager").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 '{
  "testUrl": "https://api.ipify.org?format=json",
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "DATACENTER"
    ]
  },
  "proxyUrls": [],
  "poolSize": 5,
  "targetUrls": [
    "https://httpbin.org/ip"
  ],
  "rotationStrategy": "round-robin",
  "maxRetriesPerTarget": 2,
  "requestTimeoutMs": 30000
}' |
apify call apricot_blackberry/proxy-rotation-manager --silent --output-dataset

```

## MCP server setup

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

```

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/BCNOWTdiRqWFLLbfv/builds/we6o4otUjRAxfRsga/openapi.json
