cdpfleet Docs Sign up free

Examples & benchmarks / #81 · 2026-11-04 · Benchmark · Medium · for web scraping, ai agents

Behaviour scoring: Camoufox humanize on and off vs plain Chrome clicks on a public bot-test form

A public behavioural bot-test page scores how a demo login form was filled in: clicks, keystrokes, timing. Camoufox with `humanize` off, on and slowed down, and headful Chrome with plain Playwright clicks, each filled it the same way three times, typing fast and typing paced. What the page flagged, what it measured, and whether Google's reCAPTCHA v3 demo moved after real cursor moves and a click. Dated 2026-11-04.

Camoufox Google Chrome

Run on production on 2026-11-04: ✓ Node.js ✓ Python ✓ Java ✓ C# ✓ Go

A benchmark: the results below are what this setup measured on the dates shown, on our own test runs. Sites, networks and browsers change, so rerun the code before you rely on the numbers.

The problem

Fingerprint checks look at what a browser is; behaviour checks look at what it does — whether the cursor travelled before a click, where the click landed, whether the events were trusted, how fast the keys came. Camoufox's humanize option moves the cursor along a curved path instead of jumping, and it is on by default. Does that change what a behaviour-scoring page concludes? Case 68 loaded Google's reCAPTCHA v3 demo with nothing clicked and every setup scored 0.9, so that demo alone can't answer it. This case fills in a public demo form built for exactly this test, with the same actions in every setup, and records the page's verdict, the flags it raised and what it measured. It is a dated measurement on public test pages, not an evasion guide.

What we used, and why

WhatWhy
Four setupsCamoufox (based on Firefox source; not affiliated with Mozilla) with humanize: false, humanize: true (the default: a curved cursor path of up to 1.5 s) and humanize: 3 (up to 3 s), all headless: true; and headful Google Chrome as the reference, driven by plain Playwright. Every launch uses record: false.
proxy: cdpfleet-resi-country-us-session-<id>One sticky residential exit for the whole run, shared by every setup, so a difference between rows is the browser and how it was driven, not the IP. The form page says it uses no IP reputation; the exit only keeps the network the same.
deviceandbrowserinfo.com's behavioural bot testA public demo login form (/are_you_a_bot_interactions) that records clicks, keystrokes and timing; on submit it posts them with a fingerprint to its own endpoint and shows "You are human" or "You are a bot" with the flags behind it. The page says repeated tests don't affect the result. Any email and password are fine; nothing is logged in to.
page.click() and keyboard.type()The same five actions in every setup: click the email field, type [email protected], click the password field, type an 11-character password, click Login. Fast typing sends keys with no delay, paced with 120 ms between keys. humanize changes how the cursor gets to each click target, not where the click lands or how keys are typed.
page.on("request")Reads the body the page posts to its own endpoint, so the table can show what the page itself measured: time from the first field interaction to submit, whether each click hit the exact centre of its target, untrusted events, its CDP mouse-coordinate check, the median gap between keys. Nothing is changed or blocked.
page.evaluate()Counts mousemove events on the form page, and reads the verdict the page prints.
Google's reCAPTCHA v3 demoIn each session: load the demo, wait for the score it asks for on load, move the cursor twice and click its "Try again" link, read the score of the reloaded page.

How it works

  1. Three rounds; in each, the four setups one after another — one session at a time, never in parallel.
  2. In each session, open the behavioural test form in a fresh context, fill it in with fast typing and submit; read the verdict (up to 30 s). Then the same in another fresh context with paced typing.
  3. In a third fresh context, load the reCAPTCHA v3 demo, move the cursor, click "Try again", and read the new score.
  4. A page load that fails on the network is tried once more; a verdict is never retried.

The code

The same program in five languages (also on GitHub, with the raw output). Set these environment variables first:

// npm install [email protected]
// env: CDPFLEET_API_KEY (one session at a time; public test pages only; nothing is solved or retried to get a result)
import { randomBytes } from 'node:crypto';
import { chromium, firefox } from 'playwright';

