DataDome Bypass
DataDome is a formidable bot-protection wall designed to keep automated traffic out. But when you're building autonomous agents or extracting data for legitimate tools, that wall becomes a roadblock. This API solves the challenge securely and returns a valid session cookie — letting your agents read the raw data protected behind the dome.
https://hackerssdatadomesss.up.railway.app
https://datadome.xeea.net or the raw document https://datadome.xeea.net/skills.md directly to your AI agent and ask: "Read the documentation at this URL to learn how to bypass DataDome using Hop."
Built for developers & AI Agents.
Bypass DataDome challenges programmatically, acquire valid session cookies, and request protected data seamlessly using Hop.
How Hop works
Hop acts as a smart bridge between your automated scraper or LLM agent and protected web content.
1. Request Solve
Your scraper or AI agent requests access to a URL by extracting and sending its public dd_key.
2. Solve Telemetry
Hop executes DataDome's challenge telemetry behind the scenes and returns a valid session cookie.
3. Read Web Content
Your scraper uses the session cookie to make seamless requests and extract protected data.
Getting the dd_key.
Every request needs the DataDome hsh value (also known as the dd_key) from the target site. It's a public site key, not a private credential.
- Open the protected site in Chrome, Safari, or Edge.
- Press
F12and click the Console tab. - Paste the snippet below and press
Enter. - Copy the hex string that appears — that's your
dd_key. If it returnsnull, refresh the page and try running the snippet again immediately, or filter the Network tab forcaptcha-delivery.comto find the hash value.
(function(){if(typeof dd!=='undefined'&&dd.hsh){console.log('%cKey:','color:green;',dd.hsh);return dd.hsh}const s=document.querySelectorAll('script');for(const x of s){const m=x.textContent.match(/"hsh":"([a-f0-9]+)"/);if(m){console.log('%cKey:','color:green;',m[1]);return m[1]}}return null})()
Health check.
Returns 200 if the service is running. Use this for monitoring and uptime checks.
cURLcurl https://hackerssdatadomesss.up.railway.app/health
Service status.
Returns service status and runtime statistics.
cURLcurl https://hackerssdatadomesss.up.railway.app/status
Solve a challenge.
Solve a DataDome challenge and return a valid datadome session cookie. Offers options for headless browser solving, verification, and execution delays.
Request Body Parameters| Field | Type | Required | Description |
|---|---|---|---|
site | string | Yes | Target website root origin (e.g. https://seatgeek.com). |
key | string | Yes | DataDome hsh (dd_key) hexadecimal value. |
two_phase | boolean | No | Enable headless solver fallback (highly recommended for complex sites). Default: false. |
delay | number | No | Delay in seconds during headless solver telemetry execution. Default: 5. |
verify | boolean | No | Verify if the returned cookie successfully bypassed protection before returning. Default: false. |
curl -X POST https://hackerssdatadomesss.up.railway.app/solve \
-H "Content-Type: application/json" \
-d '{"site":"https://seatgeek.com","key":"YOUR_KEY","two_phase":true,"verify":true}'
Response Schema (JSON)
{
"name": "datadome",
"value": "DATADOME_COOKIE_VALUE_HERE",
"domain": ".seatgeek.com",
"path": "/",
"expires": 1782390231,
"httpOnly": false,
"secure": true
}
Solve and fetch.
Solves the challenge and fetches the target page content in a single round trip.
Parameters| Field | Type | Required | Description |
|---|---|---|---|
site | string | Yes | Target website root origin. |
key | string | Yes | DataDome hsh (dd_key) value. |
url | string | Yes | Sub-page URL to fetch after solving. |
method | string | No | HTTP request method (GET, POST). Default: GET. |
curl -X POST https://hackerssdatadomesss.up.railway.app/fetch \
-H "Content-Type: application/json" \
-d '{"site":"https://seatgeek.com","key":"YOUR_KEY","url":"https://seatgeek.com/","method":"GET"}'
Response Schema (JSON)
{
"status": 200,
"content": "<!DOCTYPE html><html><head>...</body></html>",
"headers": {
"content-type": "text/html; charset=utf-8"
}
}
Encrypt the key.
Encrypts the dd_key using DataDome's internal algorithm.
| Field | Type | Required | Description |
|---|---|---|---|
site | string | Yes | Target website root origin. |
key | string | Yes | DataDome hsh (dd_key) value. |
curl -X POST https://hackerssdatadomesss.up.railway.app/encrypt \
-H "Content-Type: application/json" \
-d '{"site":"https://seatgeek.com","key":"YOUR_KEY"}'
Response Schema (JSON)
{
"encrypted": "ENCRYPTED_DD_KEY_VALUE_HERE"
}
Use it your way.
Same call, six languages. Pick your stack and ship.
import requests
resp = requests.post(
"https://hackerssdatadomesss.up.railway.app/solve",
json={"site": "https://seatgeek.com", "key": "YOUR_KEY"},
timeout=60,
)
resp.raise_for_status()
cookie = resp.json()
print(cookie["value"])
import axios from "axios";
const { data } = await axios.post(
"https://hackerssdatadomesss.up.railway.app/solve",
{ site: "https://seatgeek.com", key: "YOUR_KEY" },
{ timeout: 60_000 }
);
console.log(data.value);
const res = await fetch(
"https://hackerssdatadomesss.up.railway.app/solve",
{
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
site: "https://seatgeek.com",
key: "YOUR_KEY",
}),
}
);
const cookie = await res.json();
console.log(cookie.value);
package main
import (
"bytes"
"encoding/json"
"fmt"
"net/http"
)
func main() {
body, _ := json.Marshal(map[string]string{
"site": "https://seatgeek.com",
"key": "YOUR_KEY",
})
resp, _ := http.Post(
"https://hackerssdatadomesss.up.railway.app/solve",
"application/json",
bytes.NewBuffer(body),
)
defer resp.Body.Close()
var out map[string]any
json.NewDecoder(resp.Body).Decode(&out)
fmt.Println(out["value"])
}
<?php
$ch = curl_init("https://hackerssdatadomesss.up.railway.app/solve");
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => ["Content-Type: application/json"],
CURLOPT_POSTFIELDS => json_encode([
"site" => "https://seatgeek.com",
"key" => "YOUR_KEY",
]),
]);
$cookie = json_decode(curl_exec($ch), true);
echo $cookie["value"];
curl -X POST https://hackerssdatadomesss.up.railway.app/solve \
-H "Content-Type: application/json" \
-d '{"site":"https://seatgeek.com","key":"YOUR_KEY"}'
End-to-end patterns.
How to compose the endpoints into something useful — scraping, session reuse, and resilient retries.
1. Solve once, reuse the cookie
Solving costs time. Cache the cookie in memory and only re-solve when it expires or stops working.
import requests, time
BASE = "https://hackerssdatadomesss.up.railway.app"
SITE = "https://seatgeek.com"
KEY = "YOUR_KEY"
_cookie_cache = {"value": None, "obtained_at": 0}
def get_cookie(force=False):
age = time.time() - _cookie_cache["obtained_at"]
if not force and _cookie_cache["value"] and age < 600: # 10 min
return _cookie_cache["value"]
r = requests.post(f"{BASE}/solve", json={"site": SITE, "key": KEY}, timeout=60)
r.raise_for_status()
_cookie_cache["value"] = r.json()["value"]
_cookie_cache["obtained_at"] = time.time()
return _cookie_cache["value"]
def scrape(path):
cookie = get_cookie()
headers = {"Cookie": f"datadome={cookie}", "User-Agent": "Mozilla/5.0"}
r = requests.get(f"{SITE}{path}", headers=headers, timeout=30)
if r.status_code == 403: # cookie burned, retry once
cookie = get_cookie(force=True)
headers["Cookie"] = f"datadome={cookie}"
r = requests.get(f"{SITE}{path}", headers=headers, timeout=30)
r.raise_for_status()
return r.text
2. Resilient solve with exponential backoff
Bot-protection challenges occasionally fail under load. Two-phase + retry gives you the best success rate.
import requests, time, random
def solve_with_retry(site, key, max_attempts=4):
for attempt in range(max_attempts):
try:
r = requests.post(
"https://hackerssdatadomesss.up.railway.app/solve",
json={"site": site, "key": key, "two_phase": True, "delay": 5},
timeout=90,
)
if r.ok and r.json().get("value"):
return r.json()["value"]
except requests.RequestException:
pass
wait = (2 ** attempt) + random.random()
print(f"attempt {attempt + 1} failed, retrying in {wait:.1f}s")
time.sleep(wait)
raise RuntimeError("could not solve after retries")
3. One-shot scrape with /fetch
When you only need the page once, /fetch skips the round-trip and returns the rendered HTML directly.
import requests
from bs4 import BeautifulSoup
def scrape_listing(target_url, site, key):
r = requests.post(
"https://hackerssdatadomesss.up.railway.app/fetch",
json={"site": site, "key": key, "url": target_url, "method": "GET"},
timeout=90,
)
r.raise_for_status()
html = r.json()["content"]
soup = BeautifulSoup(html, "html.parser")
return [a.get_text(strip=True) for a in soup.select("a.listing-title")]
print(scrape_listing("https://seatgeek.com/concerts", "https://seatgeek.com", "YOUR_KEY"))
4. Parallel scraping with a worker pool
Need to fetch dozens of pages? Share one cookie across a thread pool — solve once, fan out the GETs.
import requests
from concurrent.futures import ThreadPoolExecutor, as_completed
BASE = "https://hackerssdatadomesss.up.railway.app"
SITE = "https://seatgeek.com"
KEY = "YOUR_KEY"
cookie = requests.post(f"{BASE}/solve",
json={"site": SITE, "key": KEY}, timeout=60).json()["value"]
headers = {"Cookie": f"datadome={cookie}", "User-Agent": "Mozilla/5.0"}
urls = [f"{SITE}/page/{i}" for i in range(1, 21)]
def fetch(u):
return u, requests.get(u, headers=headers, timeout=30).status_code
with ThreadPoolExecutor(max_workers=8) as ex:
for fut in as_completed(ex.submit(fetch, u) for u in urls):
url, status = fut.result()
print(status, url)
5. Node.js scraper class
A small reusable client that handles solving, caching, and forced refresh on 403.
import axios from "axios";
class DataDomeClient {
constructor({ base, site, key, ttlMs = 600_000 }) {
Object.assign(this, { base, site, key, ttlMs });
this.cookie = null;
this.obtainedAt = 0;
}
async getCookie(force = false) {
if (!force && this.cookie && Date.now() - this.obtainedAt < this.ttlMs)
return this.cookie;
const { data } = await axios.post(
`${this.base}/solve`,
{ site: this.site, key: this.key, two_phase: true },
{ timeout: 90_000 }
);
this.cookie = data.value;
this.obtainedAt = Date.now();
return this.cookie;
}
async get(path) {
const headers = { Cookie: `datadome=${await this.getCookie()}` };
try {
return (await axios.get(`${this.site}${path}`, { headers })).data;
} catch (e) {
if (e.response?.status !== 403) throw e;
headers.Cookie = `datadome=${await this.getCookie(true)}`;
return (await axios.get(`${this.site}${path}`, { headers })).data;
}
}
}
const client = new DataDomeClient({
base: "https://hackerssdatadomesss.up.railway.app",
site: "https://seatgeek.com",
key: "YOUR_KEY",
});
console.log(await client.get("/concerts"));
LLM integration.
Drop this whole API into Claude, GPT, Gemini, or any agent loop. Two ready-to-paste artifacts: a system prompt for chat-style tools and a Claude skill manifest for the Claude Code / Agent SDK.
https://datadome.xeea.net or the raw document https://datadome.xeea.net/skills.md directly into your AI agent's chat (such as Claude Code, ChatGPT, or Gemini Gems) and instruct it: "Read the documentation at this URL to understand how to bypass DataDome using the Hop API."
When to use this
The model decides which URL to fetch and what to extract — it calls /fetch, parses the HTML, then loops.
A user asks "summarize today's listings from SeatGeek" — the agent extracts the key, solves, fetches, and answers.
Wire /solve, /fetch, and /encrypt as tools in the OpenAI / Anthropic function-calling API.
System prompt.
Paste this into the system field of any chat completion. It teaches the model the full API surface, the dd_key flow, and best practices for retries.
Copy-paste system prompt
Optimized for Claude and GPT-class models. Drop in verbatim or trim to taste.
You are an assistant with access to Hop (DataDome Bypass API), a self-hosted
service that solves DataDome bot-protection challenges and returns cookies
that let you make authenticated requests to protected sites.
# Base URL
https://hackerssdatadomesss.up.railway.app
# Endpoints
- GET /health Service liveness check.
- GET /status Runtime statistics.
- POST /solve Solve a challenge and return the datadome cookie.
body: { site, key, two_phase?: boolean, delay?: number, verify?: boolean }
- POST /fetch Solve AND fetch target page in one call.
body: { site, key, url, method?: "GET" }
- POST /encrypt Encrypt dd_key with DataDome's algorithm.
body: { site, key }
# The dd_key
Every call needs the DataDome `hsh` value (also known as `dd_key`) from the target site. The user can
extract it in their browser by:
1. Opening the protected site in Chrome/Edge.
2. Pressing F12, Console tab.
3. Running this snippet:
(function(){if(typeof dd!=='undefined'&&dd.hsh){return dd.hsh}const s=document.querySelectorAll('script');for(const x of s){const m=x.textContent.match(/"hsh":"([a-f0-9]+)"/);if(m)return m[1]}return null})()
4. Copying the hex string.
If the user has not provided a key, ask for it before calling the API.
# Workflow rules
1. If the user asks to scrape, fetch, or read a DataDome-protected page,
default to POST /fetch — it's a single round trip.
2. If they need a session for many requests, call POST /solve once, cache
the cookie, and reuse it. Refresh only on 403.
3. For higher reliability, set `two_phase: true` and `delay: 5`.
4. Solving takes 5-30 seconds; set client timeout to 90s.
5. Never claim to have made a request you didn't make. If a call fails,
report the status code and body verbatim.
# Cookie usage
The solve response returns `{ value: "...", name: "datadome", domain: "..." }`.
Attach it to subsequent requests via `Cookie: datadome=<value>`.
# Legal
Only use this against sites the user owns or has authorization to test.
If the user requests scraping a site without that context, ask once for
confirmation that they have permission.
OpenAI / Anthropic tool definitions
If your stack supports function calling, register each endpoint as a tool the model can invoke directly.
// OpenAI / Anthropic tool schema
const tools = [
{
name: "datadome_solve",
description: "Solve a DataDome challenge and return a cookie usable for subsequent requests to the protected site.",
input_schema: {
type: "object",
properties: {
site: { type: "string", description: "Target website root origin URL (e.g., https://seatgeek.com)." },
key: { type: "string", description: "DataDome dd_key (hsh) value from the target site." },
two_phase: { type: "boolean", description: "Enable headless browser solver fallback (highly recommended). Default false." },
delay: { type: "number", description: "Delay in seconds for headless browser challenge execution. Default 5." },
verify: { type: "boolean", description: "Verify if the cookie works before returning it. Default false." },
},
required: ["site", "key"],
},
},
{
name: "datadome_fetch",
description: "Solve the challenge AND fetch the target page in one round trip. Returns the page HTML.",
input_schema: {
type: "object",
properties: {
site: { type: "string" },
key: { type: "string" },
url: { type: "string", description: "URL to fetch after solving." },
method: { type: "string", enum: ["GET", "POST"], description: "HTTP method. Default GET." },
},
required: ["site", "key", "url"],
},
},
{
name: "datadome_encrypt",
description: "Encrypt a dd_key using DataDome's internal algorithm.",
input_schema: {
type: "object",
properties: {
site: { type: "string" },
key: { type: "string" },
},
required: ["site", "key"],
},
},
];
Claude skill.
A drop-in skill for Claude Code, the Claude Agent SDK, or any harness that loads SKILL.md files. Save it under .claude/skills/datadome-bypass/SKILL.md and Claude will discover it automatically — including instructions for fetching the dd_key autonomously when the user can't provide one.
What is a dd_key, really?
The dd_key (also called hsh) is a per-site hex identifier that DataDome embeds in the page HTML or a runtime dd object. It's not a secret — it's a public site-key that DataDome uses to bind challenges to a customer. To solve a challenge programmatically, the API needs that key so it can request the matching challenge bundle from DataDome's edge.
Where to find it: it lives in one of three places on every DataDome-protected page — a window.dd.hsh property, a JSON blob inside an inline <script> tag ("hsh":"..."), or in the URL of the captcha iframe (captcha-delivery.com/captcha/?...&hash=...). An autonomous agent that can render the page can grab it from any of those.
Recommended: install an autonomous browser
For agents to fetch the dd_key without human help, give them a real browser. Three solid options, ranked by ease of LLM integration:
Anthropic-blessed MCP server that exposes Playwright as native tool calls. Zero glue code — Claude Code, Claude Desktop, and any MCP-aware agent get a full browser out of the box.
# add to ~/.config/claude/claude_desktop_config.json or .mcp.json
{
"mcpServers": {
"playwright": {
"command": "npx",
"args": ["-y", "@playwright/mcp@latest"]
}
}
}
LLM-first browser automation library. Plays well with LangChain, OpenAI Agents SDK, and custom loops. Great when you want full programmatic control of the browsing flow.
pip install browser-use playwright
playwright install chromium
Browserbase's TypeScript SDK that mixes Playwright primitives with natural-language actions (page.act("click the cookie banner")). Ideal if your agent stack is Node.
npm install @browserbasehq/stagehand
hsh in a way that's trivial to read with JS evaluation but cumbersome to extract reliably from raw HTML. A headless browser handles all three discovery paths (window object, inline script, captcha iframe) with a single page.evaluate() call.
SKILL.md — autonomous version
This skill teaches the agent the dd_key concept, how to extract it without the user, and when to fall back to asking.
---
name: datadome-bypass
description: Bypass DataDome bot protection on any URL the user wants to
scrape, fetch, or read. The skill knows how to autonomously extract the
required dd_key using a headless browser, then call the DataDome Bypass
API to obtain a cookie or fetch the page directly. Use whenever a target
site returns a CAPTCHA, 403, interstitial, or DataDome challenge.
---
# DataDome Bypass
A self-hosted API for solving DataDome challenges. Use it whenever the user
asks to access a site that's behind DataDome — including cases where the
user hasn't provided a dd_key. You can fetch the key yourself.
## Base URL
`https://hackerssdatadomesss.up.railway.app`
## Concept: what is a dd_key?
The `dd_key` (sometimes called `hsh`) is a per-site hex identifier that
DataDome embeds in every protected page. It's a PUBLIC site-key, not a
secret credential. DataDome's edge uses it to match a challenge to the
correct customer. To solve a challenge with this API, you must pass the
key of the target site.
A dd_key looks like a 64-character lowercase hex string, e.g.:
`a1b2c3d4e5f6...` (64 chars)
## Required inputs
- `site` — the protected origin, e.g. `https://seatgeek.com`
- `key` — the dd_key for that site
## Autonomous dd_key extraction (preferred)
If the user has not provided a key, DO NOT ask them right away. Try to
extract it yourself in this order:
### Path A — headless browser (highest reliability)
If you have access to a browser-automation tool (Playwright MCP,
browser-use, Stagehand, Puppeteer, or any equivalent), use it:
1. Navigate to `site`.
2. Wait for the page to settle (network idle or 3s).
3. Run this evaluation in the page context:
```js
(function(){
// 1. window.dd.hsh
if (typeof dd !== 'undefined' && dd.hsh) return dd.hsh;
// 2. inline script JSON
for (const s of document.querySelectorAll('script')) {
const m = s.textContent.match(/"hsh":"([a-f0-9]+)"/);
if (m) return m[1];
}
// 3. captcha iframe URL
for (const f of document.querySelectorAll('iframe')) {
const u = f.src || '';
const m = u.match(/[?&]hash=([a-f0-9]+)/);
if (m) return m[1];
}
return null;
})()
```
4. The returned hex string IS the dd_key.
### Path B — raw HTTP fetch (fallback, lower hit rate)
If no browser tool is available, GET the site with a normal user-agent
and grep the response body for `"hsh":"([a-f0-9]+)"`. This works on a
subset of DataDome-protected sites where the key is server-rendered.
### Path C — ask the user
Only if A and B both fail. Give them this snippet to paste in DevTools:
```
(function(){if(typeof dd!=='undefined'&&dd.hsh){return dd.hsh}const s=document.querySelectorAll('script');for(const x of s){const m=x.textContent.match(/"hsh":"([a-f0-9]+)"/);if(m)return m[1]}return null})()
```
## Decision tree (after you have a key)
- **User wants one page** → POST `/fetch` (returns HTML directly, single round trip).
- **User wants many requests against the same site** → POST `/solve` once,
cache the cookie, attach as `Cookie: datadome=<value>` to every request.
- **Solve fails or returns empty** → retry with `{ two_phase: true, delay: 5 }`.
- **Cookie stops working (403)** → re-solve, replace cached cookie.
## Recommended browser tool
If the user is setting up an agent and asks how to make this autonomous,
recommend installing the **Playwright MCP server** — it gives Claude (and
any MCP-aware agent) browser tools with no glue code:
```json
{
"mcpServers": {
"playwright": {
"command": "npx",
"args": ["-y", "@playwright/mcp@latest"]
}
}
}
```
Alternatives: `browser-use` (Python), Stagehand (Node), or raw Playwright
in a script.
## Endpoints reference
| Method | Path | Body fields |
| ------ | --------------------- | ------------------------------------ |
| GET | /health | — |
| GET | /status | — |
| POST | /solve | site, key, two_phase?, delay?, verify?|
| POST | /fetch | site, key, url, method? |
| POST | /encrypt | site, key |
## Defaults & timeouts
- dd_key extraction (browser): allow up to 30s for page load + eval.
- Solve latency: 5-30 seconds. Use a 90s client timeout.
- Cache the cookie for ~10 minutes; refresh on 403.
- Cache the dd_key per-site indefinitely — it changes rarely.
## End-to-end example (autonomous)
1. User: "Get the homepage of seatgeek.com."
2. Agent: navigates to `https://seatgeek.com` with Playwright,
evaluates the snippet, gets `key = "abc123..."`.
3. Agent: POST `/fetch` with `{site, key, url: site, method: "GET"}`.
4. Agent: returns the rendered HTML to the user.
## Constraints
- Only assist with sites the user owns or has explicit authorization to test.
If the request is ambiguous, ask once for confirmation.
- If you fetched the dd_key yourself, mention it in your reply so the user
knows what happened ("I extracted the dd_key from the site automatically.").
- Report API failures verbatim — never fabricate a successful response.
- The dd_key is not a secret, but the cookie returned by /solve is a
short-lived session token. Treat it like one.
Reference: standalone dd_key extractor
If you'd rather wire the extraction into your own agent loop instead of relying on a skill, here's a self-contained Playwright function. Drop it in, await it, get the key.
// dd_key_extractor.mjs — Node.js + Playwright
import { chromium } from "playwright";
export async function extractDdKey(site, { timeout = 30_000 } = {}) {
const browser = await chromium.launch({ headless: true });
try {
const ctx = await browser.newContext({
userAgent:
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) " +
"AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0 Safari/537.36",
});
const page = await ctx.newPage();
await page.goto(site, { waitUntil: "networkidle", timeout });
const key = await page.evaluate(() => {
if (typeof dd !== "undefined" && dd.hsh) return dd.hsh;
for (const s of document.querySelectorAll("script")) {
const m = s.textContent.match(/"hsh":"([a-f0-9]+)"/);
if (m) return m[1];
}
for (const f of document.querySelectorAll("iframe")) {
const m = (f.src || "").match(/[?&]hash=([a-f0-9]+)/);
if (m) return m[1];
}
return null;
});
if (!key) throw new Error("dd_key not found on page");
return key;
} finally {
await browser.close();
}
}
// usage
const key = await extractDdKey("https://seatgeek.com");
const res = await fetch(
"https://hackerssdatadomesss.up.railway.app/fetch",
{
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
site: "https://seatgeek.com",
key,
url: "https://seatgeek.com/",
method: "GET",
}),
}
);
console.log((await res.json()).content.slice(0, 200));
How Claude uses it
With the skill installed and a browser tool available, asking Claude "fetch the homepage of seatgeek.com" triggers the full autonomous flow: it loads the site in the headless browser, extracts the dd_key, calls /fetch, and returns the parsed HTML — without ever asking you for a key.
Resiliency & solutions.
Bypassing bot protection can sometimes fail or return errors. Here is how to handle common edge cases.
null, it means the hsh value isn't loaded statically or the window variable is empty. To resolve:1. Keep DevTools open, reload the page, and run the snippet immediately.
2. Go to the Network tab, filter for
captcha-delivery.com, and look at the request URL. The value after hash= is your site's dd_key.