Skip to content

PerimeterX Block-Replay

Continue from the exact block your request received. This flow consumes the exact block your current client received. Collect the response body, cookies, User-Agent, and proxy together before starting a task.

POST /createTask · example
{
  "clientKey": "capzy_YOUR_API_KEY",
  "task": {
    "type": "AntiPerimeterXBlockTask",
    "websiteURL": "https://example.com",
    "userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36",
    "blockData": "<the raw 403 PerimeterX block body — HTML page or XHR/JSON payload>",
    "cookies": "_pxvid=<uuid>; _pxhd=<value>",
    "proxyType": "http",
    "proxyAddress": "123.45.67.89",
    "proxyPort": "8080",
    "proxyLogin": "user",
    "proxyPassword": "pass"
  }
}

Illustrative values. Replace the inputs with values from your current integration.

Your 403 block + cookies + proxyYour session proxy requiredClearance for the supplied PX session

Save your own block response.

This flow consumes the exact block your current client received. Collect the response body, cookies, User-Agent, and proxy together before starting a task.

Your API key authenticates Capzy.
Provider keys, challenge IDs, images, and session values belong inside task.

Copy the complete document or XHR block.

  1. Enable Preserve log, trigger the blocked action, and select its 403 request in Network.
  2. Copy the complete Response body into blockData. Set blockMode to html for a block page or xhr for an API/JSON response.
  3. Use the parent page URL as websiteURL. Retain the original request in your client so you can retry the same action after clearance.
Input collection example - replace illustrative values
{
  "websiteURL": "https://example.com/account",
  "blockMode": "html",
  "blockData": "<html>YOUR_RAW_PX_BLOCK_RESPONSE</html>"
}

A page URL alone does not replace blockData for this task. Capture a fresh block from the session you will continue.

How PerimeterX Block-Replay works

PerimeterX Block-Replay is a deterministic variant of the standard PerimeterX solver. Instead of navigating fresh and hoping a press-and-hold renders, YOU make your own request, hit a 403 PerimeterX block, and hand us THAT block. We adopt the customer's exact PerimeterX session (the vid/uuid carried in the block body), solve the press-and-hold (HUMAN Challenge), and return clearance bound to your own `_pxvid` so it validates the moment you replay. This is the right path when your client already gets blocked on a real request and you want to 'consume the block' rather than mint a cold session. Currently solved in a real browser. The cookie hierarchy is the same as standard PerimeterX: _px3 (newer/strict, ~60s expiry) > _px2 (older sites) > _pxhd (hardened device).

You catch your own 403 and send us the raw block body (`blockData`) plus the cookies, proxy, and User-Agent that the blocked request used. We re-create your PerimeterX session inside a real browser, solve the press-and-hold against it, and return the _px3/_px2/_pxhd clearance cookies pinned to your visitor identity. Because PerimeterX clearance is bound to the solving IP AND the User-Agent, the proxy and UA you used for the blocked request are REQUIRED — there is no ProxyLess variant. Replay the returned cookies + User-Agent through the SAME proxy and your request clears.

  • Deterministic 'consume-the-block' flow — you supply the real 403, we adopt that exact session
  • Clearance bound to YOUR _pxvid so it validates on replay (no cold-session mismatch)
  • Your own proxy required — clearance is IP-bound to the proxy the 403 was hit with
  • Accepts the block as HTML OR XHR/JSON (blockMode auto-detects)
  • Cookies accepted as an array, a {name: value} map, or a raw Cookie header string
  • Returns _px3 (priority) → _px2 → _pxhd plus ready-to-paste cookie header + structured array
  • Press telemetry (challengePresented, holdDurationSec, holdStrategy, challengeTimeMs) for diagnostics
  • ~10s average solve

Know what goes in.

Choose a field to see its purpose, where to get it, and its place in the request. Optional values omitted from the example can be added when your flow uses them.

Keep your proxy exit and session identity consistent when using this result.

Request authentication

Required task inputs

Optional context

Proxy connection

websiteURL

stringRequired

The protected parent page that returned the 403 (the page your own request was trying to reach). NOT the PerimeterX challenge iframe URL.

Where it comes from

Copy the full page URL from the address bar or the document request in Network, including the path where this flow runs.

Open the input guide
Example valuehttps://example.com
POST /createTask · selected configuration
{
  "clientKey": "capzy_YOUR_API_KEY",
  "task": {
    "type": "AntiPerimeterXBlockTask",
    "websiteURL": "https://example.com",
    "userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36",
    "blockData": "<the raw 403 PerimeterX block body — HTML page or XHR/JSON payload>",
    "cookies": "_pxvid=<uuid>; _pxhd=<value>",
    "proxyType": "http",
    "proxyAddress": "123.45.67.89",
    "proxyPort": "8080",
    "proxyLogin": "user",
    "proxyPassword": "pass"
  }
}

The selected field is highlighted when present. Preserve the declared value type and current challenge context.

Make your first request.

Create the task, keep its taskId, and poll until status is ready. Then read the selected variant's solution.