const KEY = process.env.CDPFLEET_API_KEY;
// One sticky residential exit for the whole run, shared by every setup: a difference between rows is the browser
// and how it was driven, not the IP.
const PROXY = `cdpfleet-resi-country-us-session-c81${randomBytes(3).toString('hex')}-lifetime-30`;
const REPEATS = 3;
// Camoufox's humanize option: false = the cursor jumps straight to the target, true (the default) = a curved path of
// up to 1.5 s, a number = a path of up to that many seconds. Chrome is the reference: plain Playwright clicks.
const SETUPS = [
  { key: 'cf_off', setup: 'Camoufox, humanize: false', engine: 'camoufox', type: firefox, body: { headless: true, humanize: false } },
  { key: 'cf_on', setup: 'Camoufox, humanize: true (default)', engine: 'camoufox', type: firefox, body: { headless: true, humanize: true } },
  { key: 'cf_slow', setup: 'Camoufox, humanize: 3', engine: 'camoufox', type: firefox, body: { headless: true, humanize: 3 } },
  { key: 'chrome', setup: 'Google Chrome, headful', engine: 'chrome', type: chromium, body: { headless: false } },
];
// The same actions in every cell; only the typing speed differs between the two form runs of a session.
const TYPING = { fast: 0, paced: 120 }; // ms between keys
const EMAIL = '[email protected]';
const PASSWORD = 'lab-81!pass'; // the demo form wants 8+ characters with a special character

// deviceandbrowserinfo.com's behavioural bot test: a demo login form that records clicks, keystrokes and timing
// and, on submit, posts them with a fingerprint to its own endpoint, which answers human or bot with its flags.
const FORM = 'https://deviceandbrowserinfo.com/are_you_a_bot_interactions';
// Google's public reCAPTCHA v3 demo prints its server's verify response (with the score); its "Try again" link reloads it.
const RECAPTCHA = 'https://recaptcha-demo.appspot.com/recaptcha-v3-request-scores.php';

// The same expressions run in every language.
// Count mousemove events on the page, so the table shows what humanize changed.
const COUNT_MOVES = `(() => { window.__lab81 = 0; document.addEventListener('mousemove', () => { window.__lab81++; }, true); return true; })()`;
// The verdict the page shows after submit: isBot and every detail flag that is true.
const VERDICT = `(() => {
  const e = document.getElementById('jsonResult');
  const t = e ? e.innerText : '';
  if (t.length < 10) return null;
  const r = JSON.parse(t);
  const d = r.details || {};
  return { is_bot: r.isBot === true, flags: Object.keys(d).filter((k) => d[k] === true), moves: window.__lab81 };
})()`;
// What the page itself measured about the interaction: the "interactions" part of the body it posted, which is
// embedded as a JSON string and passed to this function.
const MEASURED = `(body) => {
  const i = JSON.parse(body).interactions || {};
  const s = i.emailTimingStats || {};
  return {
    time_to_fill_ms: Math.round(i.timeToFill),
    exact_center_clicks: [i.hasClickedEmailFieldExactCenter, i.hasClickedPasswordFieldExactCenter, i.hasClickedSubmitExactCenter].filter(Boolean).length,
    untrusted_event: i.hasUntrustedEvent === true,
    cdp_mouse_leak: i.cdpMouseLeak === true,
    key_gap_median_ms: typeof s.medianTimeBetweenKeys === 'number' ? Math.round(s.medianTimeBetweenKeys) : null,
  };
}`;
const SCORE = `(() => { if (window.__lab81rc) return null; const m = /"score"\\s*:\\s*([0-9.]+)/.exec(document.body ? document.body.innerText : ''); return m ? Number(m[1]) : null; })()`;

async function launch(s) {
  for (let attempt = 1; ; attempt++) {
    const res = await fetch(`https://starter.cdpfleet.com/${s.engine}/session`, {
      method: 'POST', headers: { 'x-api-key': KEY, 'content-type': 'application/json' },
      body: JSON.stringify({ proxy: PROXY, record: false, ...s.body }),
    });
    if (res.ok) return s.type.connect((await res.json()).wsUrl, { headers: { 'x-api-key': KEY } });
    if (![429, 503].includes(res.status) || attempt === 5) throw new Error(`launch ${res.status} ${await res.text()}`);
    await new Promise((r) => setTimeout(r, (Number(res.headers.get('retry-after')) || 5) * 1000));
  }
}
const sleep = (ms) => new Promise((r) => setTimeout(r, ms));

