A Selenium alternative for production AI-agent access to third-party portals: why a maintained API beats driving a real browser via WebDriver at scale.

If you're weighing a Selenium alternative for automation that logs into third-party portals — a property management system, a payer portal, a carrier dashboard — start by being precise about what you're comparing. Selenium drives a real browser through WebDriver: it navigates pages, finds elements by selector, waits for the DOM to settle, and clicks. Supergood works one layer down: it generates and maintains a REST API (and MCP tools) for the same portal by capturing the network calls the portal already makes. Both are legitimate. This piece is about which layer belongs in production when an AI agent has to hit those portals at volume — and why, for that case, driving a browser is usually the wrong substrate.
Selenium is the standard for cross-browser test automation and has an enormous install base, and it earned that place honestly — for QA of an application you own, it’s hard to beat. It’s the wrong tool for running AI agents against third-party portals at volume, because a rendering-layer approach carries a browser tax on every call: selector drift and WebDriver/browser-version churn make it brittle, the maintenance is yours to carry, and the infrastructure is expensive at scale. Supergood works at the network layer, so calls are milliseconds instead of seconds, auth/MFA/CAPTCHA are handled, and a frontend redesign changes nothing.
| Selenium | Supergood | |
|---|---|---|
| Latency per call | Seconds — browser boot, page render, DOM settle, explicit waits, plus LLM planning round-trips | Milliseconds — a single network request, no browser |
| Cost per call at scale | ~$0.12–0.24/session floor; ~$1–1.50 for a real multi-step completed workflow | One backend request — no per-session compute |
| Maintenance model | You own the selectors, the waits, and the driver/browser-version matrix; you fix them when they break | Maintained for you; a maintenance agent detects backend API changes and ships fixes, usually before you notice |
| MFA / CAPTCHA handling | You script it (OTP/TOTP wait loops, third-party CAPTCHA solvers) | Handled automatically — a real service-account email + phone per agent |
| Breaks on UI redesign? | Yes — new markup means new selectors | No — tools don't touch the frontend |
| Observability | You build it (logs, screenshots, retries, Grid dashboards) | Built in — full audit logs (request, response, timing, status), structured errors, auto-updated docs |
| Best for | Cross-browser E2E/QA of your own app, legacy test suites, Grid infrastructure you already run, one-off scripts | Production, high-volume agent access to third-party portals with no public API |
A button has pixels, and pixels look the same whether the click did the right thing or did nothing. That's the maintenance trap with browser automation against sites you don't own: your selectors encode assumptions about someone else's markup, and that markup changes on their schedule, not yours. When a portal ships a redesign, your find_element(By.CSS_SELECTOR, ...) calls stop matching, and you find out from a failed run — or worse, from a wrong result.
Selenium adds a second maintenance axis that its newer peers softened: the driver and browser-version matrix. For years, a Chrome auto-update could break a suite overnight because the pinned ChromeDriver no longer matched; Selenium Manager has taken most of that pain away, but the coupling between browser version, driver, and your automation is still a surface you own and monitor. Add flaky waits to the pile. WebDriverWait with expected_conditions is the correct pattern, and it's still a heuristic about when a page is "ready" — set the timeout too short and you get intermittent failures, too long and every run drags. None of these are Selenium bugs. They're the cost of pretending a computer is a human looking at a screen.
The failure mode is subtle enough to be dangerous. The most rigorous public benchmark for computer-using agents reports agent success of 67–85% on simple UI interactions but only 9–19% on complex multi-step workflows, and documents "trajectory degradation": the agent looks productive while quietly dropping constraints and confirmation steps, and eventually submits the wrong record at step 14 — the worst failure mode precisely because the system reports success (arXiv:2511.17131). A screen-clicking agent can be confidently, silently wrong, and a selector-based flow gives you no clean signal to catch it.
Supergood started as an observability company monitoring third-party APIs, and the founding observation carries the whole argument: network calls are deterministically observable and maintainable; buttons on a screen are not. A network call has a method, a URL, a body, a status code, and a payload. You can replay it, diff it, monitor it, and alert on any byte. Because the generated tools work at the network layer, frontend redesigns don't break anything — the tools never touched the frontend, so there are no selectors to drift and no driver matrix to keep in sync. When a portal changes its backend API, production telemetry catches it and a maintenance agent ships a fix, usually before the customer notices. If you've survived an RPA rollout, you'll recognize the failure mode Selenium suites hit at scale — RPA rebranded to browser automation. For the general pattern of giving agents a stable interface to software that never exposed one, see MCP for software without a public API and What is an unofficial API.
Maintenance shows up as headcount; infrastructure shows up on the invoice, and it’s the cleanest line to draw. An integration you maintain by hand is a headcount line item; one that’s maintained for you isn’t. On top of that, a hosted browser session costs roughly $0.12–0.24 at the cheapest. A real multi-step workflow — login, navigate, clear MFA, fill a form, plus the LLM round-trips to drive it — lands closer to $1–1.50 per completed call. A network call to the same backend skips all of that overhead. For a typical customer running ~1M calls/month, those per-call differences stop being rounding error and start being budget.
The latency profile compounds the cost. Research on computer-using agents found that tasks a human finishes in about 2 minutes routinely take agents 20+ minutes, and across 16 top agents even the best used 1.4–2.7x more steps than needed; 75–94% of total task time is burned on LLM planning and reflection rather than the work itself (arXiv:2506.16042). Every one of those extra steps and minutes is a browser session staying alive and tokens being spent. That's a fine profile for an offline batch job and a punishing one for anything interactive. We wrote up the general case for skipping the browser entirely in The Antibrowser, and the broader shift toward agents that act rather than click in What is agentic AI.
Assumptions — cheapest hosted browser session $0.15; realistic multi-step completed workflow $1.00/call.
| Monthly volume | Browser (floor, $0.15/session) | Browser (realistic, $1.00/call) |
|---|---|---|
| 10,000 | $1,500 | $10,000 |
| 100,000 | $15,000 | $100,000 |
| 1,000,000 | $150,000 | $1,000,000 |
Annualized at 1M/month: browser floor ≈ $1.8M/yr; realistic ≈ $12M/yr. The floor figure assumes a perfect one-session-per-call world with no retries and no LLM tokens, which is not the world anyone actually operates in; the realistic column is the honest planning number. Note these are infrastructure costs only and exclude the engineering time to build and maintain the automation, which — with Selenium’s selector, wait, and driver surface — is where the browser approach hurts most.
Here's a realistic Selenium login-and-submit flow against a portal. It's clean Selenium — nothing wrong with the code — but note everything it has to own: the driver and browser lifecycle, the selectors, the explicit waits, the MFA loop, and (not shown) the retry and observability scaffolding around it.
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
import os
def submit_charge(tenant_id, amount):
driver = webdriver.Chrome() # driver/browser version must stay in sync
wait = WebDriverWait(driver, 30)
try:
# Login
driver.get("https://portal.example-yardi.com/login")
wait.until(EC.presence_of_element_located((By.CSS_SELECTOR, "#username")))
driver.find_element(By.CSS_SELECTOR, "#username").send_keys(os.environ["PORTAL_USER"])
driver.find_element(By.CSS_SELECTOR, "#password").send_keys(os.environ["PORTAL_PASS"])
driver.find_element(By.CSS_SELECTOR, 'button[type="submit"]').click()
# MFA: wait for OTP / TOTP — you own retrieving and entering the code
wait.until(EC.presence_of_element_located((By.CSS_SELECTOR, "#mfa-code")))
code = get_otp_from_inbox() # your integration
driver.find_element(By.CSS_SELECTOR, "#mfa-code").send_keys(code)
driver.find_element(By.CSS_SELECTOR, "#mfa-submit").click()
# Navigate to the charges screen (selectors break on redesign)
wait.until(EC.element_to_be_clickable((By.LINK_TEXT, "Charges"))).click()
wait.until(EC.element_to_be_clickable((By.CSS_SELECTOR, "#new-charge"))).click()
# Fill and submit the form
driver.find_element(By.CSS_SELECTOR, "#tenant-id").send_keys(tenant_id)
driver.find_element(By.CSS_SELECTOR, "#amount").send_keys(str(amount))
driver.find_element(By.CSS_SELECTOR, "#submit-charge").click()
# Confirm — but "success" pixels look the same as "did nothing"
wait.until(EC.visibility_of_element_located((By.CSS_SELECTOR, ".toast-success")))
finally:
driver.quit()The same action through Supergood is a REST call. Auth, MFA, and CAPTCHAs are handled server-side; there is no browser, no driver, no selector, and nothing to maintain.
# 1. Authenticate once — Supergood handles login, MFA, and CAPTCHAs
curl -X POST https://api.supergood.ai/v1/yardi/auth \
-H "Authorization: Bearer $SUPERGOOD_KEY" \
-d '{"account": "acme-co"}'
# → { "session": "sg_sess_9f2..." }
# 2. Call the backend action directly (milliseconds, no browser)
curl -X POST https://api.supergood.ai/v1/yardi/charges \
-H "Authorization: Bearer $SUPERGOOD_KEY" \
-d '{"session": "sg_sess_9f2...", "tenant_id": "t_123", "amount": 2150.00}'For agent frameworks, the same portal ships as an MCP server — npx supergood-mcp init — and works with Claude, OpenAI, LangChain, n8n, Zapier, or any REST client. Endpoints come with auto-generated OpenAPI specs; the full reference is in the docs, and the per-portal shape (Yardi, in this example) is documented at the Yardi API.
Selenium is free, open source, battle-tested, and the default for a reason — pick it without hesitation when:
Supergood is a different trade. It's a paid vendor, and standing up a new portal means a one-time walkthrough of the workflow so it can capture the underlying network calls. That overhead only pays off when you're running production integrations at volume — which is precisely the case where Selenium's per-call cost and per-redesign maintenance stop being acceptable. If you're migrating off a legacy Selenium RPA stack that was never meant to carry production agent traffic, that's the migration Supergood is built for. Otherwise, use the right layer for the job.
What's the best Selenium alternative for production automation? For production AI-agent access to third-party portals with no public API, the better substrate is a maintained API at the network layer rather than a faster browser. Supergood generates deployed REST endpoints and MCP tools from the portal's own network calls, so each action is a single request in milliseconds with no browser, no selectors, and no driver matrix. If you instead need cross-browser QA of your own application, the best Selenium alternative is another browser tool (Playwright or Cypress), not an API — the layers solve different problems.
How do I migrate from Selenium RPA to an API? You don't rewrite selectors — you drop the browser layer. With Supergood, someone walks a workflow through the portal once; Supergood captures the underlying network calls and generates a deployed REST endpoint (and MCP tool) for that action. Your agent then calls the endpoint instead of driving a browser. Auth, MFA, and CAPTCHAs move server-side, and the maintenance agent handles backend API changes going forward, so the selector-and-driver upkeep you were carrying disappears. See MCP for software without a public API for the pattern.
Why is Selenium so flaky at scale? Three compounding surfaces. Selectors encode assumptions about markup you don't own, so a portal redesign breaks them. Explicit waits (WebDriverWait + expected_conditions) are heuristics about when a page is "ready," and heuristics fail intermittently under load. And the browser/driver version matrix has to stay in sync — Selenium Manager helps, but the coupling is still real. At low volume these are noise; at 1M calls/month they're a constant maintenance stream. A network call has no rendered UI to drift, so it isn't flaky in the same way.
Does Selenium handle MFA and CAPTCHAs? It can, but you build it. MFA means scripting an OTP/TOTP wait loop and wiring up code retrieval from an inbox or authenticator; CAPTCHAs typically mean integrating a third-party solving service. Both are yours to maintain, and both add steps to every run. Supergood handles auth, MFA (a real service-account email and phone per agent), and CAPTCHAs automatically, server-side.
Is Supergood a drop-in replacement for Selenium? Only for the third-party-portal use case. If you're testing your own app's UI or doing cross-browser QA, keep Selenium — Supergood doesn't render pages and isn't trying to. If your goal is production agent access to a portal that has no public API, Supergood replaces the browser automation with a maintained REST/MCP interface, and the selector, wait, and driver maintenance goes away with it.
If you're running agents against portals at volume and Selenium's flakiness is showing up in your latency, your invoice, or your on-call rotation, look at the network layer instead. Start with the docs, or read The Antibrowser for the argument in full. See it at supergood.ai.