More examples
  1. Create the task

    Send clientKey and the selected task to /createTask from your server. Keep the returned taskId.

  2. Wait for the solution

    Send that same key and ID to /getTaskResult. While processing, wait two seconds before checking again. Stop on Ready or an error.

  3. Apply the result

    Apply solution.cookies with their scopes, use solution.userAgent, and continue the original request through the same proxy. The response's consumedBlock and visitor metadata describe the processed block; they do not replace the cookie jar.

solve_perimeterx_block.py
import json, os, time
import requests

API = "https://api.capzy.ai"
KEY = os.environ["CAPZY_API_KEY"]
task = json.loads("{\"type\":\"AntiPerimeterXBlockTask\",\"websiteURL\":\"https://example.com\",\"userAgent\":\"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36\",\"blockData\":\"<the raw 403 PerimeterX block body — HTML page or XHR/JSON payload>\",\"cookies\":\"_pxvid=<uuid>; _pxhd=<value>\",\"proxyType\":\"http\",\"proxyAddress\":\"123.45.67.89\",\"proxyPort\":\"8080\",\"proxyLogin\":\"user\",\"proxyPassword\":\"pass\"}")

def call(path, payload):
    response = requests.post(f"{API}/{path}", json={"clientKey": KEY, **payload}, timeout=30)
    response.raise_for_status()
    data = response.json()
    if data.get("errorId", 0) or data.get("status") == "failed":
        raise RuntimeError(data.get("errorDescription") or data.get("errorCode") or "Task failed")
    return data

created = call("createTask", {"task": task})
for attempt in range(60):
    result = call("getTaskResult", {"taskId": created["taskId"]})
    if result.get("status") == "ready":
        solution = result["solution"]
        print(solution)
        break
    time.sleep(2)
else:
    raise TimeoutError("Task is still processing; keep the taskId to check it later.")

Before runningReplace all illustrative inputs with your captured values. Set CAPZY_API_KEY in your environment. Install requests with pip install requests.

Polling and failuresThe example checks HTTP/API failures, uses 30-second request timeouts, and stops after 60 result checks. Retain taskId if your task is still processing.

Understand what comes back.

Follow this task from creation to completion. Response fields and usage follow your selected request variant.

Clearance for the supplied PX session

Apply solution.cookies with their scopes, use solution.userAgent, and continue the original request through the same proxy. The response's consumedBlock and visitor metadata describe the processed block; they do not replace the cookie jar.

string
The _px3 (or _px2) clearance cookie value alone — use when you only need the proof string.
string
Ready-to-paste Cookie: header string of all _px*/_pxhd cookies (e.g. `_px3=...; _pxhd=...; _pxvid=...`).
array
All clearance cookies as {name, value, domain, path} for customers building their own cookie jar.
string
The User-Agent used during the solve — replay with this exact value (clearance is UA-bound).
string
Visitor uuid (the _pxvid value). Pin this on your replay session.
string
PerimeterX vid. Pin this on your replay session alongside uuid.
boolean
true if the press-and-hold (HUMAN Challenge) actually rendered and we held it; false if the adopted session passed silently. Both produce valid cookies.
number
Seconds we held the press-and-hold button. 0 when the challenge was not presented.
string
Which press-and-hold approach minted the cookies (press telemetry).
number
End-to-end time the challenge took, in milliseconds (press telemetry).
array
PerimeterX collector POST results observed during the solve.
boolean
Always true — clearance is tied to the solving IP, so replays must come through the same proxy.
boolean
Always true — confirms we adopted the block you supplied rather than minting a fresh session.
string
Echo of the PerimeterX tenant id (appId) parsed from your block.
string
Echo of the host/URL context parsed from your block.
POST /getTaskResult · example response
{
  "errorId": 0,
  "status": "ready",
  "solution": {
    "token": "<_px3 cookie value>",
    "cookie": "_pxvid=<uuid>; _px3=<value>; _pxhd=<value>",
    "cookies": [
      {
        "name": "_pxvid",
        "value": "<uuid>",
        "domain": ".target.example.com",
        "path": "/"
      },
      {
        "name": "_px3",
        "value": "<value>",
        "domain": ".target.example.com",
        "path": "/"
      },
      {
        "name": "_pxhd",
        "value": "<value>",
        "domain": ".target.example.com",
        "path": "/"
      }
    ],
    "userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36",
    "uuid": "<the _pxvid value PX bound _px3 to>",
    "vid": "<usually equal to uuid>",
    "challengePresented": true,
    "holdDurationSec": 9.4,
    "holdStrategy": "press-and-hold",
    "challengeTimeMs": 9400,
    "collectorEvents": [
      {
        "url": "collector-PXxxxx.perimeterx.net",
        "status": 200
      }
    ],
    "ipBound": true,
    "consumedBlock": true,
    "blockAppId": "PXzC5j78di",
    "blockHostUrl": "https://example.com"
  }
}

Illustrative values. Additional metadata may be returned; only Ready contains a completed solution.

Use the result

