captchaSolver object. For a saved Profile, runtime configuration takes precedence over Profile configuration, which takes precedence over Group configuration.paused, error, and unavailable as explicit outcomes. A returned answer or an applied state does not establish that the website accepted the action.Playwright controls the Nstbrowser browser context, while the built-in solver handles supported CAPTCHA processing and exposes status for your script to observe.
The connection uses the Chrome DevTools Protocol through Nstbrowserâs documented Playwright workflow. Nstbrowserâs client or Docker service provides a local endpoint on port 8848.
Playwrightâs CDP connection supports Chromium-based browsers and has lower fidelity than its native protocol. Choose a Chromium-based Nstbrowser environment for this example and evaluate the Playwright features your application needs.
The intended sequence is: launch or connect to the configured Profile, navigate to your authorized page, observe CAPTCHA processing if expected, perform the applicationâs next action, and confirm its actual result. No CAPTCHA token extraction or manual token injection is required by the status-observation pattern below.
You need an available Nstbrowser API credential, an existing stopped Profile, sufficient owning-team Credits, and an application you are authorized to test before running this integration.
A live authenticated run remains to be verified with your own test Profile. The example is a documented integration template for that validation.
Use a dedicated test Profile rather than a browser window someone is actively operating. Confirm that the Agent or Docker service is running, the plan includes CAPTCHA Solver, and the required browser kernel is available. A new runtime setting does not update an already running browser.
The sample application contract is deliberately narrow: the page displays its expected CAPTCHA on load, may require a submit action afterward, and exposes a distinct success element only after the server accepts the operation. Selectors in the environment table are values you supply for that application; they are not Nstbrowser API fields.
For action-triggered verification or multiple CAPTCHA steps, adapt the ordering and bind each observation to the current task. A terminal state left over from an earlier action must not be reused as confirmation of a later one.
Cloudflare recommends Turnstile test keys for automated application tests. Its production browser-support policy excludes automated challenge solving. Test-key outcomes do not benchmark Nstbrowserâs production solving capabilities.
Google also provides reCAPTCHA testing guidance, including v2 test keys and separate testing keys for v3. Keep test configuration separate from your production application.
Install an explicit Playwright version so that your example and project lockfile identify the dependency you are using.
The code in this draft targets the available Playwright 1.62.1 package and Node.js 24 runtime. Attach to the Nstbrowser-provided browser; a separate Playwright-managed browser download is not needed for this CDP example.
npm init -y
npm install --save-exact [email protected]
Supply these environment values through your usual local secret-management or process configuration:
| Variable | Purpose |
|---|---|
NSTBROWSER_API_KEY |
Your Nstbrowser API credential; keep it out of source and logs |
NSTBROWSER_PROFILE_ID |
The existing, stopped test Profile to launch |
STAGING_URL |
The HTTP(S) URL of your authorized test page |
CAPTCHA_EXPECTED |
1 for a page that creates a known CAPTCHA step; 0 to use only the normal application condition |
SUCCESS_SELECTOR |
An element that appears only after the application confirms success |
SUBMIT_SELECTOR |
Optional selector for the applicationâs post-verification submit action |
For example, a test application could expose [data-test="submission-accepted"] only after its server accepts a submission. That selector is an illustrative application contract, not an element guaranteed to exist on arbitrary websites.
Send enabled and all five type fields whenever you provide a non-empty runtime captchaSolver object.
The CAPTCHA configuration reference defines recaptchaV2, recaptchaV3, textCaptcha, awsCaptcha, and cloudflareTurnstile. At least one type must be enabled when the solver is enabled. The example enables reCAPTCHA v2, v3, and Turnstile, and explicitly disables the other two types.
For saved Profiles, omitting captchaSolver or sending an empty object inherits saved settings. A Once Browser has no Profile or Group inheritance and needs its own launch configuration. Start from a stopped environment when you change a runtime override.
The endpoint builder below uses URL.searchParams to encode the configuration once. Do not pre-encode the JSON and then encode it again. The connection URL contains your API credential, so keep the complete URL out of logs.
Credential prerequisite: the authenticated Nstbrowser handshake has not been run for this draft because an API credential and test Profile were unavailable. The supplied code was checked against the official schemas and locally checked for syntax and status-handling behavior. Validate the real connection and your applicationâs success condition before deployment.
Save this as run-profile.mjs:
import { chromium } from 'playwright';
import { pathToFileURL } from 'node:url';
export function buildEndpoint(profileId, apiKey) {
const endpoint = new URL(
`/api/v2/connect/${encodeURIComponent(profileId)}`,
'ws://127.0.0.1:8848',
);
endpoint.searchParams.set('x-api-key', apiKey);
endpoint.searchParams.set('config', JSON.stringify({
headless: false,
captchaSolver: {
enabled: true,
types: {
recaptchaV2: true,
recaptchaV3: true,
textCaptcha: false,
awsCaptcha: false,
cloudflareTurnstile: true,
},
},
}));
return endpoint.toString();
}
// This function executes in the page, so it has no outer dependencies.
export function terminalStatusInPage() {
const raw = document.documentElement.getAttribute('data-nst-captcha-status');
if (!raw) return false;
let value;
try { value = JSON.parse(raw); } catch { return false; }
if (!value || value.schemaVersion !== 1) {
throw new Error('Review the CAPTCHA status schema before continuing.');
}
if (['idle', 'solving', 'returned'].includes(value.state)) return false;
if (!['applied', 'solved', 'paused', 'error', 'unavailable'].includes(value.state)) {
throw new Error('Unexpected CAPTCHA state; stop and review.');
}
return { state: value.state, captchaType: value.captchaType ?? null };
}
export function redactError(error, apiKey) {
let message = String(error?.message ?? error);
for (const secret of [apiKey, apiKey ? encodeURIComponent(apiKey) : '']) {
if (secret) message = message.split(secret).join('[redacted]');
}
return message.replace(/wss?:\/\/[^\s"'<>]+/g, '[CDP endpoint]');
}
export async function main() {
const key = process.env.NSTBROWSER_API_KEY;
const profileId = process.env.NSTBROWSER_PROFILE_ID;
const target = process.env.STAGING_URL;
const successSelector = process.env.SUCCESS_SELECTOR;
const submitSelector = process.env.SUBMIT_SELECTOR;
const expected = process.env.CAPTCHA_EXPECTED;
if (!key || !profileId || !target || !successSelector || !['0', '1'].includes(expected)) {
throw new Error('Set all required environment variables before running.');
}
const targetUrl = new URL(target);
if (!['https:', 'http:'].includes(targetUrl.protocol)) {
throw new Error('Use an HTTP(S) staging application you are authorized to test.');
}
let browser;
let page;
try {
browser = await chromium.connectOverCDP(buildEndpoint(profileId, key), {
timeout: 30_000,
});
const context = browser.contexts()[0];
if (!context) throw new Error('No Profile browser context was returned.');
page = await context.newPage();
await page.goto(targetUrl.toString(), {
waitUntil: 'domcontentloaded', timeout: 30_000,
});
if (expected === '1') {
const handle = await page.waitForFunction(terminalStatusInPage, null, {
polling: 250, timeout: 120_000,
});
let status;
try { status = await handle.jsonValue(); } finally { await handle.dispose(); }
console.log('CAPTCHA stage:', status.state);
if (!['applied', 'solved'].includes(status.state)) {
throw new Error(`CAPTCHA requires attention: ${status.state}`);
}
}
if (submitSelector) await page.locator(submitSelector).click({ timeout: 30_000 });
await page.locator(successSelector).waitFor({ state: 'visible', timeout: 30_000 });
console.log('Application success condition observed.');
} finally {
if (page) await page.close().catch(() => {});
if (browser) await browser.close();
}
}
if (process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href) {
main().catch(error => {
console.error(redactError(error, process.env.NSTBROWSER_API_KEY));
process.exitCode = 1;
});
}
After supplying the environment values, run:
node run-profile.mjs
The 30_000 and 120_000 timeouts are example budgets chosen for this script. They are not Nstbrowser service limits or promised solve times. The script opens one page in the Profileâs existing context rather than creating an unrelated browser context.
Use the machine-readable page states documented by Nstbrowser for automation, rather than matching the pluginâs visual text.
The page-level CAPTCHA status reference describes the data-nst-captcha-status attribute on the top-level document and the nst:captcha-status event. The event detail is a JSON string. These interfaces expose status, not a solver token, account balance, or authentication credential.
| API state | Meaning for this example |
|---|---|
idle |
No current or recent task; do not treat it as completed verification |
solving |
Processing is underway |
returned |
A result returned, with application to the page not yet confirmed |
applied |
A result was applied; perform and check the websiteâs next action |
solved |
The native widget reported success; still confirm your business action |
paused |
Processing is paused, commonly because Credits are insufficient |
error |
Processing failed |
unavailable |
The service or runtime is unavailable |
The helper returns a bounded terminal outcome instead of waiting indefinitely for success. An absent or temporarily unreadable attribute keeps waiting until the exampleâs timeout; an unexpected schema or state stops the run for review.
This single-page example does not implement coordination across multiple active widgets, tabs, or concurrent account tasks. Add task-specific tracking before extending it to those workflows.
Wait for an application-level success condition after CAPTCHA processing and any required form submission.
applied confirms that a result reached the page. solved reports widget success. Your application still determines whether the login, form submission, or data request completed. Choose a success selector that corresponds to that confirmed outcome rather than a generic page element that was already visible.
The example closes its page and Playwright connection in finally. Confirm the Profileâs lifecycle separately in the Nstbrowser Dashboard and stop the test environment when finished. A local script exit should not be your only check that a managed environment has stopped.
Record connection success, the relevant state, and the application outcome separately; keep API keys, connection URLs, and private page content out of the record.
Stop on the first unresolved connection, configuration, CAPTCHA, or application failure before introducing a retry policy.
| Failure | Check |
|---|---|
| CDP connection fails | Local service, port, credential, Profile ID, Chromium environment, and installed package |
| Saved settings appear to override your intended types | Use a complete runtime object and start from a stopped Profile |
| Status remains absent | Feature availability, selected types, environment restart, and whether a CAPTCHA is actually expected |
paused |
The owning teamâs Credits and plan availability |
error or unavailable |
Record the state, pause the task, and use a bounded recovery or native verification route |
applied occurs, but the success selector never appears |
Submission ordering, selector correctness, and the websiteâs actual acceptance |
Successful solver returns are charged even when a later website action fails. Review current plan pricing before adding retries, and inspect the owning teamâs CAPTCHA usage in Billing. The example deliberately contains no automatic paid retry loop.
A verified connection, an observed CAPTCHA state, and a confirmed application result are the three separate checkpoints to establish before using this pattern in a wider job.
Start with one stopped Profile and a controlled page. Check the complete configuration, bounded failure handling, and final application condition. Once your team has verified those behaviors, adapt the workflow to additional authorized tasks while preserving their Profile and task boundaries.
The trial button opens Nstbrowser. Browser and CAPTCHA usage remain subject to the relevant plan and service charges.
Q: Does Playwright solve CAPTCHA by itself?
Playwright provides browser automation. In this integration, Nstbrowserâs built-in feature handles supported CAPTCHA processing, while Playwright observes the documented status and controls the authorized application workflow.
Q: Should I use chromium.launch or connectOverCDP?
Use chromium.connectOverCDP for this Nstbrowser Profile integration. A separate chromium.launch call starts a different browser and does not attach to the managed Profile described here.
Q: Can I use a Firefox Profile with this CDP example?
No. Playwrightâs CDP connection supports Chromium-based browsers. Select an appropriate Nstbrowser environment and evaluate any browser-specific application requirements separately.
Q: Why must all five CAPTCHA type fields be present?
Nstbrowser requires a complete set of type fields when a non-empty runtime solver object is provided. Explicit true or false values make the intended policy clear and avoid malformed-override fallback behavior.
Q: Why does the script time out on a page without CAPTCHA?
A page without CAPTCHA may never produce a CAPTCHA terminal state. Set CAPTCHA_EXPECTED to 0 for the normal content flow, or build application-specific branching rather than waiting for verification on every page.
Q: Can I treat applied or solved as a completed login?
No. Those states describe result application or widget progress. Confirm the websiteâs own accepted login or other business result after any required submission.
Q: How should I validate the example for my application?
Run it with your own stopped test Profile and a controlled page, then confirm the connection, current CAPTCHA state, and server-confirmed application outcome separately. A syntax check or a test-key success alone does not establish successful production solver integration.
Q: How do I keep retries from creating unexpected CAPTCHA costs?
Use explicit retry limits and review the failure cause before trying again. A failed-before-return task is uncharged, but successful solver returns from retries are billable even if the website later rejects them.