parsa
Get started

PerimeterX _px3 solver API

Get the collector posts that bake HUMAN Security's _px2 and _px3 cookies, built from the sensor script alone. No browser, no headless farm, and nothing to pay when a post doesn't land.

How PerimeterX clears a page load, and where Parsa fits
Challenge
Collector posts (_px2, _px3)
Answer time
~1 ms; ~25 ms for a new build
Browsers
Chrome on Windows or macOS
Price
€1.50 / 1,000
Billed
Only answers that come back 200

Measured in November 2026 on our own solvers; how, under benchmarks.

What is PerimeterX (HUMAN)?

PerimeterX, now HUMAN Security, protects a site with a sensor script that each customer gets under their own app id. The script loads from client.px-cloud.net/<app id>/main.min.js, or from the site's own domain as init.js. It measures the browser and the visit, and posts what it finds to collector-<app id>.px-cloud.net. The collector answers with a stream of commands, and those commands set the clearance cookies, _px2 and _px3.

The cookies are the server's verdict. The client never computes them, so there is nothing to forge: the only way to get them is to send posts the collector believes. A session is one page load: each navigation starts a new one with a new id and a fresh sequence of posts, and _px3 is baked for about five and a half minutes. A request without a fresh _px3 is blocked or gets HUMAN's press-and-hold challenge.

Parsa reads the sensor build and writes each collector post as Chrome would: the fingerprint, the timings and the pointer and key activity of a real visit, encoded the way that build expects. Your client posts it from its own IP. The collector's reply goes back into the next call, so each post answers what the collector last asked, until the cookies arrive.

How PerimeterX (HUMAN) works, in depth →

The PerimeterX challenge

Collector posts (_px2, _px3)

POST /perimeterx/collector · €1.50 / 1,000

You have PerimeterX when a page loads main.min.js from a px-cloud.net or px-cdn.net host, or an init.js from a short path on its own domain, and the site sets _pxhd or _pxvid. Fetch the script once and send it to us with the page's URL, your user agent, IP and language.

We answer with the collector URL and the form body to post there. Post it from your client and keep the collector's reply: send it back as collectorResponse with the context from our answer, and the next post continues the same page load. The reply that carries the bake command sets _px2 and _px3.

The script has hundreds of fields to report, from navigator properties, WebGL and fonts to focus, visibility and pointer activity. Each event is serialised, masked and encoded with noise placed where the session's id says, and checked by an integrity digest. When the site ships a new build we haven't read, the API answers 409 unknown_build: send the script again and carry on.

You send
userAgent
Chrome on Windows or macOS
pageUrl
The page that loaded the sensor
ip
Your proxy's exit IP (required)
acceptLanguage, zone
Your language and time zone
script / build
init.js once, then its 16-character build id
collectorResponse
The collector's last reply
context
From our previous answer
newPage
true when the visit moves to a new page
You get back
url
collector-<app id>.px-cloud.net/api/v2/collector
body
payload=…&appId=…, form-encoded
build
Send it instead of the script next time
context
Sealed session state for the next call

The request

Your client sends init.js once with the page URL, user agent and IP, posts the body we return to the collector, and sends the collector's reply back as collectorResponse with the context. It sets newPage when the visit moves on, because each page load is a session of its own.

Keep these the same for the whole session

  • One exit IP, sticky, and the same one you send us as ip
  • A Chrome TLS and HTTP/2 fingerprint and Chrome's header order
  • The user agent and Accept-Language the payload describes
  • One cookie jar, from the first page load to the protected request
curl https://api.parsalabs.io/perimeterx/collector \
  -H "Authorization: Bearer parsa_…" \
  -H "Content-Type: application/json" \
  -d '{
    "userAgent": "Mozilla/5.0 …",
    "pageUrl":   "https://shop.example.com/",
    "ip":        "203.0.113.7",
    "script":    "<init.js>"
  }'

