Collect material

Find and download the protection scripts your request needs.

Build shape.json in your application

API input structure — placeholder source does not generate headers
{
  "source_url": "https://shop.example.com/account",
  "scripts": [{"url": "https://shop.example.com/protection.js?seed=example", "source": "<full downloaded JavaScript>"}],
  "profile": {},
  "request": {
    "url": "https://shop.example.com/api/check",
    "method": "POST",
    "content_type": "application/json",
    "body": "{\"username\":\"example_user\"}"
  }
}

Replace the placeholder source with complete JavaScript you already have. A profile object is required; use measured values when needed, or {} for server defaults. Omit document_cookie when unused; it defaults to an empty string. For GET/HEAD, set body and content_type to null.

Generate with Quickstart, then send with your own target client. Keep input files outside Git. The collection steps below are optional if you already have these inputs.

What goes in scripts?

Each scripts item has exactly two fields: url and source, both strings. The source is the full decoded JavaScript response body; request.body is your target request body. URLs alone, HTML script tags and base64 are not script sources.

If the observed flow uses both, include the loader first, then the payload and any required dependencies in execution order. Use one item for one self-contained script. Keep each complete observed URL, including all query parameters, paired with its unmodified response body. Do not trim or beautify the source. Extensionless script URLs work too; a path or file name alone cannot establish which scripts your flow requires.

Your existing flow, request logs or network inspector can provide these inputs; no particular browser or automation product is required. See the C# REST example for serialization. Supply exactly one material field: scripts plus material_ref: null is invalid. Keep explicit null body and content_type fields for GET/HEAD.

Inspect Payload → Form Data as well as headers. For URL-encoded body telemetry, follow Form payload integration.

Find missing inputs when needed

  1. If you need to inspect a browser integration, open the source page in its network inspector. For example, in DevTools → Network, enable Preserve log, then perform the action you need.
  2. Select the protected request carrying telemetry in headers or Payload → Form Data. Supported material returns a/b/c/d/f/z, with optional same-family a0 in header mode; names vary and other families may be unsupported. Use its Initiator chain to find the protection JavaScript, including its loader, payload and required dependencies.
  3. Copy the complete script URLs, including query parameters. Preserve their execution order. Names and paths vary by site; downloading every application script is not a reliable substitute.
  4. Save the request URL, method, content type and exact UTF-8 body as request-body.txt. For GET/HEAD, use no body.

source_url is the page that loaded the scripts, not the API endpoint. Keep its query and remove any #fragment.

Capture browser inputs when applicable

Use this step when the integration depends on measured browser values or session cookies. Do not invent measurements or add cookies, authentication or a proxy to an integration that does not use them.

  1. Open the profile collector, review it, and paste it into the source page’s DevTools Console. Save the copied JSON as profile.json.
  2. When needed, copy document.cookie from that same page into document-cookie.txt without a trailing newline. Use an empty file when the string is empty.
  3. If the target uses cookies, keep its full HTTP jar in your target client. For the optional Python helper, export a scoped Netscape jar as cookies.txt, retaining domain, path, Secure and expiry information. Keep the original export’s HttpOnly metadata; the helper’s HTTP transport may not retain that flag.

The collector reads values; it does not change browser settings, fetch scripts or collect cookies. It does not capture protection-specific canvas pixels. The HTTP jar and JavaScript-visible document.cookie are different inputs.

Optional same-session download

If your integration requires session continuity, use one target session for the page, scripts and final request. This example uses browser inputs. Choose a supported curl_cffi impersonation compatible with your profile. If a download returns an HTML challenge, fix the session before generating.

Collect with one Python session
import json
import os
from pathlib import Path
from curl_cffi import CurlOpt
from curl_cffi.requests import Session

profile = json.loads(Path("profile.json").read_text(encoding="utf-8"))
# SOURCE_URL, LOADER_URL and PAYLOAD_URL come from your own Network capture.
source_url = os.environ["SOURCE_URL"]
protection_urls = [os.environ["LOADER_URL"], os.environ["PAYLOAD_URL"]]
user_agent = profile.get("user_agent") or profile["navigator"]["userAgent"]
proxy = os.environ.get("RAYON_TARGET_PROXY") or None
target_session = Session(
    impersonate=os.environ["CHROME_PROFILE"], trust_env=False,
    proxy=proxy, curl_options={} if proxy else {CurlOpt.PROXY: ""},
    headers={"User-Agent": user_agent},
)
# Import your existing scoped cookies before collection, when required.
page = target_session.get(source_url, allow_redirects=False, timeout=30)
page.raise_for_status()
if page.status_code != 200:
    raise ValueError("Inspect the source-page redirect or response first")
scripts = []
for script_url in protection_urls:
    response = target_session.get(script_url, allow_redirects=False, timeout=30)
    response.raise_for_status()
    source = response.content.decode("utf-8", errors="strict")
    if (response.status_code != 200
            or "text/html" in response.headers.get("content-type", "").lower()
            or source.lstrip().startswith("<")):
        raise ValueError("Expected JavaScript, received a redirect or HTML challenge")
    scripts.append({"url": script_url, "source": source})
# Keep target_session open for the application request, then close it.

This snippet shows the download step. The linked workflow reference imports cookies, validates inputs and sends the request. It does not guess which scripts your integration needs.

Optional Python helper

Use the Python examples only when you want explicit script downloads and optional target sending through one curl_cffi session. If your integration already supplies inputs, call the SDK directly.

The workflow reference covers config fields, scoped cookies, allowed origins, form mode and offline validation before explicit network flags. The helper does not discover scripts or measure a browser profile.

Reuse material, not headers

The first successful /v1/shape call uploads the scripts and generates a result; there is no separate free upload endpoint. Later calls replace scripts with the returned material_ref and still supply the current per-call inputs. Direct REST callers retain their sources and manage this reference themselves.

  • material_ref is scoped to the API key and original source URL. The process-local cache lasts five minutes and has a bounded capacity; eviction or restart can expire a reference earlier. Reusing a reference does not extend its expiry. Re-uploading identical source near expiry can return a new reference. Use the returned material_expires_at.
  • Persistent Python and JavaScript SDK 1.1.4 clients reuse references in header and form modes. Each call still supplies the current profile, request and cookie inputs and generates fresh telemetry.
  • Python and JavaScript SDK 1.1.4 retain up to 128 original script bundles and 16 MiB of serialized material per client. It coalesces concurrent uploads of the same material and refreshes a reference on the next real request within five seconds of expiry. It never downloads scripts or starts background billable generation.
  • It retries once only after HTTP 422 material_expired with billing_status: not_charged, when the original scripts are still retained. Other generation failures are not automatically retried. Retain the originals and explicitly re-upload after checking the error and billing outcome.