Log In with BQL and Browser Automation
Automate a login flow (navigating to the login page, filling credentials, solving CAPTCHAs, and handling post-login redirects) using BQL or a Puppeteer/Playwright connection.
- A Browserless API token from your account dashboard
Steps
- REST API
- Frameworks
- BQL
Use the REST API to automate a login flow without a persistent browser connection.
- cURL
- JavaScript
- Python
1. Build the BQL mutation
Chain navigation and interaction steps. The agent resolves selectors dynamically from a snapshot of the page:
https://production-sfo.browserless.io/chromium/bql?token=YOUR_API_TOKEN_HERE
2. Send the request
curl -X POST \
"https://production-sfo.browserless.io/chromium/bql?token=YOUR_API_TOKEN_HERE" \
-H "Content-Type: application/json" \
-d '{
"query": "mutation AgentLogin { goto(url: \"https://app.example.com/login\") { status } waitForSelector(selector: \"input[type=email], input[name=email], #email\") { selector } typeEmail: type(selector: \"input[type=email], input[name=email], #email\", text: \"user@example.com\") { time } typePass: type(selector: \"input[type=password]\", text: \"YOUR_PASSWORD\") { time } solve { found solved } submit: click(selector: \"button[type=submit]\") { time } waitForNavigation { status } }"
}'
3. Check the output
{
"data": {
"goto": { "status": 200 },
"waitForSelector": { "selector": "input[name=email]" },
"typeEmail": { "time": 134 },
"typePass": { "time": 98 },
"solve": { "found": false, "solved": false },
"submit": { "time": 76 },
"waitForNavigation": { "status": 200 }
}
}
1. Send the request
const response = await fetch(
'https://production-sfo.browserless.io/chromium/bql?token=YOUR_API_TOKEN_HERE',
{
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
query: `mutation AgentLogin {
goto(url: "https://app.example.com/login") {
status
}
waitForSelector(selector: "input[type=email], input[name=email], #email") {
selector
}
typeEmail: type(
selector: "input[type=email], input[name=email], #email"
text: "user@example.com"
) {
time
}
typePass: type(selector: "input[type=password]", text: "YOUR_PASSWORD") {
time
}
solve {
found
solved
}
submit: click(selector: "button[type=submit]") {
time
}
waitForNavigation {
status
}
}`,
operationName: 'AgentLogin',
}),
}
);
const result = await response.json();
console.log('Login complete:', result.data);
2. Check the output
Run with node agent-login.mjs. The mutation chain handles any CAPTCHA and waits for the post-login navigation.
1. Install dependencies
pip install requests
2. Send the request
import requests
query = """
mutation AgentLogin {
goto(url: "https://app.example.com/login") {
status
}
waitForSelector(selector: "input[type=email], input[name=email], #email") {
selector
}
typeEmail: type(
selector: "input[type=email], input[name=email], #email"
text: "user@example.com"
) {
time
}
typePass: type(selector: "input[type=password]", text: "YOUR_PASSWORD") {
time
}
solve {
found
solved
}
submit: click(selector: "button[type=submit]") {
time
}
waitForNavigation {
status
}
}
"""
response = requests.post(
'https://production-sfo.browserless.io/chromium/bql?token=YOUR_API_TOKEN_HERE',
json={'query': query, 'operationName': 'AgentLogin'},
)
print(response.json()['data'])
3. Check the output
Run with python agent-login.py. The mutation chain completes the login flow autonomously.
Use a browser connection to fill in credentials, solve CAPTCHAs, and submit the login form.
- Puppeteer
- Playwright
1. Install dependencies
npm install puppeteer-core
2. Connect and log in
Puppeteer gives you full control for adaptive login flows — try multiple selectors, handle unexpected prompts, and recover from errors:
import puppeteer from 'puppeteer-core';
const browser = await puppeteer.connect({
browserWSEndpoint:
'wss://production-sfo.browserless.io?token=YOUR_API_TOKEN_HERE&solveCaptchas=true',
});
try {
const page = await browser.newPage();
await page.goto('https://app.example.com/login', { waitUntil: 'networkidle2' });
// Try multiple selectors — login pages don't use a consistent attribute for the email field.
const emailSelectors = ['input[type="email"]', 'input[name="email"]', '#email'];
for (const sel of emailSelectors) {
if (await page.$(sel)) {
await page.type(sel, 'user@example.com');
break;
}
}
await page.type('input[type="password"]', process.env.PASSWORD);
await page.click('button[type="submit"]');
await page.waitForNavigation({ waitUntil: 'networkidle2' });
console.log('Logged in. Current URL:', page.url());
} finally {
// Always close to release the session even on error.
await browser.close();
}
3. Check the output
Run with node agent-login.mjs. The solveCaptchas=true parameter enables automatic CAPTCHA solving if one appears during login.
1. Install dependencies
npm install playwright-core
2. Connect and log in
import { chromium } from 'playwright-core';
const browser = await chromium.connectOverCDP(
'wss://production-sfo.browserless.io?token=YOUR_API_TOKEN_HERE&solveCaptchas=true'
);
try {
// Use the default context — browser.newPage() creates a new context that
// doesn't inherit proxy, profile, or launch settings.
const context = browser.contexts()[0];
const page = await context.newPage();
await page.goto('https://app.example.com/login', { waitUntil: 'networkidle' });
const emailSelectors = ['input[type="email"]', 'input[name="email"]', '#email'];
for (const sel of emailSelectors) {
if (await page.locator(sel).count() > 0) {
await page.fill(sel, 'user@example.com');
break;
}
}
await page.fill('input[type="password"]', process.env.PASSWORD);
await page.click('button[type="submit"]');
await page.waitForLoadState('networkidle');
console.log('Logged in. Current URL:', page.url());
} finally {
// Always close to release the session even on error.
await browser.close();
}
3. Check the output
Run with node agent-login.mjs. The solveCaptchas=true parameter enables automatic CAPTCHA solving during the login flow.
1. Write the mutation
Chain navigation, field detection, typing, CAPTCHA solving, and submission in a single request:
mutation AgentLogin {
goto(url: "https://app.example.com/login") {
status
}
waitForSelector(selector: "input[type=email], input[name=email], #email") {
selector
}
typeEmail: type(
selector: "input[type=email], input[name=email], #email"
text: "user@example.com"
) {
time
}
typePass: type(selector: "input[type=password]", text: "YOUR_PASSWORD") {
time
}
solve {
found
solved
}
submit: click(selector: "button[type=submit]") {
time
}
waitForNavigation {
status
}
}
2. Run it
Paste into the BQL IDE and click Run, or send via HTTP (see the cURL tab).
3. Check the output
{
"data": {
"goto": { "status": 200 },
"waitForSelector": { "selector": "input[name=email]" },
"typeEmail": { "time": 134 },
"typePass": { "time": 98 },
"solve": { "found": false, "solved": false },
"submit": { "time": 76 },
"waitForNavigation": { "status": 200 }
}
}
Next steps
- Save Logins to Authenticated Profiles — persist the logged-in state for reuse across sessions
- Log In via Email OTP — handle login flows that require a one-time code sent by email
- Solving reCAPTCHAs — automatically solve CAPTCHA challenges during login