Back to blog
Engineering10 min read
Running Dozens of Antidetect Browsers in Parallel Without Losing Sync
We break down grouped vision analysis, status recovery, and concurrency controls that keep large browser batches moving as one system.
Apr 1, 2026The Challenge of Parallel Browser Automation
Running one browser is straightforward. Running 50 browsers doing the same thing at the same time is a different problem entirely. Pages load at different speeds, CAPTCHAs appear on some sessions but not others, and network conditions vary between profiles. Without coordination, a parallel batch quickly becomes 50 independent scripts that drift apart.
Ornold treats a batch of browsers as a single coordinated system. Every action — navigation, form filling, clicking, CAPTCHA solving — runs across all active sessions simultaneously, with built-in handling for divergence and failure.
How Parallel Execution Works in Ornold
When you tell an AI agent "open google.com in all browsers," Ornold doesn't loop through sessions one by one. It sends the navigation command to all active sessions in parallel via CDP (Chrome DevTools Protocol). The same applies to clicks, form fills, screenshots, and every other browser action.
// This navigates ALL active sessions simultaneously
await browser_parallel_navigate({ url: "https://target.example/signup" });
// This fills the email field in ALL sessions at once
// Each session can get a different value
await browser_parallel_fill({
ref: "email",
values: profiles.map(p => p.email)
});
The "parallel" prefix in tool names isn't just naming convention — it reflects the actual execution model. Commands fan out to all sessions, execute concurrently, and results are collected before the next step.
Grouped Vision Analysis
The hardest part of parallel automation is knowing what's on each screen. In a batch of 30 browsers, some might show the signup form, others might have a CAPTCHA, and a few might be stuck on a loading screen. You need to know the state of every session before deciding what to do next.
Ornold's grouped vision analysis solves this by taking screenshots from all sessions, analyzing them as a batch, and grouping sessions by their visual state:
// Analyze all sessions at once
const analysis = await browser_parallel_vision_analyze_grouped();
// Results are grouped by page state:
// group "signup_form": [session1, session2, ... session25]
// group "captcha": [session26, session27, session28]
// group "error_page": [session29]
// group "loading": [session30]
This is fundamentally different from checking each session individually. One API call gives you the full picture. The AI agent can then branch logic: solve CAPTCHAs for the sessions that need it, retry navigation for the ones that failed, and continue the flow for the ones that are ready.
Grouped analysis is especially valuable when pages diverge due to A/B tests, geo-targeting, or bot detection. Instead of assuming all sessions are in the same state, you see the actual reality of each one.
Bounded Concurrency
Running 50 browsers in true parallel sounds good until you hit resource limits. Each browser session consumes CPU, memory, and network bandwidth. Vision analysis calls have API rate limits. CAPTCHA solving services throttle concurrent requests.
Ornold uses bounded concurrency — it processes sessions in controlled batches rather than firing everything at once:
- Browser actions (navigate, click, fill) — Run across all sessions simultaneously. These are lightweight CDP commands that don't strain resources.
- Vision analysis — Processed in groups (default: 12 at a time). Screenshots are taken in parallel, but analysis calls are batched to stay within API limits.
- CAPTCHA solving — Requests go out in parallel, but the solving service naturally throttles based on capacity. Ornold handles the queuing.
- Screenshots — Captured simultaneously from all sessions. Image data is streamed, not buffered in memory.
// Control concurrency for vision analysis
await browser_parallel_vision_analyze_grouped({ maxConcurrency: 12 });
// For very large batches, you can lower concurrency
// to reduce memory pressure
await browser_parallel_vision_analyze_grouped({ maxConcurrency: 6 });
Status Tracking and Recovery
In any batch operation, some sessions will fail. A page might not load, a CAPTCHA might time out, or a form submission might return an error. The question is: how do you detect failures and recover without restarting the entire batch?
Ornold provides real-time status tracking for every active session:
// Check the state of all sessions
const status = await browser_status();
// Returns per-session info:
// { id: "session-1", url: "https://target.example/dashboard", state: "ready" }
// { id: "session-2", url: "https://target.example/signup", state: "ready" }
// { id: "session-3", url: "about:blank", state: "error" }
With status data, the AI agent can make recovery decisions:
- Sessions on the wrong page — Re-navigate to the target URL
- Sessions with CAPTCHAs — Run the CAPTCHA solver for just those sessions
- Sessions with errors — Take a screenshot for diagnosis, then retry or skip
- Sessions that are ahead — Wait for slower sessions to catch up before the next batch action
Keeping Sessions in Sync
The biggest challenge in parallel automation isn't starting 50 browsers — it's keeping them in sync as the workflow progresses. Different pages load at different speeds. Some sessions hit rate limits. Others get redirected.
Ornold uses wait-for-all semantics: after each parallel action, it waits until all sessions have completed (or timed out) before moving to the next step. This prevents fast sessions from racing ahead while slow ones fall behind.
// Navigate all sessions
await browser_parallel_navigate({ url: "https://target.example/signup" });
// Wait until all sessions show the signup form
await browser_parallel_wait_for({
text: "Create Account",
timeoutMs: 15000
});
// Only proceeds when ALL sessions are ready (or timed out)
// Now fill forms — all sessions are on the same page
await browser_parallel_fill({ ref: "email", values: emails });
The timeout in `wait_for` acts as a safety valve. If a session can't reach the expected state within the timeout, it's flagged rather than blocking the entire batch forever.
Per-Profile Data in Batch Operations
Parallel doesn't mean identical. Each browser profile typically needs unique data — different email addresses, names, passwords, or other form values. Ornold supports per-session values in batch commands:
// Each session gets its own email
await browser_parallel_fill({
ref: "email",
values: [
"user1@mail.com",
"user2@mail.com",
"user3@mail.com",
// ... one per active session
]
});
// Each session gets its own password
await browser_parallel_fill({
ref: "password",
values: profiles.map(p => p.password)
});
The values array maps 1:1 to active sessions in order. If you have 30 active sessions, you provide 30 values. This keeps the parallel execution model while allowing full per-profile customization.
A Complete Parallel Workflow
Here's a realistic end-to-end flow for registering accounts across 30 antidetect browser profiles:
// 1. Start sessions and navigate
await browser_list(); // See available profiles
await browser_parallel_navigate({ url: "https://target.example/signup" });
await browser_parallel_wait_for({ text: "Create Account", timeoutMs: 15000 });
// 2. Fill forms with per-profile data
await browser_parallel_fill({ ref: "email", values: emails });
await browser_parallel_fill({ ref: "password", values: passwords });
await browser_parallel_fill({ ref: "name", values: names });
// 3. Solve CAPTCHAs (detected and solved across all sessions)
await browser_solve_captcha();
// 4. Submit
await browser_parallel_click({ ref: "submit" });
// 5. Verify results
await browser_parallel_wait_for({ text: "Welcome", timeoutMs: 20000 });
const status = await browser_status();
// Check which sessions succeeded and which need recovery
Scaling Tips
- Start with 5-10 sessions to validate the workflow, then scale up. Debugging is much easier with a small batch.
- Use `browser_status()` after every major step. Don't assume all sessions are in the same state.
- Set reasonable timeouts. Too short and you'll get false failures. Too long and stuck sessions block the batch.
- For batches over 30, consider splitting into groups and running them sequentially. This reduces peak resource usage.
- Monitor your antidetect browser's resource usage. Each profile consumes RAM — 50 Chromium instances can easily use 16+ GB.
- Keep your proxy quality consistent. Mixed proxy quality leads to mixed page load times, which causes sync issues.
Common Pitfalls
- Assuming all sessions see the same page — They don't. A/B tests, geo-targeting, and bot detection create divergence. Always check with `browser_status()` or grouped analysis.
- No recovery logic — If you don't handle failures, one stuck session can block the entire batch. Build in retries and skip logic.
- Ignoring resource limits — 50 browsers + vision analysis + CAPTCHA solving can overwhelm a machine. Use bounded concurrency.
- Sequential thinking in a parallel system — Don't loop through sessions one by one. Use the `browser_parallel_*` tools to act on all sessions at once.
- Not validating results — A successful form submission doesn't mean the account was created. Always verify the final state.