// A load that fails on the network (proxy timeout) is tried once more; the page's answer is never retried.
async function open(tab, url) {
  for (let attempt = 1; attempt <= 2; attempt++) {
    try {
      await tab.goto(url, { waitUntil: 'domcontentloaded', timeout: 30000 });
      return true;
    } catch { /* network failure */ }
  }
  return false;
}

// One form run: click the email field, type, click the password field, type, click Login; read the verdict.
async function form(browser, typing) {
  const out = { typing, verdict: 'error', flags: [], moves: null, action_seconds: null, measured: null };
  const context = await browser.newContext(); // a fresh context: every run is a first visit
  try {
    const tab = await context.newPage();
    let posted = null;
    tab.on('request', (req) => { if (req.url().includes('/fingerprint_bot_test')) posted = req.postData(); });
    if (!(await open(tab, FORM))) return out;
    await sleep(1000);
    await tab.evaluate(COUNT_MOVES);
    const t0 = Date.now();
    await tab.click('#email');
    await tab.keyboard.type(EMAIL, { delay: TYPING[typing] });
    await tab.click('#password');
    await tab.keyboard.type(PASSWORD, { delay: TYPING[typing] });
    await tab.click('#loginForm button[type=submit]');
    out.action_seconds = Math.round((Date.now() - t0) / 100) / 10;
    let v = null;
    for (let i = 0; i < 60 && v === null; i++) { await sleep(500); v = await tab.evaluate(VERDICT).catch(() => null); }
    if (v === null) { out.verdict = 'no verdict'; return out; }
    Object.assign(out, { verdict: v.is_bot ? 'bot' : 'human', flags: v.flags, moves: v.moves });
    if (posted) out.measured = await tab.evaluate(`(${MEASURED})(${JSON.stringify(posted)})`);
    return out;
  } catch {
    return out;
  } finally {
    await context.close().catch(() => {});
  }
}

// Google's v3 demo: wait for the score it asks for on load, move the cursor and click "Try again", read the new score.
async function recaptcha(browser) {
  const out = { before: null, after: null };
  const context = await browser.newContext();
  try {
    const tab = await context.newPage();
    if (!(await open(tab, RECAPTCHA))) return out;
    for (let i = 0; i < 40 && out.before === null; i++) { await sleep(500); out.before = await tab.evaluate(SCORE).catch(() => null); }
    await tab.evaluate('window.__lab81rc = 1'); // marks this document, so the score is read from the reloaded one
    await tab.mouse.move(300, 300);
    await tab.mouse.move(600, 200);
    await tab.click('a[href="/recaptcha-v3-request-scores.php"]');
    for (let i = 0; i < 40 && out.after === null; i++) { await sleep(500); out.after = await tab.evaluate(SCORE).catch(() => null); }
    return out;
  } catch {
    return out;
  } finally {
    await context.close().catch(() => {});
  }
}

const runs = [];
const scores = [];
// Serial: one session at a time; the repeats are interleaved so a slow minute doesn't land on one setup.
for (let repeat = 1; repeat <= REPEATS; repeat++) {
  for (const s of SETUPS) {
    const base = { setup: s.setup, key: s.key, repeat };
    let browser;
    try {
      browser = await launch(s);
    } catch {
      for (const typing of Object.keys(TYPING)) runs.push({ ...base, typing, verdict: 'error', flags: [], moves: null, action_seconds: null, measured: null });
      scores.push({ ...base, before: null, after: null });
      continue;
    }
    for (const typing of Object.keys(TYPING)) runs.push({ ...base, ...(await form(browser, typing)) });
    scores.push({ ...base, ...(await recaptcha(browser)) });
    await browser.close().catch(() => {});
  }
}
console.log(JSON.stringify({ measured_at: new Date().toISOString().slice(0, 10), runs, recaptcha: scores }, null, 2));

