Local-first · Self-healing · No per-request fees

Web scraping that fixes itself when sites change.

Build a scraper. Two weeks later the target ships a redesign and .product-price becomes .css-8xk2m9. Most stacks crash. LumaBrowser is a free local browser with an LLM selector fallback and CDP network interception — so when CSS changes, your scraper keeps running and you keep billing for builds, not maintenance.

Download LumaBrowser — free See the two strategies
Works with Python, Node, Go, curl · runs locally · no API key required

The pattern every freelance scraper knows. You ship a scraper Tuesday. It runs perfectly for nine days. Wednesday morning a panicked Slack message: “the data is empty.” The site rebuilt overnight. .product-price is now .css-8xk2m9. You spend two hours in DevTools, push a fix, invoice the client for “maintenance.” They start asking why you keep charging them to fix something they thought worked. Three months in, it's every scraper, every other week.

Two ways out, both built in

LumaBrowser ships with two strategies for surviving a redesign — pick whichever fits your target site, or use both. Both are free, both run locally.

Strategy 1

Self-healing selectors

Pass a natural-language description alongside your CSS path. When the selector misses, an LLM resolves the description against a cleaned DOM snapshot and returns the new locator. Every click, fill_form, get_element, and wait_for call accepts an llmFallback field.

curl -X POST http://localhost:3000/api/browser/tabs/0/get_element \
  -H "Content-Type: application/json" \
  -d '{
    "selector": ".product-price",
    "llmFallback": "the visible price next to the buy button"
  }'
Strategy 2

CDP network interception

Skip the DOM entirely. Most modern sites render from a JSON API the page calls in the background. The Network Watcher uses Chrome DevTools Protocol to capture those requests and forward the parsed payloads to your webhook — no HTML parsing, no selectors that can rot.

curl -X POST http://localhost:3000/api/network-watcher \
  -H "Content-Type: application/json" \
  -d '{
    "urlPattern":  "*/api/products/search*",
    "forwardTo":   "https://your-webhook.example.com/products"
  }'

End-to-end: a price scraper that survives a redesign

One Python script. Open the page, extract the price with a CSS selector, fall back to a description if the class changed. Same script keeps running through the next ship-week.

import requests

API = "http://localhost:3000/api/browser"

# 1. Open the page in a real Chromium tab.
tab = requests.post(f"{API}/tabs", json={
    "url": "https://acme-shop.com/products/widget-pro"
}).json()["id"]

# 2. Read the price. CSS path first, LLM fallback if the class moved.
price = requests.post(f"{API}/tabs/{tab}/get_element", json={
    "selector":    ".product-price",
    "llmFallback": "the visible price next to the buy button"
}).json()["text"]

# 3. Same shape both ways — the call returns text either way.
print("current price:", price)
const API = 'http://localhost:3000/api/browser';

// 1. Open the page.
const { id: tab } = await fetch(`${API}/tabs`, {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ url: 'https://acme-shop.com/products/widget-pro' })
}).then(r => r.json());

// 2. CSS path first, LLM fallback if it misses.
const { text: price } = await fetch(`${API}/tabs/${tab}/get_element`, {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({
        selector:    '.product-price',
        llmFallback: 'the visible price next to the buy button'
    })
}).then(r => r.json());

console.log('current price:', price);
# Open the page
curl -X POST http://localhost:3000/api/browser/tabs \
  -H "Content-Type: application/json" \
  -d '{"url": "https://acme-shop.com/products/widget-pro"}'

# Read the price — CSS first, LLM fallback if the class moved
curl -X POST http://localhost:3000/api/browser/tabs/0/get_element \
  -H "Content-Type: application/json" \
  -d '{
    "selector": ".product-price",
    "llmFallback": "the visible price next to the buy button"
  }'

Where LumaBrowser sits in the scraping stack

Most paid scraping APIs charge per request and bill JavaScript-rendered pages at higher multiples. Most open-source frameworks have no self-healing. LumaBrowser is the local-app option that closes both gaps.

ScrapingBee ZenRows Apify Bright Data Scraper API Scrapy / raw Playwright LumaBrowser
Cost From $49/mo, credit-multiplier model (5x for JS) From $49/mo, CPM model $5/mo free credits, then $0.20–0.30/CU $1.50–2.50 per 1k requests, $499/mo subscription Free OSS Free
Where it runs Vendor cloud Vendor cloud Vendor cloud (managed Actors) Vendor cloud + proxies Your machine Your machine
JS rendering 5x credit multiplier Premium-tier CPM Built in Built in Yes (Playwright) Always on
Self-healing selectors No No No (platform) No No Per-call llmFallback
CDP network interception No No Limited No Manual setup Built in
Authenticated targets Bring cookies Bring cookies Manual login flow Bring cookies Manual Your logged-in session

Pricing as listed by each vendor at time of publication. Cloud scraping APIs typically charge more for JavaScript-rendered pages, premium proxies, or anti-bot bypass — the listed entry price is the floor, not the typical bill. LumaBrowser's free tier ships with anonymous app analytics; see what we collect.

This comparison reflects publicly available pricing and feature information gathered to the best of our knowledge from each vendor's public materials. Vendors update plans frequently and we're a small team — if anything here looks wrong, please email [email protected] with the correction and a source, and we'll update the page.

The maintenance math

Panic debug
→ log line
what a one-class rename looks like with an LLM fallback in place

Bill clients for builds, not babysitting

The most common selector-rot pattern — one-class rename on a CSS-in-JS site — is exactly what an LLM fallback resolves on the next run. The scraper keeps running, the audit log shows which calls fell back, and you can update the strict CSS path on your own schedule instead of in a panicked debug session. Your maintenance hours go where the work actually is — new sources, new fields, new targets — not patching .css-8xk2m9.

Stop charging clients for selector maintenance

LumaBrowser is the local browser the scrape-monkey freelancer wishes had existed five years ago. Free to download, runs on your machine, plugs into Python, Node, Go, or curl — whatever you already write your scrapers in.

Download LumaBrowser — free See the testing version