# 200 OK · charged €0.0015
{
  "url":     "https://collector-PXa1b2c3d4.px-cloud.net/api/v2/collector",
  "body":    "payload=…&appId=PXa1b2c3d4…",
  "build":   "9f3c1a7e0b2d4c86",
  "context": "…"
}
import requests
from curl_cffi import requests as browser

s = browser.Session(impersonate="chrome", proxy=PROXY)
req = {"userAgent": UA, "pageUrl": page_url, "ip": exit_ip,
       "acceptLanguage": "en-US,en;q=0.9",
       "script": s.get(script_url).text}

def post():
    ans = requests.post(
        "https://api.parsalabs.io/perimeterx/collector",
        headers={"Authorization": "Bearer parsa_…"}, json=req,
    ).json()
    reply = s.post(ans["url"], data=ans["body"], headers={
        "Content-Type": "application/x-www-form-urlencoded"})
    # The reply is the collector's commands; the next
    # post answers them.
    req.update(build=ans["build"], context=ans["context"],
               collectorResponse=reply.text)
    req.pop("script", None)

post()  # and again for each post the page makes
const res = await fetch("https://api.parsalabs.io/perimeterx/collector", {
  method: "POST",
  headers: {
    "Authorization": "Bearer parsa_…",
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    userAgent, pageUrl,
    ip: exitIp,
    build,                // or script, the first time
    context,              // from the previous answer
    collectorResponse,    // the collector's last reply
  }),
});
const { url, body } = await res.json();
// POST body to url, form-encoded, from your client.

Benchmarks

Measured on our solver with a real sensor build. The time is what we spend writing a post, which your client waits for on top of the round trip to api.parsalabs.io. A browser has to run the sensor script for every page load.

MeasureParsaHeadless Chromium
Collector post (known build)About 1 msThe sensor runs in the page
Reading a new buildAbout 25 ms for a 228 KB scriptThe script loads each session
Captured posts reproduced21 of 21, byte for byte–
Memory per sessionNone on your side: a JSON request277 MB for the first, about 102 MB for each one after
Ten sessions at onceTen HTTPS requests1,198 MB of Chromium
Start-up, before any networkNoneAbout 100 ms to launch and load an empty page

PerimeterX pricing

€1.50per 1,000 solves

Prepaid from €5, no plan and no monthly fee. Only an answer that comes back 200 is billed: an error, a timeout or a build we can't read costs nothing.

Collector posts (_px2, _px3)
€1.50 / 1,000

PerimeterX questions

How do I know a site uses PerimeterX?

The page loads main.min.js from client.px-cloud.net or px-cdn.net under an app id starting with PX, or an init.js from a short path on the site's own domain, and the site sets _pxhd or _pxvid. Its collector posts go to collector-<app id>.px-cloud.net.

Is PerimeterX the same as HUMAN Security?

Yes. PerimeterX merged with HUMAN in 2022, and the sensor and the _px cookies kept their names. This API covers the web sensor.

How long does a _px3 cookie last?

The collector bakes _px2 and _px3 with a lifetime of 330 seconds, about five and a half minutes. Each page load is its own session, so keep posting to the collector as a browser would and the cookie stays fresh.

What are _pxvid, _pxhd and pxcts?

_pxvid is the visitor id, set for a year. _pxhd comes from the site's edge and your client only echoes it. pxcts is set by the script itself. Keep them all in your jar; only _px2 and _px3 are the clearance.

What if the site ships a new sensor build?

If Parsa hasn't seen a build, it asks for the script again (409, unknown_build). Send init.js once more and carry on. A build the solver can't read is answered with an error and kept for us to look at.

Does it solve the press-and-hold captcha?

Not yet. The captcha runs over the same collector channel, but its record isn't generated yet. Clean sessions, with a sticky IP and a browser TLS fingerprint, rarely meet it.

Am I charged when a post is rejected?

You pay €0.0015 when Parsa answers 200 with a post to send. A build it can't read, a bad context or any other error costs nothing.

GuideHow PerimeterX (HUMAN) worksRead the guide →

Other solvers