API Documentation

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.

Base URL https://hackerssdatadomesss.up.railway.app
i
AI Agent Quick Tip Copy-paste the URL 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."
Overview

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.

Setup

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.

  1. Open the protected site in Chrome, Safari, or Edge.
  2. Press F12 and click the Console tab.
  3. Paste the snippet below and press Enter.
  4. Copy the hex string that appears — that's your dd_key. If it returns null, refresh the page and try running the snippet again immediately, or filter the Network tab for captcha-delivery.com to 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})()
Endpoint

Health check.

GET /health

Returns 200 if the service is running. Use this for monitoring and uptime checks.

cURL
curl https://hackerssdatadomesss.up.railway.app/health
Endpoint

Service status.

GET /status

Returns service status and runtime statistics.

cURL
curl https://hackerssdatadomesss.up.railway.app/status
Endpoint

Solve a challenge.

POST /solve

Solve a DataDome challenge and return a valid datadome session cookie. Offers options for headless browser solving, verification, and execution delays.

Request Body Parameters
FieldTypeRequiredDescription
sitestringYesTarget website root origin (e.g. https://seatgeek.com).
keystringYesDataDome hsh (dd_key) hexadecimal value.
two_phasebooleanNoEnable headless solver fallback (highly recommended for complex sites). Default: false.
delaynumberNoDelay in seconds during headless solver telemetry execution. Default: 5.
verifybooleanNoVerify if the returned cookie successfully bypassed protection before returning. Default: false.
cURL Example
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
}
Endpoint

Solve and fetch.

POST /fetch

Solves the challenge and fetches the target page content in a single round trip.

Parameters
FieldTypeRequiredDescription
sitestringYesTarget website root origin.
keystringYesDataDome hsh (dd_key) value.
urlstringYesSub-page URL to fetch after solving.
methodstringNoHTTP request method (GET, POST). Default: GET.
cURL
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"
  }
}
Endpoint

Encrypt the key.

POST /encrypt

Encrypts the dd_key using DataDome's internal algorithm.

Parameters
FieldTypeRequiredDescription
sitestringYesTarget website root origin.
keystringYesDataDome hsh (dd_key) value.
cURL
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"
}
Examples

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"}'
Real-world Workflows

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"));
For AI Agents

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.

i
AI Agent Quick Tip You can copy and paste the URL 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

1 Autonomous scraping agents

The model decides which URL to fetch and what to extract — it calls /fetch, parses the HTML, then loops.

2 User-driven research bots

A user asks "summarize today's listings from SeatGeek" — the agent extracts the key, solves, fetches, and answers.

3 Pipelines with tool calling

Wire /solve, /fetch, and /encrypt as tools in the OpenAI / Anthropic function-calling API.

For AI Agents

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.

P

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"],
    },
  },
];
For AI Agents

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:

1 Playwright MCP Recommended

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"]
    }
  }
}
2 browser-use (Python)

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
3 Stagehand (Node.js)

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
i
Why a real browser? DataDome embeds the 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.
S

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.

Troubleshooting

Resiliency & solutions.

Bypassing bot protection can sometimes fail or return errors. Here is how to handle common edge cases.

!
`dd_key` extraction returns null If the browser console snippet returns 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.
i
Solver Timeouts (HTTP 504 / Network Errors) Solving DataDome challenges requires executing telemetry in real-time. This can take anywhere from 5 to 30 seconds. Always configure your HTTP client timeout to at least 90 seconds to avoid dropping active requests.
i
Cookie Expiration & 403 Forbidden DataDome session cookies are short-lived (typically 10-30 minutes). When scraping, implement a fallback mechanism: cache the cookie, use it for requests, and if you encounter an HTTP 403, trigger a re-solve request to Hop to refresh the cookie cache.
Important

Read before shipping.

!
Legal Notice Use this API only on websites you own or have explicit permission to test. Bypassing bot protection without authorization may violate terms of service and applicable laws.
i
Performance Solving challenges typically takes 5–30 seconds depending on the target. Standard solver options trade a small amount of latency for higher reliability.