What we got

SetupFast typing: verdictPaced typing: verdictTime to fill, median (fast · paced)mousemove events per form, medianClicks on the exact centrereCAPTCHA v3 demo after moves + clickMeasured
Camoufox, humanize: falsebot 1/3 (suspiciousClientSideBehavior 1, superHumanSpeed 1)human 3/31.9 s · 4.3 s318/180.9 · 0.9 · 0.92026-11-04
Camoufox, humanize: true (default)human 3/3human 3/33.2 s · 5.2 s7218/180.9 · 0.9 · 0.92026-11-04
Camoufox, humanize: 3human 3/3human 3/33.1 s · 5.0 s6718/180.9 · 0.9 · 0.92026-11-04
Google Chrome, headfulbot 3/3 (hasInconsistentTimingResolution 3, isAutomatedWithCDP 3, isAutomatedWithCDPInWebWorker 3, hasSuspiciousWeakSignals 3, suspiciousClientSideBehavior 3, superHumanSpeed 3)bot 3/3 (hasInconsistentTimingResolution 3, isAutomatedWithCDP 3, isAutomatedWithCDPInWebWorker 3, hasSuspiciousWeakSignals 3, suspiciousClientSideBehavior 3)192 ms · 4.1 s318/180.9 · none · 0.92026-11-04

From the Node.js run on 2026-11-04. IP addresses are replaced with placeholders (203.0.113.x); equal addresses stay equal. The other languages produced the same findings.

