API — Port Pool

Open and manage port pools — batches of ports opened from a proxy list for large, concurrent jobs — then export the results.

Open a pool

NoteThe Port Pool endpoints live under the /scraper path for backward compatibility — it is the same feature as the + Port Pool button in the dashboard.
POST/api/v1/scraper/tasksAuth required

Opens a port pool from a proxy list.

ParameterTypeRequiredDescription
namestringYesPool name.
sourceobjectYesWhere the proxy list comes from: kind is "file", "url" or "inline"; location is the path or URL; content carries the list itself for "inline". Optional refresh_interval re-reads the source and is an integer number of NANOSECONDS: 10 minutes is 600000000000, one minute is 60000000000, 30 seconds is 30000000000. The default is 10m; anything below 30s is silently raised to 30s and the response says nothing about it — a bare 600 means 600 nanoseconds, so the source (an external URL included) would be re-read every 30 seconds. default_scheme applies to bare host:port entries (default socks5).
targetintYesHow many ports the pool should open.
port_lointNoLow end of the local port range the pool may use.
port_hiintNoHigh end of the local port range.
protocolstringNoListen protocol of the pool ports: "socks5" (default) or "http".
up_policyobjectNoHow often a port switches upstream. mode is "requests" (every n connections), "minutes" (every n minutes) or "on_error" (hold the proxy until the client is served an error). Default: every request. 🔴 With js_solver: true the cadence DOES NOT APPLY: both up_policy and fp_policy are forced to no-cadence. The port takes an upstream on its first connection and holds it, together with the identity, for the port's whole life; the upstream changes only out of band, after a connect error (the identity does not change with it). The one exception is mode: on_error, which survives the forcing and behaves as described.
fp_policyobjectNoHow often a port switches identity, in the same shape and with the same three modes. Default: every 10 requests. 🔴 The same forcing applies with js_solver: true, see up_policy above: the port's identity does not rotate on cadence at all — except in on_error mode.
devicestringNoBrowser family for the pool identities, for example "chrome". See GET /api/v1/scraper/device_matrix.
device_osstringNoOperating system for the pool identities, for example "windows".
profile_sourcestringNoWhere identities come from: "auto" generates them on the fly, "db" takes them from the curated database.
auto_uaboolNoPick each identity from the request User-Agent instead of the pool setting.
spoof_headersboolNoRewrite outgoing headers to match the current browser. Recommended for scrapers; on by default.
connect_timeout_secondsintNoCap on a single connect attempt for every port in the pool, in seconds. Default 5.
request_timeout_secondsintNoSilence budget for every port in the pool, in seconds: the connect phase including retries, then the wait for the first byte. Default 30; 0 removes the limit.
idle_timeout_secintNoIdle timeout on a connection of a pool port, in seconds: the connection is closed after this long with no bytes. Default 60. Distinct from idle_seconds, which closes the port itself. The former name request_timeout_sec is still accepted when a pool saved by an earlier version is read.
idle_secondsintNoPort lifetime, in seconds. A pool keeps its ports open by default (0).
Example (curl)
curl -H "X-API-Key: YOUR_API_KEY" -H "Content-Type: application/json" \
  -d '{"name":"tiktok-pool","target":100,"port_lo":20000,"port_hi":30000,
        "protocol":"socks5",
        "source":{"kind":"file","location":"C:/proxies.txt"},
        "up_policy":{"mode":"requests","n":1},
        "fp_policy":{"mode":"requests","n":10},
        "connect_timeout_seconds":5,"request_timeout_seconds":30}' \
  http://127.0.0.1:8891/api/v1/scraper/tasks
Response
{
  "id": "pool-7f3a",
  "export_token": "3b91c0d4e5f6",
  "warnings": null
}

🔴 The id and the export_token come FROM HERE: the id goes into /scraper/tasks/{id}/…, and the export_token into the export address /export/{token}/proxies.txt. There is nowhere else they are visible — apart from GET /api/v1/scraper/tasks. warnings carries EXACTLY ONE remark — “opened N of M ports (range LO-HI too small or ports busy)” — when fewer ports were opened than the requested target; when they all opened, the field comes back as null. It reports NOTHING about the proxy source: neither how many entries failed to parse, nor that the source could not be read at all (a 404 or timeout on the URL, a missing file). In that case the pool still comes up — with an empty upstream list — and answers 200, so the only symptom is failing traffic. Check the source UP FRONT with the separate POST /api/v1/scraper/probe: it returns total (usable entries) and bad (unparseable ones).

