CAPTCHA with Images (ComplexImage)
and CapMonster Cloud

Captcha solving, website integration, and testing.

Pricing of ComplexImage solution

CAPTCHA
Price (USD)
$ 0.30
1000 answers
Inherited a site with a captcha or another protection layer but no access to the source code? In that case you naturally ask: which solution is installed, is it configured correctly, and how can the workflow be tested?

In this article, we have tried to answer all the key questions. The first step in solving the task is to determine which protection system is being used. To do this, you can refer to the list of popular captchas and anti-bot protection systems, where you will find visual examples and key indicators that help you quickly understand what you are dealing with.

If you discover that your site uses ComplexImage, the next step is to study its properties and operation in more detail. In this article, you can also review the instructions on how to integrate ComplexImage so that you fully understand how it functions on your site. This will help you not only understand the current protection, but also properly plan its maintenance.

What is an image CAPTCHA
What is an image CAPTCHA
An image CAPTCHA is a type of visual CAPTCHA where the user needs to perform a task related to image analysis. Typically, the system asks the user to select images that meet a certain condition, count objects, identify the correct position of an item, or perform another visual action. Unlike text-based CAPTCHAs, where users only enter characters from an image, image CAPTCHAs test the ability to interpret visual information and perform logical actions. This makes them harder to automatically recognize and more effective at protecting registration, login, and other critical forms from bots.

How to solve image CAPTCHA using CapMonster Cloud

When testing forms with Tencent CAPTCHA, it is often necessary to check the CAPTCHA functionality and ensure it is correctly integrated.
You can manually test the CAPTCHA implemented on your website:
  • Open the page with the form and make sure the CAPTCHA is displayed.
  • Try submitting the form without solving the CAPTCHA — the server should return an error.
  • After successfully solving the CAPTCHA, the form should submit without errors.
For automatic CAPTCHA solving, you can use specialized services such as CapMonster Cloud — a tool that receives CAPTCHA parameters, processes them on its servers, and returns a ready solution — e.g., numbers or click coordinates that can be used to pass verification without user involvement.

Working with CapMonster Cloud via API typically involves the following steps:

Creating a taskCreating a task
arrow
Sending an API requestSending an API request
arrow
Receiving the resultReceiving the result
arrow
Placing the token on the pagePlacing the token on the page
arrow
Image CAPTCHA recognition using ready-made libraries
CapMonster Cloud provides ready-made libraries for easy usage in Python, JavaScript (Node.js), and C#.
Python
JavaScript
C#
Insert solution and submit form
Node.js example for a complete cycle of CAPTCHA recognition on your webpage. Possible approaches: use HTTP requests to retrieve HTML and protection parameters, send the solution, and process the result. Or, as in the example, use automation tools (like Playwright) — open the page, wait for the check, submit parameters via CapMonster Cloud client, receive the result, insert it into the proper field (for testing you can use correct or incorrect data) and observe the outcome.
python

// npm install playwright @zennolab_com/capmonstercloud-client
// npx playwright install chromium

import { chromium } from 'playwright';
import { CapMonsterCloudClientFactory, ClientOptions, ComplexImageTaskRecognitionRequest } from '@zennolab_com/capmonstercloud-client';

const API_KEY = "YOUR_API_KEY";
const TARGET_URL = "https://example.com/captcha-page";

