reCAPTCHA Recognition: bypass using grid method

Our team regularly enhances the service. 2Captcha provides a service for automatic solving and bypassing reCAPTCHA. According to the CaptchaTheCat rating service 2Captcha is the best reCAPTCHA solver.

We are pleased to announce the latest updates in the Grid method for solving captcha using our captcha recognition service. In this article, we will look in details at how to use the updated API to bypass the reCAPTCHA. This is useful if the reCAPTCHA challenge can not be solved using token method.

The changes in the Grid method are made to provide a captcha-type targeted recognition approach with higher accuracy and reduced recognition time achieved by combining human power with computer vision.

What is reCAPTCHA v2?

reCAPTCHA v2 is a CAPTCHA protection service developed by Google to differentiate between humans and bots. It presents various challenges to users, such as identifying specific objects within a set of 9 images or simply clicking a checkbox labeled "I'm not a robot."
The goal of reCAPTCHA v2 is to ensure that the user interacting with the website is indeed a human and not an automated bot, thereby enhancing security and preventing spam or fraudulent activities.

What's new in the API?

In the article we will consider two types of reCAPTCHA and how to solve them using api and grid method

Now the Grid method accepts an additional parameter imgType that is used to target the request to a proper recognition pipeline according to the CAPTCHA type. For reCAPTCHA you must set it's value to recaptcha.

How to solve the reCAPTCHA using the Grid method?

We can divide the process into following steps:

  1. Clicking the checkbox
  2. Collecting the task data
  3. Interacting with the API
  4. Clicking the grid tiles

To click the checkbox and tiles you can use the corresponding methods of your favorite browser automation framework.

In the article we focus on the steps 2 and 3.

Collecting the data

reCAPTCHA grid challenge consists of:

  • task text
  • image with 3x3 or 4x4 grid applied

Task text

We can get the text from innerText property of the element returned by selector .rc-imageselect-desc-wrapper, and we also need to remove the new line symbols to get the whole text as a one line string

const comment = document.querySelector('.rc-imageselect-desc-wrapper').innerText.replaceAll('\n', ' ')

Image with 4x4 grid

This case is pretty simple, we just need to use the image from an element returned by selector img.rc-image-tile-44.

const img4x4 = document.querySelector('img.rc-image-tile-44')

Image with 3x3 grid

Initially all the tiles for 3x3 grid are loaded as a single image, but when you click on images, the tiles can be updated with a new small image, so we need to paste these updated images over the initial one.

To get the initial 3x3 image we can use the selector: img.rc-image-tile-33 and to get the updated tiles: img.rc-image-tile-11.

const initial3x3img = table3x3.querySelector('img.rc-image-tile-33')
const updatedTiles = document.querySelectorAll('img.rc-image-tile-11')

Use the Canvas API to place the images and tiles properly and to get the base64 encoded version of the images.

Below we provide a JavaScript function that can help you extract the data properly. You can inject this code into the captcha iframe after you click the captcha checkbox. Then, call the getCaptchaData function once the captcha task is visible. The function returns a Promise that is resolved with the Object containing the following properties:

Helper function call example:

  • type - the API task type: GridTask
  • body - the extracted image as base64 string
  • comment - the task text