Raw output (Node.js)
{
  "measured_at": "2026-11-04",
  "runs": [
    {
      "setup": "Camoufox, humanize: false",
      "key": "cf_off",
      "repeat": 1,
      "typing": "fast",
      "verdict": "human",
      "flags": [],
      "moves": 3,
      "action_seconds": 2.7,
      "measured": {
        "time_to_fill_ms": 2302,
        "exact_center_clicks": 3,
        "untrusted_event": false,
        "cdp_mouse_leak": false,
        "key_gap_median_ms": 16
      }
    },
    {
      "setup": "Camoufox, humanize: false",
      "key": "cf_off",
      "repeat": 1,
      "typing": "paced",
      "verdict": "human",
      "flags": [],
      "moves": 3,
      "action_seconds": 4.5,
      "measured": {
        "time_to_fill_ms": 4254,
        "exact_center_clicks": 3,
        "untrusted_event": false,
        "cdp_mouse_leak": false,
        "key_gap_median_ms": 125
      }
    },
    {
      "setup": "Camoufox, humanize: true (default)",
      "key": "cf_on",
      "repeat": 1,
      "typing": "fast",
      "verdict": "human",
      "flags": [],
      "moves": 58,
      "action_seconds": 6.8,
      "measured": {
        "time_to_fill_ms": 4316,
        "exact_center_clicks": 3,
        "untrusted_event": false,
        "cdp_mouse_leak": false,
        "key_gap_median_ms": 9
      }
    },
    {
      "setup": "Camoufox, humanize: true (default)",
      "key": "cf_on",
      "repeat": 1,
      "typing": "paced",
      "verdict": "human",
      "flags": [],
      "moves": 84,
      "action_seconds": 6.4,
      "measured": {
        "time_to_fill_ms": 5261,
        "exact_center_clicks": 3,
        "untrusted_event": false,
        "cdp_mouse_leak": false,
        "key_gap_median_ms": 126
      }
    },
    {
      "setup": "Camoufox, humanize: 3",
      "key": "cf_slow",
      "repeat": 1,
      "typing": "fast",
      "verdict": "human",
      "flags": [],
      "moves": 78,
      "action_seconds": 2.6,
      "measured": {
        "time_to_fill_ms": 1335,
        "exact_center_clicks": 3,
        "untrusted_event": false,
        "cdp_mouse_leak": false,
        "key_gap_median_ms": 3
      }
    },
    {
      "setup": "Camoufox, humanize: 3",
      "key": "cf_slow",
      "repeat": 1,
      "typing": "paced",
      "verdict": "human",
      "flags": [],
      "moves": 49,
      "action_seconds": 5.5,
      "measured": {
        "time_to_fill_ms": 4391,
        "exact_center_clicks": 3,
        "untrusted_event": false,
        "cdp_mouse_leak": false,
        "key_gap_median_ms": 123
      }
    },
    {
      "setup": "Google Chrome, headful",
      "key": "chrome",
      "repeat": 1,
      "typing": "fast",
      "verdict": "bot",
      "flags": [
        "hasInconsistentTimingResolution",
        "isAutomatedWithCDP",
        "isAutomatedWithCDPInWebWorker",
        "hasSuspiciousWeakSignals",
        "suspiciousClientSideBehavior",
        "superHumanSpeed"
      ],
      "moves": 3,
      "action_seconds": 0.8,
      "measured": {
        "time_to_fill_ms": 350,
        "exact_center_clicks": 3,
        "untrusted_event": false,
        "cdp_mouse_leak": false,
        "key_gap_median_ms": 3
      }
    },
    {
      "setup": "Google Chrome, headful",
      "key": "chrome",
      "repeat": 1,
      "typing": "paced",
      "verdict": "bot",
      "flags": [
        "hasInconsistentTimingResolution",
        "isAutomatedWithCDP",
        "isAutomatedWithCDPInWebWorker",
        "hasSuspiciousWeakSignals",
        "suspiciousClientSideBehavior"
      ],
      "moves": 3,
      "action_seconds": 4.5,
      "measured": {
        "time_to_fill_ms": 4156,
        "exact_center_clicks": 3,
        "untrusted_event": false,
        "cdp_mouse_leak": false,
        "key_gap_median_ms": 125
      }
    },
    {
      "setup": "Camoufox, humanize: false",
      "key": "cf_off",
      "repeat": 2,
      "typing": "fast",
      "verdict": "human",
      "flags": [],
      "moves": 3,
      "action_seconds": 2.2,
      "measured": {
        "time_to_fill_ms": 1939,
        "exact_center_clicks": 3,
        "untrusted_event": false,
        "cdp_mouse_leak": false,
        "key_gap_median_ms": 4
      }
    },
    {
      "setup": "Camoufox, humanize: false",
      "key": "cf_off",
      "repeat": 2,
      "typing": "paced",
      "verdict": "human",
      "flags": [],
      "moves": 3,
      "action_seconds": 4.2,
      "measured": {
        "time_to_fill_ms": 4016,
        "exact_center_clicks": 3,
        "untrusted_event": false,
        "cdp_mouse_leak": false,
        "key_gap_median_ms": 128
      }
    },
    {
      "setup": "Camoufox, humanize: true (default)",
      "key": "cf_on",
      "repeat": 2,
      "typing": "fast",
      "verdict": "human",
      "flags": [],
      "moves": 75,
      "action_seconds": 5.9,
      "measured": {
        "time_to_fill_ms": 3225,
        "exact_center_clicks": 3,
        "untrusted_event": false,
        "cdp_mouse_leak": false,
        "key_gap_median_ms": 14
      }
    },
    {
      "setup": "Camoufox, humanize: true (default)",
      "key": "cf_on",
      "repeat": 2,
      "typing": "paced",
      "verdict": "human",
      "flags": [],
      "moves": 72,
      "action_seconds": 6.2,
      "measured": {
        "time_to_fill_ms": 5243,
        "exact_center_clicks": 3,
        "untrusted_event": false,
        "cdp_mouse_leak": false,
        "key_gap_median_ms": 127
      }
    },
    {
      "setup": "Camoufox, humanize: 3",
      "key": "cf_slow",
      "repeat": 2,
      "typing": "fast",
      "verdict": "human",
      "flags": [],
      "moves": 67,
      "action_seconds": 6.5,
      "measured": {
        "time_to_fill_ms": 4621,
        "exact_center_clicks": 3,
        "untrusted_event": false,
        "cdp_mouse_leak": false,
        "key_gap_median_ms": 32
      }
    },
    {
      "setup": "Camoufox, humanize: 3",
      "key": "cf_slow",
      "repeat": 2,
      "typing": "paced",
      "verdict": "human",
      "flags": [],
      "moves": 76,
      "action_seconds": 6.7,
      "measured": {
        "time_to_fill_ms": 5518,
        "exact_center_clicks": 3,
        "untrusted_event": false,
        "cdp_mouse_leak": false,
        "key_gap_median_ms": 129
      }
    },
    {
      "setup": "Google Chrome, headful",
      "key": "chrome",
      "repeat": 2,
      "typing": "fast",
      "verdict": "bot",
      "flags": [
        "hasInconsistentTimingResolution",
        "isAutomatedWithCDP",
        "isAutomatedWithCDPInWebWorker",
        "hasSuspiciousWeakSignals",
        "suspiciousClientSideBehavior",
        "superHumanSpeed"
      ],
      "moves": 3,
      "action_seconds": 0.6,
      "measured": {
        "time_to_fill_ms": 192,
        "exact_center_clicks": 3,
        "untrusted_event": false,
        "cdp_mouse_leak": false,
        "key_gap_median_ms": 3
      }
    },
    {
      "setup": "Google Chrome, headful",
      "key": "chrome",
      "repeat": 2,
      "typing": "paced",
      "verdict": "bot",
      "flags": [
        "hasInconsistentTimingResolution",
        "isAutomatedWithCDP",
        "isAutomatedWithCDPInWebWorker",
        "hasSuspiciousWeakSignals",
        "suspiciousClientSideBehavior"
      ],
      "moves": 3,
      "action_seconds": 4.4,
      "measured": {
        "time_to_fill_ms": 4061,
        "exact_center_clicks": 3,
        "untrusted_event": false,
        "cdp_mouse_leak": false,
        "key_gap_median_ms": 125
      }
    },
    {
      "setup": "Camoufox, humanize: false",
      "key": "cf_off",
      "repeat": 3,
      "typing": "fast",
      "verdict": "bot",
      "flags": [
        "suspiciousClientSideBehavior",
        "superHumanSpeed"
      ],
      "moves": 3,
      "action_seconds": 0.9,
      "measured": {
        "time_to_fill_ms": 508,
        "exact_center_clicks": 3,
        "untrusted_event": false,
        "cdp_mouse_leak": false,
        "key_gap_median_ms": 4
      }
    },
    {
      "setup": "Camoufox, humanize: false",
      "key": "cf_off",
      "repeat": 3,
      "typing": "paced",
      "verdict": "human",
      "flags": [],
      "moves": 3,
      "action_seconds": 4.6,
      "measured": {
        "time_to_fill_ms": 4305,
        "exact_center_clicks": 3,
        "untrusted_event": false,
        "cdp_mouse_leak": false,
        "key_gap_median_ms": 126
      }
    },
    {
      "setup": "Camoufox, humanize: true (default)",
      "key": "cf_on",
      "repeat": 3,
      "typing": "fast",
      "verdict": "human",
      "flags": [],
      "moves": 43,
      "action_seconds": 4.2,
      "measured": {
        "time_to_fill_ms": 3067,
        "exact_center_clicks": 3,
        "untrusted_event": false,
        "cdp_mouse_leak": false,
        "key_gap_median_ms": 14
      }
    },
    {
      "setup": "Camoufox, humanize: true (default)",
      "key": "cf_on",
      "repeat": 3,
      "typing": "paced",
      "verdict": "human",
      "flags": [],
      "moves": 75,
      "action_seconds": 6.1,
      "measured": {
        "time_to_fill_ms": 4937,
        "exact_center_clicks": 3,
        "untrusted_event": false,
        "cdp_mouse_leak": false,
        "key_gap_median_ms": 124
      }
    },
    {
      "setup": "Camoufox, humanize: 3",
      "key": "cf_slow",
      "repeat": 3,
      "typing": "fast",
      "verdict": "human",
      "flags": [],
      "moves": 69,
      "action_seconds": 4.4,
      "measured": {
        "time_to_fill_ms": 3123,
        "exact_center_clicks": 3,
        "untrusted_event": false,
        "cdp_mouse_leak": false,
        "key_gap_median_ms": 7
      }
    },
    {
      "setup": "Camoufox, humanize: 3",
      "key": "cf_slow",
      "repeat": 3,
      "typing": "paced",
      "verdict": "human",
      "flags": [],
      "moves": 65,
      "action_seconds": 6.2,
      "measured": {
        "time_to_fill_ms": 5014,
        "exact_center_clicks": 3,
        "untrusted_event": false,
        "cdp_mouse_leak": false,
        "key_gap_median_ms": 126
      }
    },
    {
      "setup": "Google Chrome, headful",
      "key": "chrome",
      "repeat": 3,
      "typing": "fast",
      "verdict": "bot",
      "flags": [
        "hasInconsistentTimingResolution",
        "isAutomatedWithCDP",
        "isAutomatedWithCDPInWebWorker",
        "hasSuspiciousWeakSignals",
        "suspiciousClientSideBehavior",
        "superHumanSpeed"
      ],
      "moves": 3,
      "action_seconds": 0.5,
      "measured": {
        "time_to_fill_ms": 171,
        "exact_center_clicks": 3,
        "untrusted_event": false,
        "cdp_mouse_leak": false,
        "key_gap_median_ms": 3
      }
    },
    {
      "setup": "Google Chrome, headful",
      "key": "chrome",
      "repeat": 3,
      "typing": "paced",
      "verdict": "bot",
      "flags": [
        "hasInconsistentTimingResolution",
        "isAutomatedWithCDP",
        "isAutomatedWithCDPInWebWorker",
        "hasSuspiciousWeakSignals",
        "suspiciousClientSideBehavior"
      ],
      "moves": 3,
      "action_seconds": 4.2,
      "measured": {
        "time_to_fill_ms": 3958,
        "exact_center_clicks": 3,
        "untrusted_event": false,
        "cdp_mouse_leak": false,
        "key_gap_median_ms": 125
      }
    }
  ],
  "recaptcha": [
    {
      "setup": "Camoufox, humanize: false",
      "key": "cf_off",
      "repeat": 1,
      "before": 0.9,
      "after": 0.9
    },
    {
      "setup": "Camoufox, humanize: true (default)",
      "key": "cf_on",
      "repeat": 1,
      "before": 0.9,
      "after": 0.9
    },
    {
      "setup": "Camoufox, humanize: 3",
      "key": "cf_slow",
      "repeat": 1,
      "before": 0.9,
      "after": 0.9
    },
    {
      "setup": "Google Chrome, headful",
      "key": "chrome",
      "repeat": 1,
      "before": 0.9,
      "after": 0.9
    },
    {
      "setup": "Camoufox, humanize: false",
      "key": "cf_off",
      "repeat": 2,
      "before": 0.9,
      "after": 0.9
    },
    {
      "setup": "Camoufox, humanize: true (default)",
      "key": "cf_on",
      "repeat": 2,
      "before": 0.9,
      "after": 0.9
    },
    {
      "setup": "Camoufox, humanize: 3",
      "key": "cf_slow",
      "repeat": 2,
      "before": 0.9,
      "after": 0.9
    },
    {
      "setup": "Google Chrome, headful",
      "key": "chrome",
      "repeat": 2,
      "before": null,
      "after": null
    },
    {
      "setup": "Camoufox, humanize: false",
      "key": "cf_off",
      "repeat": 3,
      "before": 0.9,
      "after": 0.9
    },
    {
      "setup": "Camoufox, humanize: true (default)",
      "key": "cf_on",
      "repeat": 3,
      "before": 0.9,
      "after": 0.9
    },
    {
      "setup": "Camoufox, humanize: 3",
      "key": "cf_slow",
      "repeat": 3,
      "before": 0.9,
      "after": 0.9
    },
    {
      "setup": "Google Chrome, headful",
      "key": "chrome",
      "repeat": 3,
      "before": 0.9,
      "after": 0.9
    }
  ]
}

Takeaways