async function solveComplexImageTaskPlaywright() {
    const browser = await chromium.launch({ headless: false });
    const context = await browser.newContext();
    const page = await context.newPage();
    await page.goto(TARGET_URL);

    // Find the CAPTCHA image
    const captchaHandle = await page.$('#captcha'); // replace with the real selector
    const captchaBase64 = await captchaHandle.evaluate(img => {
        const canvas = document.createElement('canvas');
        canvas.width = img.width;
        canvas.height = img.height;
        const ctx = canvas.getContext('2d');
        ctx.drawImage(img, 0, 0);
        return canvas.toDataURL('image/png').split(',')[1];
    });

    console.log("Captcha base64:", captchaBase64.substring(0, 50) + "...");

    const cmcClient = CapMonsterCloudClientFactory.Create(
        new ClientOptions({ clientKey: API_KEY })
    );

    // Send CAPTCHA for recognition
    const citRecognitionRequest = new ComplexImageTaskRecognitionRequest({
        imagesBase64: [captchaBase64],
        metaData: { Task: 'oocl_rotate' } // replace with your CAPTCHA type
    });

    const result = await cmcClient.Solve(citRecognitionRequest);
    console.log("Solution received:", result);

    // Process the solution
    const solution = result.solution;

    if (!solution) {
        console.error("No solution received");
        return;
    }

    if (solution.metadata?.AnswerType === "Coordinate") {
        // CAPTCHA with coordinates
        const box = await captchaHandle.boundingBox();
        for (const point of solution.answer) {
            const clickX = box.x + point.X;
            const clickY = box.y + point.Y;
            console.log(`Clicking at: (${clickX}, ${clickY})`);
            await page.mouse.click(clickX, clickY);
        }
    } else if (solution.metadata?.AnswerType === "Grid") {
        // Grid CAPTCHA (true/false array)
        const box = await captchaHandle.boundingBox();
        const gridItems = await page.$$('#captcha_grid div'); // replace with grid element selectors
        const answers = solution.answer;

        for (let i = 0; i < answers.length; i++) {
            if (answers[i] && gridItems[i]) {
                const itemBox = await gridItems[i].boundingBox();
                const clickX = itemBox.x + itemBox.width / 2;
                const clickY = itemBox.y + itemBox.height / 2;
                console.log(`Clicking grid item ${i} at: (${clickX}, ${clickY})`);
                await page.mouse.click(clickX, clickY);
            }
        }
    } else {
        console.warn("Unknown captcha solution type:", solution.metadata?.AnswerType);
    }

    // Click confirmation button (if any)
    await page.click('#submit_button'); // replace with the real button selector

    console.log("Captcha solved.");
}

solveComplexImageTaskPlaywright().catch(console.error);
  
How to integrate image CAPTCHA into your website
To understand how CAPTCHA works on your site, check its logic, or reconfigure it, we recommend this section. It describes the general process of protection integration — this helps quickly grasp all the nuances.

1. Generate CAPTCHA on the server.

  • A CAPTCHA image is created: single image or image grid.
  • Noise, distortions, and random elements are added to prevent bots.
  • A unique captchaId is generated and the correct solution is stored (in memory, database, or cache).

2. Send CAPTCHA to client

  • Server sends captchaId and image (Base64 or URL) to client.
  • Client displays the CAPTCHA, e.g., with <img> or grid <div> with instructions.
Example client part (HTML + JS)Example client part (HTML + JS)
arrow

3. User enters the answer

  • User enters text, clicks on images, or rotates objects.
  • Client formats the answer in the proper structure:
    • Text CAPTCHA → string
    • Grid → true/false array
    • Coordinates → array of {X, Y}

4. Submit answer to server:

  • Client sends POST request with captchaId and answer.
  • Optionally, session token can be sent to prevent repeated solving.

5. Server validation

  • Server looks up CAPTCHA session by captchaId.
  • Compares user answer with correct solution.
  • Returns success: true/false.
  • If failed, CAPTCHA can be refreshed.
Server-side exampleServer-side example
arrow

6. Next steps

  • Successful CAPTCHA → protected process (form, registration, etc.) is allowed.
  • Failed CAPTCHA → issue a new one, optionally limit attempts.

Additionally

  • Use TTL (time-to-live) for CAPTCHA sessions to expire automatically.
  • Cache images and use temporary URLs to save resources.
  • Ensure correct click and touch behavior on mobile devices.
  • Log and analyze to improve UX and bot protection.