403 “the scraper port pool requires a Pro license (single-thread Lite cannot open a port pool)” on the Lite plan; 503 “scraper manager not configured” if the pool is not wired in this build; 400 with an explanation when the configuration does not add up: port_lo above port_hi, target below 1, an unknown profile_source, up_policy.mode, fp_policy.mode or vdns_mode, or an unknown/incompatible device and device_os pair. 🔴 A range too small for target is NOT a refusal: the pool opens as many ports as fit — including NONE, when they are all busy — answers 200 and puts a line like “opened 11 of 100 ports (range 20000-20010 too small or ports busy)” into warnings. So check warnings, not just the status code.

Two defaults are applied at CREATION rather than stored in the configuration: protocol, when unset, becomes socks5 (so UDP and HTTP/3 work), and vdns_mode becomes forced — the target name is resolved at the egress and does not leak into your own network's DNS. An explicit "off" stays off.

NoteThe on_error mode keeps the upstream proxy and the identity for as long as they work, and rotates them once the client is served a 4xx or 5xx response. 2xx and 3xx count as success, so redirects and cached responses do not trigger a rotation. What counts is the status YOUR CLIENT receives: if a request met a challenge and the challenge was handled so that the client got 200, no error occurred. A burst of failures from one dying proxy costs a single rotation, not one per request. The mode is available to port pools only, and needs a port that inspects traffic — on a port opened with tls_passthrough there is no HTTP status to observe.

Advanced settings for the pool's ports

The pool opens its ports itself, so a setting that did not reach it stays at its default — while the form looks applied. The fields below are placed on every port of the pool.

FieldTypeWhat it does
js_solverboolChallenge Breaker on every pool port: requests that hit a challenge go to the solver pool. It requires opening TLS — incompatible with tls_passthrough. 🔴 It makes the port sticky: the pool's up_policy and fp_policy stop applying on cadence — one exit IP and one identity per port for the port's whole life (exceptions: on_error mode, and the out-of-band upstream change after a connect error). This is by design: a session won by solving a challenge is bound to one exit + fingerprint pair, and changing either mid-session throws it away. Exit diversity comes from the NUMBER of pool ports, not from the rotation cadence — raise target and widen the port range. The substitution is not visible over the API: GET /api/v1/scraper/tasks/{id}/config returns your original up_policy and fp_policy, because the forcing is applied to the running port, not to the stored configuration.
captcha_actionstringWhat to do when a CAPTCHA turns up rather than an unattended challenge: rotate_retry (the default) changes the identity and retries; return_to_script hands the challenge to your code.
captcha_max_attemptsintHow many times to retry with a new identity under rotate_retry. 3 by default.
keep_sessionsboolEvery pool port keeps its own per-domain cookie jar: it absorbs Set-Cookie, injects cookies and follows redirects. Independent of js_solver.
cache_modestringThe cache mode on the pool's ports: normal, hard, hard-media or hard-autowarm.
cache_ignore_no_cacheboolCache in spite of a no-cache header.
vdns_modestringVirtual DNS: off, on_leak or forced. 🔴 On a NEW pool an unset field becomes forced — the target name is resolved at the egress and does not leak into your network's DNS.
leak_guardstringThe pre-start leak check for the pool's ports: off, warn or enforce.
force_ipv4boolEgress over IPv4 only.
tls_passthroughboolPass TLS straight through. Then neither the cache, nor the log, nor Challenge Breaker works on the pool's ports: the content is not read.
tls_mirrorboolReplay the client's own captured fingerprint outward instead of the profile's.
h2_spoofingboolSpoof the HTTP/2 settings to match the browser profile.
spoof_user_agentboolSpoof the User-Agent. Off relays your client's own header byte for byte.
enable_http3boolRe-originate over HTTP/3 where the site offers h3.
upstream_tls_insecureboolDrop certificate verification for an https proxy. Only for your own proxy with a self-signed certificate.
allow_mitm_upstreamboolAllow an egress that opens TLS itself. Forbidden by default: such an egress erases the fingerprint.
max_concurrentintThe cap on concurrent requests on EACH pool port; 0 means no cap.
sem_timeoutintHow many seconds a request waits for a slot within that cap.
skip_retryboolDo not retry a request after a network error.
retry_delay_msintThe pause between retries, in milliseconds.
Notejs_solver and keep_sessions are exactly what a pool is most often built for: getting past challenges and a session of its own on every port. The first requires opening TLS, so it does not work together with tls_passthrough.

Listing and stopping pools

GET/api/v1/scraper/tasksAuth required

A snapshot of every pool: how many ports are open, which ones exactly, and the range they were allocated from.

Example (curl)
curl -H "X-API-Key: YOUR_API_KEY" \
  http://127.0.0.1:8891/api/v1/scraper/tasks
