How to solve and bypass captchas in the browser with Tampermonkey
If you’re tired of getting blocked by captchas while testing forms, scraping pages, or automating dumb flows — this post is for you.
You don’t need a headless browser. You don’t need Puppeteer. You don’t even need a backend. Just Tampermonkey and a captcha solver API.
This is pure JavaScript that runs in your browser. Works on any site that throws reCAPTCHA v2 or hCaptcha at you. Zero dependencies.
I’m using 2Captcha in the examples because it’s stable and has decent docs, but if you want something cheaper, solvecaptcha.com is plug-and-play compatible and usually costs less per solve. Just swap the endpoint and field names — everything else stays the same.
What you’ll need
- Tampermonkey extension (Chrome/Firefox/Edge — doesn’t matter)
- A 2Captcha or SolveCaptcha API key
- Basic understanding of JavaScript and the DOM
Step 1: Install Tampermonkey
- Go to tampermonkey.net
- Install the extension
- Click “Create new script”
- Paste one of the scripts below (depending on the captcha type)
reCAPTCHA v2 solver example
How to know it's reCAPTCHA? You'll usually see this in the HTML:
<div class="g-recaptcha" data-sitekey="..."></div>
Here’s the full userscript that grabs the sitekey, sends it to the API, waits for the solution, injects it, and auto-submits the form.
reCAPTCHA userscript
// ==UserScript==
// @name reCAPTCHA v2 Solver
// @namespace https://thecat.says/
// @version 1.0
// @description Solves reCAPTCHA v2 using 2Captcha-compatible API
// @match *://*/*
// @grant none
// ==/UserScript==
(async function () {
const API_KEY = 'YOUR_API_KEY_HERE';
const sleep = ms => new Promise(r => setTimeout(r, ms));
const el = document.querySelector('.g-recaptcha[data-sitekey]');
if (!el) return;
const sitekey = el.getAttribute('data-sitekey');
const url = location.href;
const createTask = async () => {
const res = await fetch('https://api.2captcha.com/createTask', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
clientKey: API_KEY,
task: {
type: 'RecaptchaV2TaskProxyless',
websiteURL: url,
websiteKey: sitekey,
isInvisible: false
}
})
});
const json = await res.json();
if (json.errorId !== 0) throw new Error(json.errorDescription);
return json.taskId;
};
const getToken = async (taskId) => {
for (let i = 0; i < 24; i++) {
await sleep(5000);
const res = await fetch('https://api.2captcha.com/getTaskResult', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ clientKey: API_KEY, taskId })
});
const json = await res.json();
if (json.status === 'ready') return json.solution.gRecaptchaResponse;
}
throw new Error('captcha timeout');
};
const token = await getToken(await createTask());
let textarea = document.querySelector('[name="g-recaptcha-response"]');
if (!textarea) {
textarea = document.createElement('textarea');
textarea.name = 'g-recaptcha-response';
textarea.style.display = 'none';
document.body.appendChild(textarea);
}
textarea.value = token;
const form = el.closest('form');
if (form) form.submit();
})();
hCaptcha solver example
You’ll know it’s hCaptcha if you see:
<div class="h-captcha" data-sitekey="..."></div>
Same deal. Just use HCaptchaTaskProxyless in the payload.
hCaptcha userscript
// ==UserScript==
// @name hCaptcha Solver
// @namespace https://thecat.says/
// @version 1.0
// @description Solves hCaptcha using 2Captcha-compatible API
// @match *://*/*
// @grant none
// ==/UserScript==
(async function () {
const API_KEY = 'YOUR_API_KEY_HERE';
const sleep = ms => new Promise(r => setTimeout(r, ms));
const el = document.querySelector('.h-captcha[data-sitekey]');
if (!el) return;
const sitekey = el.getAttribute('data-sitekey');
const url = location.href;
const createTask = async () => {
const res = await fetch('https://api.2captcha.com/createTask', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
clientKey: API_KEY,
task: {
type: 'HCaptchaTaskProxyless',
websiteURL: url,
websiteKey: sitekey
}
})
});
const json = await res.json();
if (json.errorId !== 0) throw new Error(json.errorDescription);
return json.taskId;
};
const getToken = async (taskId) => {
for (let i = 0; i < 24; i++) {
await sleep(5000);
const res = await fetch('https://api.2captcha.com/getTaskResult', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ clientKey: API_KEY, taskId })
});
const json = await res.json();
if (json.status === 'ready') return json.solution.gRecaptchaResponse;
}
throw new Error('captcha timeout');
};
const token = await getToken(await createTask());
let textarea = document.querySelector('[name="h-captcha-response"]');
if (!textarea) {
textarea = document.createElement('textarea');
textarea.name = 'h-captcha-response';
textarea.style.display = 'none';
document.body.appendChild(textarea);
}
textarea.value = token;
const form = el.closest('form');
if (form) form.submit();
})();
Debug tips
- captcha not showing up? wait for DOM or use a MutationObserver
- form not submitting? some sites need a synthetic click
- stuck at “not ready”? check your key or try SolveCaptcha if 2Captcha is overloaded
- use console.log() to trace what’s happening if nothing works
Done
Tampermonkey + recognition API = full browser-side challenges bypass without needing to spin up anything else.