Background
Possible errors and debugging
Bug Icon
Image CAPTCHA does not load
(Empty grid, broken images, 404/500 errors, base64 issues) — check that the server correctly generates images, the base64 data is not corrupted, the image format is supported by the browser, and the client receives a valid captchaId.
Bug Icon
User clicks are not processed
(Cells are not highlighted or the response is empty) — make sure click coordinates or indices are sent to the server, the data is properly serialized, and touch events are supported on mobile devices.
Bug Icon
Captcha fails despite correct clicks
Verify that the captcha is not regenerated before validation, the correct answer is stored separately per session, the response format matches the expected one, and the captcha TTL has not expired.
Bug Icon
CAPTCHA expired
If the user takes too long, increase TTL, refresh CAPTCHA on retry, and notify user to reload images.
Protection resilience checks
After integration, make sure the system really protects the site from automated actions.
Security and optimization tips
Store the correct CAPTCHA answer <span class="font-bold">only on the server</span> (memory, Redis, or database), never on the client.
Use one-time CAPTCHA IDs (<span class="font-bold">captchaId</span>).
Limit the number of attempts per CAPTCHA.
Always use <span class="font-bold">HTTPS</span> for transmitting images and clicks.
Cache images and use <span class="font-bold">temporary URLs</span> if possible.
Log generation and verification errors (time, IP/fingerprint, reason for failure).
Regularly update CAPTCHA mechanics (image sets, grid size/structure, task types).
Conclusion

If you encounter a site with an already installed CAPTCHA or another protection system and have no access to the code — don’t worry! It is easy to identify which technology is used. To verify functionality, you can use CapMonster Cloud in an isolated test environment to ensure token processing and logic work correctly.

For image CAPTCHAs — it’s enough to identify the system, study its behavior, and make sure protection works. This article showed how to recognize ComplexImage image CAPTCHA and how to integrate or reconfigure it to reliably maintain protection and control its operation.

Conclusion
Helpful links
DocIconCapMonster Cloud Documentation (ComplexImage)DocIconGuide to creating your own CAPTCHADocIconRegister with CapMonster Cloud service

Frequently Asked Questions about ComplexImage

To send a single- or multi-image CAPTCHA to CapMonster Cloud, follow these steps:

  1. Send a POST request to https://api.capmonster.cloud/createTask.
  2. In the request body, pass your clientKey and a task object with:
    • type: ComplexImageTask
    • class: recognition
    • imagesBase64: An array of Base64-encoded images. An image from the single-image CAPTCHA must also be passed in the array.
    • metadata.Task: The recognition module name, for example dli or oocl_rotate_new
    • metadata.TaskArgument: The modules bls_3x3 and portugal_text_find_icon require additional information here
    • metadata.PayloadType: The type of data sent in the task. Set the Audio value for the audio captcha.
  3. Save the taskId returned in the response.
  4. Send requests to https://api.capmonster.cloud/getTaskResult with that taskId.
  5. Keep polling until status changes to ready.
  6. When the task is ready, read the result from the solution object. Use AnswerType to understand how to apply the answer.

More details: Complex Image / Click CAPTCHA docs

The metadata.Task field tells CapMonster Cloud which specific recognition module to apply to the submitted image — for instance, dli for a standard classification task or oocl_rotate_new for rotation-based challenges. Choosing the wrong module may produce an incorrect solution type even if the image is valid.

The full list of available module identifiers, please see here.

The solution.metadata.AnswerType field value in the getTaskResult response tells you exactly how to interpret the answer:

  • Text — the answer is a plain string you can insert into a form field
  • Coordinate — returns an array of {X, Y} objects. You use each pair to trigger a mouse click at the matching position on the page, offset by the image's bounding box.
  • Grid — delivers a true/false array aligned to the CAPTCHA's image cells, so you iterate through it and click only the cells where the value is true.
  • NumericArray — returns an array of numbers indicating the image rotation angle for corresponding CAPTCHA types.

The task.imagesBase64 parameter expects an array of strings, where each string is a standard Base64-encoded representation of the image file.

  • Pass the array inside the task object alongside the other required fields — type, class, and metadata.Task.
  • Strip any data URI prefix (such as data:image/png;base64,) before including the encoded string, and wrap even a single image in an array rather than passing it as a bare string.

See the task creation examples and how to get Base64 in the documentation.

If the expected answer is sent to the client, automated scripts can read it and bypass the challenge entirely. Always store the correct answer server-side (for example, in Redis or a database) and only return a pass/fail result to the frontend.

To secure the process further, enforce a strict Time-To-Live (e.g., 300 seconds) and cap verification attempts per session.