Response
[
  {
    "id": "pool-7f3a",
    "name": "catalogue-pool",
    "target": 100,
    "ports_up": 98,
    "ports": [20000, 20001, 20002],
    "export_token": "3b91c0d4e5f6",
    "port_lo": 20000,
    "port_hi": 30000
  }
]
  • An empty array means there is no pool right now — that is not an error. 503 “scraper manager not configured” if the pool is not wired in this build.
  • The response carries no upstream count and no rotation state: how many entries the source holds is answered by POST /api/v1/scraper/probe (field total), and which rotation is configured by GET /api/v1/scraper/tasks/{id}/config (up_policy and fp_policy). The field ports_up is the count of live ports, and ports lists their numbers.
DELETE/api/v1/scraper/tasks/{id}Auth required

Stops the pool and closes all of its ports. The traffic going through them is cut.

Example (curl)
curl -X DELETE -H "X-API-Key: YOUR_API_KEY" \
  http://127.0.0.1:8891/api/v1/scraper/tasks/pool-7f3a
Response
{
  "ok": true
}
  • 404 with the manager's text if there is no pool with that id.
POST/api/v1/scraper/tasks/{id}/testAuth required

Runs the full port test — the same one as POST /api/v1/port/{port}/test — on one of the pool's live ports, and reports which port was tested.

Example (curl)
curl -X POST -H "X-API-Key: YOUR_API_KEY" \
  http://127.0.0.1:8891/api/v1/scraper/tasks/pool-7f3a/test
Response
{
  "port": 20134,
  "report": { "leak": { "…": "…" }, "fingerprint": { "match": true },
                "udp": { "supported": true }, "ok": true }
}
  • 409 “scraper task has no open ports yet” while the pool has raised none, and 409 “scraper task ports are not live” when the ports are no longer working. 404 if there is no such pool.
GET/api/v1/scraper/tasks/{id}/configAuth required

Returns the pool's stored configuration in exactly the shape POST /api/v1/scraper/tasks accepts: an edit is a stop followed by a fresh start.

Example (curl)
curl -H "X-API-Key: YOUR_API_KEY" \
  http://127.0.0.1:8891/api/v1/scraper/tasks/pool-7f3a/config
Response
{
  "id": "pool-7f3a",
  "name": "catalogue-pool",
  "port_lo": 20000,
  "port_hi": 30000,
  "target": 100,
  "protocol": "socks5",
  "source": { "kind": "file", "location": "C:/proxies.txt" },
  "up_policy": { "mode": "requests", "n": 1 },
  "fp_policy": { "mode": "requests", "n": 10 },
  "vdns_mode": "forced",
  "export_token": "3b91c0d4e5f6"
}
  • 404 “task not found”. The configuration also carries the advanced fields absent from the open table: cache_mode, leak_guard, js_solver, keep_sessions, captcha_action and the port's three timeouts.
GET/api/v1/scraper/device_matrixAuth required

Lists the browser and operating-system combinations a pool can be built from.

Example (curl)
curl -H "X-API-Key: YOUR_API_KEY" \
  http://127.0.0.1:8891/api/v1/scraper/device_matrix
Response
{
  "chrome": ["windows", "macos", "linux", "android", "ios"],
  "edge": ["windows", "macos", "linux", "android", "ios"],
  "firefox": ["windows", "macos", "linux", "android", "ios"],
  "safari": ["macos", "ios"]
}
  • The values here go into the device and device_os fields when opening a pool.
  • The only impossible combination is Safari outside Apple hardware. The table describes what the validator accepts on an EXPLICIT choice; device: random draws from a narrower set — no iOS for Chrome, Firefox and Edge, and no Android for Firefox and Edge — those pairs are reached only by naming them.
POST/api/v1/scraper/probeAuth required

Downloads and parses the proxy source WITHOUT starting a pool: how many entries are usable, how many are broken, which schemes occur, and a random sample to test connectivity against yourself.

ParameterTypeRequiredDescription
sourceobjectYesThe same source object as when opening a pool: kind (file, url or inline), location and content.
Request body
{ "source": { "kind": "url", "location": "https://example.com/proxies.txt" } }
Example (curl)
curl -X POST -H "X-API-Key: YOUR_API_KEY" -H "Content-Type: application/json" \
  -d '{"source":{"kind":"url","location":"https://example.com/proxies.txt"}}' \
  http://127.0.0.1:8891/api/v1/scraper/probe
Response
{
  "total": 13045,
  "bad": 210,
  "by_scheme": { "socks5": 12835, "http": 210 },
  "sample": ["socks5://203.0.113.10:1080", "socks5://203.0.113.11:1080"]
}
  • 400 with an explanation if the source could not be downloaded or parsed. The body is capped at 32 MiB — the size of a list pasted whole; the probe is capped at 35 seconds.
  • 403 when the license is inactive: this endpoint sits behind the license, like the other egress checks.

Export results

GET/export/{token}/proxies.txtNo auth

Downloads a pool's ports as a plain-text proxy list. Access is granted by the token in the URL, so no API key is needed.

Example (curl)
curl http://127.0.0.1:8891/export/TASK_TOKEN/proxies.txt