try {
    let data = await getCaptchaData()
    console.log(JSON.stringify(data))
} catch (e) {
    console.error(e)
}
Helper function code
const getCaptchaData = () => {
    return new Promise((resolve, reject)=>{
        let canvas = document.createElement('canvas')
        let ctx = canvas.getContext('2d')
        let comment = document.querySelector('.rc-imageselect-desc-wrapper').innerText.replaceAll('\n', ' ')

        let img4x4 = document.querySelector('img.rc-image-tile-44')
        if (!img4x4) {
            let table3x3 = document.querySelector('table.rc-imageselect-table-33 > tbody')
            if (!table3x3) {
                reject('Can not find reCAPTCHA elements')
            }

            initial3x3img = table3x3.querySelector('img.rc-image-tile-33')

            canvas.width = initial3x3img.naturalWidth
            canvas.height = initial3x3img.naturalHeight
            ctx.drawImage(initial3x3img, 0, 0)

            let updatedTiles = document.querySelectorAll('img.rc-image-tile-11')

            if (updatedTiles.length > 0) {
                const pos = [
                    { x: 0, y: 0 }, { x: ctx.canvas.width / 3, y: 0 }, { x: ctx.canvas.width / 3 * 2, y: 0 },
                    { x: 0, y: ctx.canvas.height / 3 }, { x: ctx.canvas.width / 3, y: ctx.canvas.height / 3 }, { x: ctx.canvas.width / 3 * 2, y: ctx.canvas.height / 3 },
                    { x: 0, y: ctx.canvas.height / 3 * 2 }, { x: ctx.canvas.width / 3, y: ctx.canvas.height / 3 * 2 }, { x: ctx.canvas.width / 3 * 2, y: ctx.canvas.height / 3 * 2 }
                ]
                updatedTiles.forEach((t) => {
                    const ind = t.parentElement.parentElement.parentElement.tabIndex - 3
                    ctx.drawImage(t, pos[ind - 1].x, pos[ind - 1].y)
                })
            }
            resolve({
                rows: 3,
                columns: 3,
                type: 'GridTask',
                comment,
                body: canvas.toDataURL().replace(/^data:image\/?[A-z]*;base64,/, '')
            })
        } else {
            canvas.width = img4x4.clientWidth
            canvas.height = img4x4.clientHeight
            ctx.drawImage(img4x4, 0, 0)
            resolve({
                rows: 4,
                columns: 4,
                comment,
                body: canvas.toDataURL().replace(/^data:image\/?[A-z]*;base64,/, ''),
                type: 'GridTask'
            })
        }
    })
}

Result example

{
    "type": "GridTask",
    "body": "/9j/4AAQSkZJRgABAQEAYABgAAD/4QBoRXhpZg...",
    "imgType": "recaptcha",
    "rows": 3,
    "columns": 3,
    "comment": "Select all images that have cars in them"
}

Interacting with the API

Once we have all the required data, we can make a request to the API to solve our captcha task. Use the data extracted on previous step, add the imgType parameter set to recaptcha and indicate the number of columns and rows.

Method: POST
API endpoint: https://api.2captcha.com/createTask

Task parameters

Parameter Type Required Description
type String Yes The task type must be set to GridTask
imgType String Yes Must be set to recaptcha
body String Yes Image in base64 format with the task tiles glued together
comment String Yes The task text in English
rows Number Yes The number of rows, will be 3 or 4
columns Number Yes The number of columns, will be 3 or 4
imgInstructions String No Additional task image (when provided) in base64 format

Request body example

{
    "clientKey":"YOUR_API_KEY",
    "task": {
        "type": "GridTask",
        "imgType": "recaptcha",
        "body": "iVBORw0KGgoAAAA...",
        "comment": "Click on the images that best match the theme of the sample image.",
        "rows": 3, // 4
        "columns": 3, // 4
        "imgInstructions": "iVBORw0KGgoAAA..."
    }
}

Result example

{
    "errorId": 0,
    "status": "ready",
    "solution": {
        "click": [
            3,
            4,
            7
        ]
    },
    "cost": "0.0012",
    "ip": "1.2.3.4",
    "createTime": 1692863536,
    "endTime": 1692863556,
    "solveCount": 1
}

Use the click method of your browser automation framework to click on the corresponding tiles. The selector for tiles is 'div.task'. Please note that the array of nodes is numerated from 0 and in our API answer the tiles are numerated from 1 to 9. In our example we click in the tiles using pure JavaScript:

document.querySelectorAll('div.task')[3-1].click()
document.querySelectorAll('div.task')[4-1].click()
document.querySelectorAll('div.task')[7-1].click()

You can also use our libraries to quickly implement the method into your code.

Code examples for libraries

Ruby
result = client.grid({
  method: 'base64',
  key: 'your_api_key',
  recaptcha: 1,
  json: 1,
  recaptchacols: 3,
  recaptcharows: 3,
  img_type: 'recaptcha',
  textinstructions: 'lease click on all entities similar to the following silhouette',
  imginstructions: Base64.encode64(File.read('path/to/hint.jpg')),
  body: Base64.encode64(File.read('path/to/captcha.jpg')),
  previous_id: 0
})
Python
result = solver.grid( method='base64',
                     body = 'base64',
                     key='your_api_key',
                     recaptcha=1,
                     json=1,
                     recaptchacols=3,
                     recaptcharows=3,
                     img_type='recaptcha',
                     textinstructions='Please click on all entities similar to the following silhouette',
                     imginstructions=base64.b64encode(open('path/to/hint.jpg', 'rb').read()).decode('utf-8'),
                     previous_id=0)

Code example using browser automation