Submit a solved token in a Playwright JSON request
Intercept the application's known JSON POST and replace its CAPTCHA field with solution.token. Filling a textarea won't help if the application builds its request from JavaScript state.
The example below targets an owned application's same-origin submission endpoint. It preserves the other JSON fields and request headers rather than rebuilding the request.
On this page
Match the example to your application
- 1.
Prepare the solver
Use Node.js 22+, Playwright and the official JavaScript SDK. Follow the SDK installation instructions; use the documented package download rather than assuming npm registry availability. Keep
SOLVETHREE_API_KEYin the Node.js process, not in browser code. - 2.
Configure the site and proxy
Set
TARGET_URLto your owned test page andSOLVETHREE_PROXYto its HTTP or HTTPS proxy URL. The example requests actionloginfor the page's hostname. That hostname needs an active workspace profile; configure the site profile first. Change the action if your application uses a different one. - 3.
Identify the exact submission
Set
TARGET_SUBMIT_URLto the JSON POST endpoint, including its path. It must share the page's origin. The example expects an object containingrecaptchaTokenand a button whose accessible name is exactlySubmit. Change these application-specific details together.
For a test fixture, use a page that sends a JSON object to that endpoint and returns an inspectable response. Include another field and a CSRF field so you can check that neither changes. An echo-only local fixture can demonstrate the JSON rewrite, but it doesn't verify CAPTCHA acceptance. The supplied example still performs a real solve and needs a configured site reachable through the proxy.
Solve, intercept and submit
import { chromium } from 'playwright';
import { Solvethree } from '@solvethree/sdk';
const client = new Solvethree({ apiKey: process.env.SOLVETHREE_API_KEY });
const target = new URL(process.env.TARGET_URL);
const proxy = new URL(process.env.SOLVETHREE_PROXY);
const result = await client.solve({ site: target.hostname, action: 'login', proxy: process.env.SOLVETHREE_PROXY });
const proxyConfig = { server: `${proxy.protocol}//${proxy.host}`, username: decodeURIComponent(proxy.username), password: decodeURIComponent(proxy.password) };
const submit = new URL(process.env.TARGET_SUBMIT_URL);
if (submit.origin !== target.origin) throw new Error('Use this demo with a same-origin submission URL');
const browser = await chromium.launch();
try {
const context = await browser.newContext({ proxy: proxyConfig, userAgent: result.solution.userAgent,
extraHTTPHeaders: { 'sec-ch-ua': result.solution.secChUa } });
const page = await context.newPage();
await page.route(submit.href, async route => {
if (route.request().method() !== 'POST') return route.continue();
const body = route.request().postDataJSON();
if (!body || typeof body !== 'object' || Array.isArray(body)) throw new Error('Expected the known JSON object');
await route.continue({ postData: JSON.stringify({ ...body, recaptchaToken: result.solution.token }),
headers: { ...route.request().headers(), 'content-type': 'application/json' } });
});
await page.goto(target.href);
const outcome = page.waitForResponse(r => r.url() === submit.href && r.request().method() === 'POST');
await page.getByRole('button', { name: 'Submit', exact: true }).click();
console.log('Application HTTP status:', (await outcome).status());
// recaptchaToken, the Submit button and this endpoint belong to this demo app.
// Adapt them to the real application, retaining its session/CSRF data.
await context.close();
} finally { await browser.close(); }client.solve creates the task and polls until it is ready or throws. The example then creates a browser context using solution.userAgent, solution.secChUa and the same single proxy supplied to the solve. The SDK doesn't apply the solution to the browser; the route handler does that.
The browser context is new. If submission requires authentication or other session setup, perform it in this context before submitting. The example doesn't import an existing browser session.
Replace the token, not the whole request
The route is registered before navigation and scoped to the configured submission URL. Non-POST requests continue unchanged. For a POST, the handler reads the existing JSON object, copies its fields and replaces only recaptchaToken.
That retains body fields such as form values and CSRF data. The handler also copies the request headers and sets the content type to JSON. Keep the application's existing session and CSRF behavior; a solved token doesn't replace either.
Don't broaden the route to every request or replace whichever field happens to resemble a token. Inspect the owned application's outgoing request and use its actual field name. This example doesn't discover widgets, invoke arbitrary callbacks or support arbitrary third-party pages. Playwright's network documentation covers the interception mechanism.
Check the application response
The response wait starts before the click and matches the submission URL and POST method. The logged status belongs to the application's response—not the solvethree task. Inspect that response's body or the application's resulting state using its own success contract. Neither a ready task nor an HTTP success status alone proves the application accepted the token.
- The response wait never completes.
Check whether the exact Submit button triggers a POST to the configured URL.
Try this: Correct the button locator, endpoint or method to match the application. Don't widen interception to unrelated traffic.
- The route cannot read a JSON object.
Inspect the request body and content type.
Try this: Use this handler only for the known JSON-object endpoint. Form data, arrays and other payload formats need different handling.
- The task is ready, but the application rejects submission.
Inspect the outgoing JSON for
solution.token, then check session/CSRF data, action, returned browser identity and proxy.Try this: Correct the mismatched request before paying for another solve. Keep the browser on the solve's proxy and use the returned identity.
- The solve fails before the browser opens.
Distinguish an HTTP request error from a terminal task failure. For a failed task, inspect
errorCodeanderror.Try this: Fix the reported input, profile or connectivity problem rather than retrying unchanged inputs. See API error handling.
Sources and API reference
Run your first task.
Start with $5 in workspace credit.