Give your AI agents, QA scripts, and integrations a real browser — with a REST API, built-in MCP server, and network interception. Bring your own AI: connect a local model, OpenAI, or Anthropic key.
# Create a tab and navigate curl -X POST http://localhost:3000/api/browser/tabs \ -H "Content-Type: application/json" \ -d '{"url": "https://example.com"}' # Click with LLM fallback — never break on selector changes curl -X POST http://localhost:3000/api/browser/tabs/0/click \ -d '{"selector": "button.submit", "llmFallback": "Click the submit button"}' # Get the page content curl http://localhost:3000/api/browser/tabs/0/source?type=text
Point Claude Desktop, OpenClaw, or any MCP-compatible agent at LumaBrowser's built-in MCP server. Navigate, click, and evaluate the DOM natively — no glue code required.
SPAs and randomized CSS classes break scripts daily. Add llmFallback to any selector-based command and LumaBrowser's on-board LLM resolves the right element automatically — keeping your scripts stable.
Use the CDP-based Network Watcher to intercept HTTP traffic without root certificates or proxy setups, and forward it directly to your backend.
Three lines to navigate, click, and extract — against any website.
# Create a tab and navigate to a page
curl -X POST http://localhost:3000/api/browser/tabs \
-H "Content-Type: application/json" \
-d '{"url": "https://news.ycombinator.com"}'
# Click with LLM fallback — if the selector breaks, AI finds the right element
curl -X POST http://localhost:3000/api/browser/tabs/0/click \
-H "Content-Type: application/json" \
-d '{"selector": ".titleline > a", "llmFallback": "Click the first story link"}'
# Get the page content as clean text
curl http://localhost:3000/api/browser/tabs/0/source?type=text
import requests
# Create a tab and navigate
requests.post("http://localhost:3000/api/browser/tabs", json={
"url": "https://news.ycombinator.com"
})
# Click with LLM fallback — resilient to DOM changes
requests.post("http://localhost:3000/api/browser/tabs/0/click", json={
"selector": ".titleline > a",
"llmFallback": "Click the first story link"
})
# Fill a form — each field has its own LLM fallback
requests.post("http://localhost:3000/api/browser/tabs/0/fill", json={
"fields": [
{"selector": "input[name=q]", "value": "LumaBrowser",
"llmFallback": "The search input"}
]
})
// Create a tab and navigate
await fetch("http://localhost:3000/api/browser/tabs", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ url: "https://news.ycombinator.com" })
});
// Click with LLM fallback — AI resolves the selector if it breaks
await fetch("http://localhost:3000/api/browser/tabs/0/click", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
selector: "button.submit",
llmFallback: "Click the submit button"
})
});
// Wait for an element — LLM resolves if selector is stale
await fetch("http://localhost:3000/api/browser/tabs/0/wait", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
selector: ".success-msg",
llmFallback: "The success confirmation message"
})
});
Real request/response flows showing what LumaBrowser handles for you.
{ "url": "https://acme.com/signup" }{ "id": 0, "url": "https://acme.com/signup", "title": "Sign Up — Acme" }{
"fields": [
{ "selector": "#full_name", "value": "Jane Doe",
"llmFallback": "The full name input field" }
],
"llmFallback": "Fill the signup form"
}#full_name not found — site renamed it to data-field-xk92llmFallback is set → triggering AI resolution...
{
"pageInfo": { "selectorStability": "unstable", "jsRendered": true },
"elements": [
{ "Name": "Full Name Input", "CssSelector": "input[data-field-xk92]",
"FallbackSelector": "form .field-group:first-child input",
"Type": "Input" },
{ "Name": "Email Input", "CssSelector": "input[data-field-ml47]",
"Type": "Input" },
{ "Name": "Submit Button", "CssSelector": "button.btn-register",
"Type": "Button" }
]
}input[data-field-xk92]Prompt: "Action: fill | Description: The full name input field
Page template elements:
- "Full Name Input" (Input): input[data-field-xk92]
- "Email Input" (Input): input[data-field-ml47]
..."
→ input[data-field-xk92]{
"success": true,
"message": "Form filled successfully (resolved by LLM fallback)",
"data": {
"filled": 1,
"resolvedSelector": "input[data-field-xk92]"
}
}Compare: ?type=full vs ?type=clean vs ?type=text
<!DOCTYPE html> <html lang="en" data-theme="dark" data-build="a8f3c"> <head> <meta charset="UTF-8"> <meta name="viewport" content="..."> <title>Dashboard — Acme Corp</title> <link rel="stylesheet" href="/assets/main.c7x92k.css"> <link rel="stylesheet" href="/assets/vendor.d83hf.css"> <script src="/assets/runtime.k29d.js" defer></script> <script src="/assets/app.m48fk.js" defer></script> <style>.css-1a2b3c{display:flex} .css-4d5e6f{margin:0 auto;max-width:1200px} .css-7g8h9i{font-family:Inter,sans-serif} /* ... 847 more lines of CSS ... */</style> <script>window.__NEXT_DATA__={props:{pageProps: {session:{user:{id:"usr_382",name:"Jane"}, token:"eyJhbG..."}}},page:"/dashboard"} </script> </head> <body> <div id="__next"> <div class="css-1a2b3c"> <nav class="css-k3m8x2"> <img src="data:image/svg+xml;base64,PHN2..." alt="" class="css-p9q2r1" /> <a href="/" class="css-m4n5o6">Dashboard</a> <a href="/settings" class="css-m4n5o6">Settings</a> </nav> <main class="css-4d5e6f"> <h1 class="css-7g8h9i">Welcome back, Jane</h1> <div class="css-a1b2c3"> <div class="css-d4e5f6">Revenue</div> <span class="css-g7h8i9">$12,450</span> </div> <!-- ... 200+ more nodes ... -->
<nav>
<a href="/">Dashboard</a>
<a href="/settings">Settings</a>
</nav>
<main>
<h1>Welcome back, Jane</h1>
<div class="metric-card">
<div>Revenue</div>
<span>$12,450</span>
</div>
<div class="metric-card">
<div>Active Users</div>
<span>1,284</span>
</div>
<table class="data-table">
<thead>
<tr><th>Product</th><th>Sales</th><th>Revenue</th></tr>
</thead>
<tbody>
<tr><td>Widget Pro</td><td>342</td><td>$8,550</td></tr>
<tr><td>Gadget X</td><td>156</td><td>$3,900</td></tr>
</tbody>
</table>
<a href="/reports">View full report →</a>
</main>
Dashboard | Settings
Welcome back, Jane
Revenue: $12,450
Active Users: 1,284
Product Sales Revenue
Widget Pro 342 $8,550
Gadget X 156 $3,900
View full report →
{
"prompt": "Log into acme.com/login with [email protected] / demo123,
go to settings, change company name to 'Acme Industries'",
"autoCloseTab": false,
"includeScreenshot": true
}1. navigate → acme.com/login (template attached) 2. fill_form → email + password 3. click → Login button (url changed → /dashboard) 4. wait_for → .dashboard-content ✓ 5. navigate → /settings (template generated) 6. fill_form → company name = "Acme Industries" 7. click → Save Changes ✓
{
"success": true,
"summary": "Logged in and updated company name to Acme Industries",
"finalResponse": "Done! I logged into acme.com, navigated to...",
"tabId": 0,
"iterations": 4,
"durationMs": 18420,
"toolCalls": [
{ "tool": "navigate", "durationMs": 1200 },
{ "tool": "fill_form", "durationMs": 340 },
{ "tool": "click", "durationMs": 890 },
{ "tool": "wait_for", "durationMs": 1100 },
{ "tool": "navigate", "durationMs": 1450 },
{ "tool": "fill_form", "durationMs": 280 },
{ "tool": "click", "durationMs": 760 }
],
"screenshot": "data:image/png;base64,iVBOR..."
}What the AI Chat agent actually does behind the scenes — with Template Builder enabled for faster targeting.
{ "prompt": "Log into acme.com/login with [email protected] / demo123, go to settings, change company name to 'Acme Industries'." }https://acme.com/logininput#email, input#password, button.login-btninput#email = "[email protected]", input#password = "demo123" — selectors from template, instant matchbutton.login-btn — "Log In" button from template.dashboard-content to appear → visible in 1.2s/settings — template auto-generated (no cache), 8 elements extractedinput#company-name, button.save-settings, etc.input#company-name = "Acme Industries" — from freshly generated templatebutton.save-settings — "Save Changes".toast-success → "Settings saved successfully" appears in 0.8sA built-in LLM assistant that can see and control the browser. Ask it to navigate pages, fill forms, extract data, or run multi-step automations — conversationally. It has full access to every browser tool and streams responses in real time.
Here are the top 3 stories on Hacker News right now:
Monitor HTTP traffic at the browser level using Chrome DevTools Protocol. Define URL patterns, and LumaBrowser captures matching requests and forwards the full request/response to your webhook — no proxy, no root certs.
The first time you visit a page, the LLM analyzes the full DOM and generates a cached selector map. Every subsequent visit uses the cached template — slashing token usage by 95%+ and making automation near-instant.
{
"elements": [
{ "name": "Login Button",
"primary": "button[data-testid='login']",
"fallbacks": ["button.auth-cta", "#login-btn"] },
{ "name": "Email Field",
"primary": "input[name='email']",
"fallbacks": ["input[type='email']"] }
]
}
Schedule recurring LLM-driven automations. Each execution runs a full agentic loop with browser tools — navigate, click, extract, and report back via webhook. Set it and forget it.
Captures browser Notification API calls from any page and forwards them to your webhook. Monitor alerts, messages, or status changes from any web app — without polling.
{
"source": "app.example.com",
"title": "New message from Sarah",
"body": "Hey, the deploy finished!",
"timestamp": "2026-03-29T14:22:00Z",
"url": "https://app.example.com/chat"
}
Run language models directly on your GPU — no API keys, no cloud, no cost. LumaBrowser ships with WebGPU-accelerated inference using Qwen 2.5 models. Fully private, fully local.
Every major feature — Network Watcher, Template Builder, AI Chat, Timed Tasks, even local WebGPU inference — is a modular extension. Disable what you don't need, or build your own with the extension API: custom REST routes, MCP tools, and UI panels, all hot-loaded at startup.
A lightweight JavaScript data grid with virtual scrolling, sortable columns, context menus, column reordering, and CSV export. Fast and dependency-free.
A lightweight reactive UI library for building dynamic interfaces with minimal overhead. Simple API, no build step, no virtual DOM.
The textbook price comparison engine. Find the best deals on textbooks across multiple retailers in one search.