# DataDome Solver — datadome Cookie per URL (`lentic_clockss/datadome-solver`) Actor

Get a datadome session cookie for each URL behind a DataDome protection layer, plus the fetched page body. Each row carries the cookie value, a ready-to-send Cookie header, domain, expiry and the page. $0.55 per 1,000 rows.

- **URL**: https://apify.com/lentic_clockss/datadome-solver.md
- **Developed by:** [kane liu](https://apify.com/lentic_clockss) (community)
- **Categories:** Developer tools, Automation, MCP servers
- **Features:** Limited permissions
- **Stats:** 1 total users, 0 monthly users, 0.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.55 / 1,000 results

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#pay-per-event

## What's an Apify Actor?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

**DataDome Solver — `datadome` cookie per URL: for each page URL you get the page status and body and, when the target presents its interstitial flow, a `datadome` session cookie you can reuse in your own HTTP client, browser or pipeline. $0.55 per 1,000 rows.**

- One dataset row per URL: the page status and body, plus the `datadome` cookie value when the target issued one.
- A ready-to-send `Cookie` header in `cookieHeader`, so there is no extra parsing.
- A `credential` block with the cookie name, domain, `issuedAt`, `expiresAt` and `ttlSeconds`.
- A row that fails says why in `failure_class` and `error` instead of returning a silent empty result.
- Pay only for the rows you receive: $0.55 per 1,000 rows on the Free plan plus a small start fee.

### What does DataDome Solver do?

DataDome Solver accepts a list of public page URLs that sit behind a DataDome protection layer. For each URL it fetches the page and returns the page status and body. When the target presents its interstitial flow, the row also carries the `datadome` session cookie that the fetch ended with, a ready-to-send `Cookie` header and a credential block (`domain`, `issuedAt`, `expiresAt`, `ttlSeconds`).

- **Page and cookie (default)**: every URL in `urls` produces one row with `statusCode`, `body`, and, when a cookie was issued, `datadomeCookie`, `cookieHeader` and `credential`.
- **Pages that need no cookie**: when the page arrives without a DataDome challenge, the row still carries the page and says `credentialNeeded` is `false`.
- **One row per URL**: `maxResults` caps how many URLs one run delivers and charges.

The page is fetched in the same session that obtained the cookie. That matters because the cookie is tied to the connection that received it (see the FAQ).

### What data can you extract from DataDome Solver?

| Field | Description | Example |
| --- | --- | --- |
| `url` | The input URL | `https://www.example.com/` |
| `finalUrl` | URL after any redirects, when it differs from the input | `https://www.example.com/en/protected-page` |
| `statusCode` | HTTP status of the fetch after the cookie was applied | `200` |
| `body` | Page HTML fetched with the cookie, truncated at 200,000 characters | `<!doctype html>…` |
| `bodyLen` | Byte length of the page before text truncation | `209553` |
| `bodyTruncated` | `true` when `body` was cut at the character limit | `true` |
| `datadomeCookie` | The `datadome` cookie value | `AbC123…` |
| `cookieHeader` | `datadome=<value>`, ready to send as a `Cookie` header | `datadome=AbC123…` |
| `credential` | Session credential object | see Output example |
| `credential.name` | Cookie name, always `datadome` | `datadome` |
| `credential.domain` | Domain the cookie applies to | `www.example.com` |
| `credential.value` | Same value as `datadomeCookie` | `AbC123…` |
| `credential.ttlSeconds` | Seconds the cookie is expected to remain valid | `21600` |
| `credential.issuedAt` | When the cookie was issued (UTC) | `2026-10-28T09:00:00Z` |
| `credential.expiresAt` | `issuedAt + ttlSeconds` (UTC) | `2026-10-28T15:00:00Z` |
| `credentialNeeded` | `false` when the page arrived without a DataDome challenge | `true` |
| `tokenIssued` | `true` when a `datadome` cookie was issued for this row | `true` |
| `tokenBinds` | Which caller attributes the cookie is bound to, when the target publishes that information | `{}` |
| `ttlHintS` | How long the cookie is expected to remain valid, in seconds | `21600` |
| `elapsedMs` | Total milliseconds this URL took | `3120` |
| `provider` | Tag of the method that produced the row | `datadome` |
| `billable` | Whether this row is charged | `true` |
| `failure_class` | Machine-readable failure reason when the row did not succeed | a short reason code |
| `error` | Human-readable failure detail when the row did not succeed | a sentence naming the problem |
| `scrapedAt` | When the row was written (UTC) | `2026-10-28T09:00:03Z` |
| `rowType` | Always `page` | `page` |
| `entityId` | The input URL this row answers | `https://www.example.com/` |

Fields that do not apply to a row can be absent. For example, a row with `credentialNeeded` set to `false` has no `datadomeCookie`, and only a failed row has `failure_class` and `error`.

### How to use DataDome Solver

1. Open [DataDome Solver](https://apify.com/lentic_clockss/datadome-solver) in Apify Console (a free Apify account is enough to try it).
2. Fill `urls` with one or more page URLs that sit behind DataDome. Replace the sample URL with a page you want.
3. Leave `maxResults` small, for example 3, for your first run.
4. Click **Start** and wait for the run to finish.
5. Read `datadomeCookie` or `cookieHeader` from each row, check `statusCode` and `body`, and send the cookie on your next requests to `credential.domain` before `credential.expiresAt`.

The input form is prefilled with one sample URL and `maxResults` set to 3, so you can start a run straight away and see the output format before you spend much. The sample URL is a placeholder, so use your own page to get real data. An input with no URL stops at once with the message "Provide at least one URL in urls." and no row is charged.

#### One run, several URLs

Put every URL in the `urls` list. The Actor drops duplicates and processes each URL once, and each URL produces exactly one row, so the row count is easy to predict from the list.

### What to fill in

| Field | What to enter | Default |
| --- | --- | --- |
| `urls` | One or more public page URLs behind DataDome. Each must start with `http://` or `https://` | required |
| `maxResults` | Most rows this run delivers and charges, one per URL. From 1 to 70 | `3` |
| `country` | Two-letter country code for the outbound connection, for example `US` or `DE` | empty |
| `proxyGroup` | Outbound connection group name. Leave empty for the default | empty |

```json
{
  "urls": ["https://www.example.com/"],
  "maxResults": 3,
  "country": "US"
}
```

- `maxResults` is the most rows for the whole run. Each URL gives one row, so a run never delivers more rows than URLs. The maximum is 70.
- Unknown fields never fail the run; they are ignored.
- Wrong input stops the run at once with a message that names the problem, for example a URL that is not an http(s) address or a `country` that is not two letters. No rows are charged.
- The `INPUT_ECHO` record in the run's key-value store shows how your input was read, including how many URLs were sent.

### Output example

```json
{
  "rowType": "page",
  "entityId": "https://www.example.com/",
  "url": "https://www.example.com/",
  "finalUrl": "https://www.example.com/",
  "statusCode": 200,
  "body": "<!doctype html><html>…</html>",
  "bodyLen": 209553,
  "bodyTruncated": true,
  "datadomeCookie": "AbC123…",
  "cookieHeader": "datadome=AbC123…",
  "credential": {
    "name": "datadome",
    "domain": "www.example.com",
    "value": "AbC123…",
    "cookieHeader": "datadome=AbC123…",
    "ttlSeconds": 21600,
    "issuedAt": "2026-10-28T09:00:00Z",
    "expiresAt": "2026-10-28T15:00:00Z"
  },
  "credentialNeeded": true,
  "tokenIssued": true,
  "tokenBinds": {},
  "ttlHintS": 21600,
  "elapsedMs": 3120,
  "provider": "datadome",
  "billable": true,
  "scrapedAt": "2026-10-28T09:00:03Z"
}
```

You can export the dataset as JSON, CSV, Excel, XML, RSS or an HTML table. To use the cookie, send `cookieHeader` as the `Cookie` request header (or set a cookie named `datadome` with `credential.value`) on requests to `credential.domain` while `credential.expiresAt` has not passed.

### Why use this DataDome solver?

- **Pay per row.** You pay for rows written to the dataset, not for URLs tried.
- **Partial results are kept.** If the run is stopped or times out, rows collected so far are delivered and billed; nothing else is.
- **Clear errors.** A row that fails carries a reason in `failure_class` and `error` instead of a silent empty result.
- **No made-up data.** A field the page does not provide stays empty.
- **Several URLs per run.** Pass a list and get one row for each URL.
- **No setup.** You do not need to set up proxies, cookies or an account with the target site.

### How much does it cost to use DataDome Solver?

The Actor uses pay-per-event pricing: a $0.005 start fee per run and a price per row delivered. Platform usage is included in these prices.

| Apify plan | Price per row | Price per 1,000 rows |
| --- | --- | --- |
| Free | $0.00055 | $0.55 |
| Starter | $0.00055 | $0.55 |
| Scale | $0.00055 | $0.55 |
| Business | $0.00055 | $0.55 |
| Platinum | $0.00055 | $0.55 |
| Diamond | $0.00055 | $0.55 |

- `maxResults` applies to the whole run, one row per URL. Default `3`; the maximum is `70`.
- Runs on the free Apify plan stop at 200 rows.
- The run never costs more than the **Maximum cost per run** you set in the run options (`maxTotalChargeUsd` in the API). If that limit leaves room for fewer rows than you asked for, the run delivers only as many rows as the limit covers.
- The Apify Free plan includes **$5.00** in monthly usage credit (about **9,090 rows** at `$0.00055` per row across Free-plan runs capped at 200 rows per run).

Example: 20 URLs → 20 rows → `20 × $0.00055 + $0.005 = $0.016` on the Free plan.

### Use with AI agents and the API

#### Apify MCP server (Claude, ChatGPT, Cursor, VS Code)

Add this Actor as a tool in any MCP client that supports remote servers:

```
https://mcp.apify.com?tools=lentic_clockss/datadome-solver
```

Sign in with Apify when the client asks (OAuth), or send the header `Authorization: Bearer <YOUR_APIFY_TOKEN>`. The tool starts a run and returns the run and dataset IDs; the agent then reads the rows with `get-dataset-items`. Clients without remote MCP support can run `npx @apify/actors-mcp-server --tools lentic_clockss/datadome-solver` with the `APIFY_TOKEN` environment variable.

Example prompt: "Get a datadome cookie and the page for https://www.example.com/."

- **Agent execution note:** Set `maxResults` between `5` and `20` on exploratory tool calls to keep responses compact, and read `RUN_SUMMARY` and `INPUT_ECHO` from the default key-value store to inspect normalized inputs and delivered row counts.

#### Apify API

```python
import requests
from apify_client import ApifyClient

client = ApifyClient("<YOUR_APIFY_TOKEN>")
run = client.actor("lentic_clockss/datadome-solver").call(
    run_input={"urls": ["https://www.example.com/"], "maxResults": 3}
)
for item in client.dataset(run["defaultDatasetId"]).iterate_items():
    print(item.get("cookieHeader"), item["statusCode"])
    if item.get("cookieHeader"):
        resp = requests.get(item["url"], headers={"Cookie": item["cookieHeader"]}, timeout=30)
        print(resp.status_code, len(resp.text))
```

```javascript
import { ApifyClient } from 'apify-client';

const client = new ApifyClient({ token: '<YOUR_APIFY_TOKEN>' });
const run = await client.actor('lentic_clockss/datadome-solver').call({
  urls: ['https://www.example.com/'],
  maxResults: 3,
});
const { items } = await client.dataset(run.defaultDatasetId).listItems();
console.log(items);
```

Or over HTTP: `POST https://api.apify.com/v2/acts/lentic_clockss~datadome-solver/runs` with the input as the JSON body and your token in the `Authorization: Bearer` header, then `GET https://api.apify.com/v2/datasets/<datasetId>/items?format=json`.

#### Integrations

Connect the results to Google Sheets, Make, Zapier, n8n, Slack, Airtable or your own webhook from the **Integrations** tab of the Actor, or schedule regular runs with Apify Schedules.

### FAQ

#### How do I use the cookie?

Send `cookieHeader` as the `Cookie` header on requests to `credential.domain`, or set a cookie named `datadome` with `credential.value`. Plan to use it before `credential.expiresAt`, and use it in the same session that received it (see the next question).

#### Does an issued cookie guarantee that the page loads?

An issued cookie does not guarantee the page loads: the credential binds the exit IP, TLS profile and UA used at issuance, so consume it in the same session; this actor fetches the page in-session for you. If you replay the cookie from a different connection or client, the target may ask for a new challenge. `tokenBinds` reports which attributes the cookie is bound to when the target publishes that information, and `statusCode` and `body` in the row show what the in-session fetch returned.

#### What happens when the site uses a form the Actor does not support?

The row says so. An unsupported challenge form returns `failure_class`, not an empty success: `tokenIssued` is `false`, and `failure_class` and `error` describe what went wrong. Check `billable` on such a row to see whether it is charged.

#### Why did a row have no `datadomeCookie`?

Either the page arrived without a challenge (`tokenIssued` is `false` and `credentialNeeded` is `false`), or the cookie could not be obtained. Such a row still carries `statusCode` and `body` when the page was fetched, and `failure_class` and `error` when it was not.

#### How long does the cookie last?

`credential.ttlSeconds` and `ttlHintS` give the expected lifetime in seconds, and `credential.expiresAt` is the matching UTC time. These are expectations, not guarantees. The target can end a session earlier.

#### Why did my run end as ABORTED?

A run ends ABORTED when the run timeout you set is shorter than the time the URLs needed, or when the run's charge limit was reached. The default timeout is 300 seconds; give the run at least that. Rows already collected are still delivered and billed.

#### How many results can I get?

One row per URL in `urls`, at most `maxResults` (maximum 70) per run. Runs on the free Apify plan stop at 200 rows.

#### Why are some fields empty?

`datadomeCookie`, `cookieHeader`, `credential` and the timing fields are filled only when a cookie was issued. `failure_class` and `error` are filled only on rows that did not succeed. `finalUrl` is set when the final address differs from the input.

#### Is it legal to use this Actor?

You are responsible for complying with the target site's terms and with data-protection law. Use it on public pages only; personal data needs a lawful basis.

### Limitations

- Only some DataDome-protected sites are supported today, and the supported list grows over time. For a form the Actor does not support, the row reports `failure_class` instead of data.
- Targets that run DataDome in observation mode need no credential; for those the row carries the page and `credentialNeeded` is `false`.
- The cookie is issued for the domain in the URL and does not transfer to a different site.
- The cookie is tied to the connection that received it, so it may not load pages when replayed from a different connection.
- `body` is truncated at 200,000 characters per row; `bodyLen` and `bodyTruncated` tell you when that happened.
- One row per URL, and at most 70 rows per run.

### Related solvers and scrapers

More developer and data tools from the same publisher:

- [akamai Session solver](https://apify.com/lentic_clockss/akamai-solver)
- [AWS WAF Scraper](https://apify.com/lentic_clockss/aws-waf-solver)
- [Cloudflare solver](https://apify.com/lentic_clockss/cf-clearance-solver)
- [Imperva Credential Scraper](https://apify.com/lentic_clockss/imperva-schema)
- [Stealth Web Scraper](https://apify.com/lentic_clockss/stealth-web-scraper)
- [Phone Number Validator](https://apify.com/lentic_clockss/phone-number-intelligence)
- [GitHub Scraper](https://apify.com/lentic_clockss/github-scraper)
- [Polymarket Scraper](https://apify.com/lentic_clockss/polymarket-scraper)

Browse all of our scrapers at [apify.com/lentic_clockss](https://apify.com/lentic_clockss).

### Support

Found a problem or need another field? Open an issue in the **Issues** tab of this Actor's page and include the run ID and the input you used.

# Actor input Schema

## `urls` (type: `array`):

Public page URLs behind DataDome. Each URL becomes one dataset row with the datadome cookie and the fetched page.

## `maxResults` (type: `integer`):

Most rows (one per URL) to deliver and charge for this run.

## `country` (type: `string`):

Optional two-letter country code for the outbound connection (e.g. US, DE).

## `proxyGroup` (type: `string`):

Optional outbound connection group name. Leave empty for the default.

## Actor input object example

```json
{
  "urls": [
    "https://www.example.com/"
  ],
  "maxResults": 3
}
```

# Actor output Schema

## `rows` (type: `string`):

One row per URL with the datadome cookie value, the cookieHeader, the credential descriptor and the page body.

## `runSummary` (type: `string`):

Run summary with row counts, the input as understood, and finish times.

## `inputEcho` (type: `string`):

Input values the run actually used after normalization.

## `errorSummary` (type: `string`):

Error information when the run fails.

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "urls": [
        "https://www.example.com/"
    ],
    "maxResults": 3
};

// Run the Actor and wait for it to finish
const run = await client.actor("lentic_clockss/datadome-solver").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = {
    "urls": ["https://www.example.com/"],
    "maxResults": 3,
}

# Run the Actor and wait for it to finish
run = client.actor("lentic_clockss/datadome-solver").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print(f"💾 Check your data here: https://console.apify.com/storage/datasets/{run.default_dataset_id}")
for item in client.dataset(run.default_dataset_id).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "urls": [
    "https://www.example.com/"
  ],
  "maxResults": 3
}' |
apify call lentic_clockss/datadome-solver --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,lentic_clockss/datadome-solver"
        }
    }
}
```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/jV9aN6YVmDQXx0KPM/builds/ErrK7XIThEsoCxkpH/openapi.json
