Browser and session

Use browser and session inputs when your integration needs them.

Use these steps when your integration needs browser values or session continuity, in either mode. Prepared SDK inputs do not require a browser collector. The application example below uses headers; follow Form payload integration to compose a form body.

Use measured browser values

Run the profile collector on the source page when measured values are needed and save profile.json. Supply those values in profile; do not invent measurements or randomize them between collection and sending.

Profile fields
FieldDetails
user_agent, platform, language, languagesBrowser identity and language.
navigatoruserAgent, platform, vendor, appVersion, appCodeName, appName, product, language, languages, hardwareConcurrency, maxTouchPoints, webdriver, cookieEnabled, doNotTrack.
screenwidth, height, availWidth, availHeight, colorDepth, pixelDepth.
windowinnerWidth, innerHeight, outerWidth, outerHeight, devicePixelRatio, screenX, screenY, screenLeft, screenTop, pageXOffset, pageYOffset.
webglrenderer and vendor.
canvasOptional data_url with the actual PNG pixels required by the protection code. A hash is not a substitute.
  • Unknown fields and conflicting top-level/navigator aliases are rejected.
  • {} selects server defaults, not your actual browser. Check the returned profile_used.
  • The SDK’s validate_profile checks inputs offline. Native Android/iOS protection is outside this web Shape API.
Profile bounds
  • Identity strings: 8,192 characters. Languages: 1–32 values.
  • Dimensions: up to 32,768; available screen dimensions may be zero. Coordinates: signed 32-bit; pixel ratio: 0.1–16.
  • Hardware concurrency: 1–1,024; touch points: 0–100; color/pixel depth: 1–64.
  • Canonical profile: 64 KiB. Canvas PNG: 64 KiB, up to 8,192 pixels per dimension and 16 million total pixels.

Keep the target session consistent

  • Use browser-grade target transport, such as a compatible curl_cffi Chrome impersonation. A User-Agent alone does not guarantee a matching TLS fingerprint.
  • When session continuity applies, keep IP continuity and browser identity from collection through the protected request. Retain a sticky proxy only if your integration uses one.
  • When cookies are used, keep them in a scoped cookie jar. Supply document_cookie from the source page; exclude HttpOnly values.
  • Match the browser’s header values and order. Keep required Origin, Referer, authentication and CSRF headers when required. Remove stale telemetry before applying the entire fresh header map with names unchanged and values intact, including optional same-family a0 when the scripts emit it.

RayonAPI does not return replacement cookies. The SDK’s connection to RayonAPI does not configure your target client. Never forward the RayonAPI key to the target. Do not forward generated headers or cookie values to unrelated hosts.

Send the exact request

Check the destination against an explicit approved HTTPS origin allowlist. Source and API subdomains may differ when both have approved access.

Send through your existing target_session
from urllib.parse import urlsplit

def https_origin(url):
    parsed = urlsplit(url)
    if (parsed.scheme != "https" or not parsed.hostname
            or parsed.username is not None or parsed.password is not None
            or parsed.fragment):
        raise ValueError("Use HTTPS without credentials or a fragment")
    port = parsed.port if parsed.port is not None else 443
    if not 1 <= port <= 65535:
        raise ValueError("Invalid port")
    return (parsed.scheme, parsed.hostname.lower(), port)

allowed_origins = {
    https_origin("https://shop.example.com"),
    https_origin("https://api.shop.example.com"),
}
request = shape["request"]
if https_origin(request["url"]) not in allowed_origins:
    raise ValueError("Endpoint origin not allowed")
body = request["body"]
headers = {**application_headers, **result.headers,
           "User-Agent": result.profile_used["user_agent"]}
if request["content_type"] is not None:
    headers["Content-Type"] = request["content_type"]
response = target_session.request(
    request["method"], request["url"],
    data=body.encode("utf-8") if body is not None else None,
    headers=headers, allow_redirects=False, timeout=30,
)
  • target_session is the session that fetched your scripts; application_headers contains the target’s required headers, not the RayonAPI key.
  • Serialize JSON once. Preserve whitespace and UTF-8 bytes. Null means no body; an empty string means an empty body.
  • Inspect the target body as well as its status. HTTP 200 can still contain a challenge page.

For a runnable version, use the complete Python workflow.