Apply solution.cookies with their scopes, use solution.userAgent, and continue the original request through the same proxy. The response's consumedBlock and visitor metadata describe the processed block; they do not replace the cookie jar.

Execution context result is the parsed Ready response from the polling example. session, when referenced, is your existing requests.Session (or browser-compatible session) with the original cookie jar, headers, and solving proxy configured in session.proxies for both http and https. Keep this client for the complete flow. task is the task object you submitted. For Shape, action_body is the original protected request body as bytes or a string, with its original Content-Type in session.headers. Install curl_cffi when the fragment imports it; match the target's required browser/TLS identity.

Use the result · Python

from http.cookies import SimpleCookie
sol = result["solution"]
cookie_values = SimpleCookie()
cookie_values.load(sol["cookie"])
for name, cookie in cookie_values.items():
    session.cookies.set(name, cookie.value, domain="target.example.com", path="/")
session.headers["User-Agent"] = sol["userAgent"]
# session retains the original cookie jar and the solve's proxy.
resp = session.get("https://target.example.com/", timeout=30)
print(resp.status_code)

When something doesn't work.

Check your captured values, the selected variant, and the response before starting another task.

How is this different from the standard PerimeterX solver?

The standard PerimeterX solver navigates the protected page fresh and hopes the press-and-hold renders, minting a new session. Block-Replay is deterministic: you make your OWN request, hit a 403 PerimeterX block, and hand us THAT block (blockData + the cookies/proxy/UA it used). We adopt your exact session and return clearance bound to your own _pxvid, so it validates the moment you replay — no cold-session mismatch.

What's the full flow?

1) Make your request to the protected page through your proxy. 2) Catch the 403 PerimeterX block. 3) Capture the raw 403 body (blockData), the cookies from that response, the proxy you used, and the User-Agent. 4) Submit AntiPerimeterXBlockTask with all of those. 5) Replay the returned cookies + userAgent on the SAME proxy and your request clears.

Why isn't there a ProxyLess variant?

PerimeterX clearance (_px3 / _px2 / _pxhd) is cryptographically tied to the IP that solved it AND to the User-Agent. The whole point of Block-Replay is that we adopt the session your blocked request created — that request went through your proxy with your UA, so the solve and your replay must use the same proxy + UA. A token minted on our pool IP would be rejected from yours. AntiPerimeterXBlockTaskProxyLess is rejected with ERROR_PROXY_REQUIRED.

What exactly do I put in blockData?

The raw body of the 403 PerimeterX response, exactly as your client received it — either the HTML block page or the XHR/JSON block payload. It carries the session vid/uuid we solve for. Don't send a screenshot or a URL; send the response body. If you have both an HTML and a JSON form, send whichever your request actually got, and set blockMode if you want to force parsing ('auto' detects HTML vs JSON for you).

How do I pass cookies?

Whatever's easiest for your client — we accept three shapes: an array of {name, value, domain?, path?}, a {name: value} map, or a raw "a=b; c=d" Cookie header string. Send the cookies that came back with the 403 (the _px* / _pxvid set), since visitor continuity from your own session is what makes the replay validate.

What does the solution look like and how do I use it?

You get back `cookie` (a ready-to-paste Cookie: header with every _px* cookie), `userAgent` (pin this), plus `uuid`/`vid` to pin your visitor identity. For most integrations you just drop `cookie` into the Cookie: header and `userAgent` into the User-Agent: header, then replay through the SAME proxy you supplied. `challengePresented`, `holdDurationSec`, `holdStrategy`, `challengeTimeMs`, and `collectorEvents` are diagnostic. `ipBound` and `consumedBlock` are always true.

Do I need pxAppId / uuid / vid?

No — we parse them from blockData. They're optional overrides: pass them only when you already know the values and want to force them (for example pxAppId/appId for the tenant, uuid/pxVid/_pxvid for the visitor, vid for the PX vid, _pxhd/pxhd for the hardened-device cookie). The override wins over what we parse.

Integration checks for PerimeterX Block-Replay
  • Proxy is REQUIRED and must be the SAME one your blocked request used. Clearance is IP-bound to the proxy the 403 was hit with — AntiPerimeterXBlockTaskProxyLess is rejected with ERROR_PROXY_REQUIRED because a cookie minted on our pool IP is useless from yours.
  • Send the userAgent that produced the block. Clearance is UA-bound — the User-Agent on your blocked request, the solve, and every replay must match. We return the exact UA used; pin it.
  • blockData must be the raw 403 body, not a screenshot or a URL. Send either the HTML block page or the XHR/JSON block payload exactly as your client received it — it carries the session vid/uuid we solve for.
  • Capture the cookies from the blocked response (the _px* / _pxvid set PerimeterX returned with the 403). Visitor continuity from your own session is what makes the replay validate.

Connect PerimeterX Block-Replay to your workflow.

Get your Capzy API key and use this guide's integration.

UnavailablePer 1,000 solves
UnavailableAverage solve time

AntiPerimeterXBlockTask · Live configuration
Current stats are unavailable. Check the solver catalog.