[KongchangAI]
· 3 min read· 1,764 words

One HTTP Request Misjudged 200 Websites: How a Browser + LLM Pipeline Turned It Around

One HTTP Request Misjudged 200 Websites: How a Browser + LLM Pipeline Turned It Around

A single HTTP check misjudged heavily; Nearbia switched to Chrome rendering plus local LLM classification, converging 200 business domains into 88 trustworthy corpus entries.

To determine whether 200 local business websites truly belonged to the St. Petersburg area, the Nearbia team first tried a single HTTP request to grab HTML and match city names, failing in both directions: missing JS-rendered sites and those with addresses only on contact pages, while letting through lead-gen sites and parked domains—undercounting 9 dead sites and falsely admitting 6 lead-gen sites. Running the checker from a Netherlands node left 47 sites unresponsive; switching to a U.S. IP brought 15 back immediately. Version 2's pipeline used Chrome rendering, local LLM labeling, citation verification, and code-based location in four steps, ranking location evidence by phone, ZIP code, and city-plus-state combinations. Ultimately 88 domains formed the corpus, 45 of them in-city; the initial database missed 21 in-city plumbers, with Google alone finding 11.

Using a single HTTP request to determine whether 200 local business websites truly belong to a service provider in a given area sounds like a lightweight approach. But the Nearbia team's real-world testing proved that this simple method misses more than 30% of dead sites and a large number of marketing sites. They ultimately pivoted to a pipeline combining browser rendering with local LLM classification, pulling accuracy back to a usable level. Below is the complete record of pitfalls and data.

rss source: Why One HTTP Request Misjudged 200 Business Websites, and the Browser + LLM Pipeline That Replaced I

Input: 8 Sources, 200 Domains

Candidate domains came from 5 free sources and 3 paid sources. Free sources included Overture Maps, Foursquare, OpenStreetMap, AllThePlaces, and Florida state license records; paid sources were Google Business listings, Google Maps, and Google organic search results.

The filtering rule was based on geographic boundaries: a domain was kept if at least one source placed it within the St. Petersburg city limits (using the U.S. Census Bureau's TIGERweb Places layer) or within 3 kilometers of the boundary. Ultimately, 92 domains were inside the city, 90 were in adjacent areas like Gulfport and Kenneth City, 17 came only from organic search with no coordinates, and 1 had only a postal city designation.

Source coverage had its own gaps: 30 domains came only from Foursquare, 21 only from Overture, and 17 only from Google organic results. All Google queries in this slice cost roughly $0.18 in total.

Version 1: One HTTP Request to Rule Them All

The initial approach was resource-efficient: send one request to each homepage, grab the first 400KB of raw HTML, and search for city names and plumbing-related terms. Results failed in both directions.

It rejected genuine businesses: sites rendering content with JavaScript, sites placing the address only on a contact page, and sites writing "Tampa Bay" rather than a specific city name.

It also let wrong targets through: lead-gen and directory sites stuff city names throughout the page; parked domains and server placeholder pages were misclassified as live sites.

Compared with a real browser, the quick check undercounted dead domains by 9 (27 vs. 36) and let through 6 lead-gen domains. After October 1, 2026, this checker was downgraded to merely reporting whether a domain responds, no longer participating in judgments.

Where the Checker Runs Matters Too

Running the check from a computer in the Netherlands, 47 of 200 sites were unresponsive. But retrying from a U.S. IP, 15 of those 47 loaded immediately. All subsequent checks ran from U.S. nodes.

Why does "one HTTP request" inherently fail?

What requests or curl fetches is just the raw HTML returned by the server—it doesn't execute JavaScript in the page. Modern business sites rely heavily on front-end frameworks (React, Vue, Wix, Squarespace, etc.) or third-party components, so addresses, phone numbers, and business hours are often injected into the DOM only after scripts run. The city name simply hasn't appeared in the 400KB the crawler grabbed, so it gets misjudged as an "irrelevant site."

The other side is "false positives": SEO lead-gen sites and directory aggregator pages stuff keywords like "St. Petersburg plumber" throughout titles and body text, easily fooling static text matching; parked domains and hosting providers' default placeholder pages return HTTP 200 but contain no real business content.

Browser rendering (headless Chrome) is essential because it simulates the final DOM a real user sees, while also exposing network requests, redirect chains, and page liveness—only this can distinguish "responding" from "having real content."

Why does geographic IP change the availability judgment?

The same domain failing from the Netherlands and succeeding from the U.S. is usually not network jitter but a combination of three things:

  1. CDN and edge node policies—Cloudflare, Akamai, and others allocate different nodes based on source IP region, and some regions may be rate-limited or trigger challenges.
  2. Regional bot protection—Many U.S. hosting providers are stricter by default toward non-domestic traffic, returning 403, 429, or JS challenge pages that make the site look "down."
  3. Localized content and DNS resolution—Some sites return different pages based on visitor location, or even resolve to different origin IPs.

This means any pipeline that takes "is the site alive" as input must fix its probe node in the target market's location—otherwise it will misjudge normally accessible local businesses as dead sites, directly contaminating the downstream corpus.

Version 2: Render, Label, Verify, Locate

The new pipeline has four steps:

  1. Render. Each live homepage loads in regular Chrome with JavaScript enabled, 5 browsers in parallel. The 171 responding homepages took about 40 minutes.

  2. Label. A language model deployed on Nearbia's own servers reads each captured page and assigns a site type: local company-owned site, chain branch page, national chain, directory site, lead-gen site, unrelated site, or dead site.

  3. Verify. The model must quote source text from the page to support its label, and code confirms each quote actually appears on the page. Labels with confidence below 0.6 go to human review.

  4. Locate. Whether the business is local is not the model's job. Code decides based on evidence found on the site.

Why split "label first, then locate" into two steps?

Having the language model answer both "what type of site is this" and "what city is it in" seems convenient but is actually unreliable: LLM judgments about "geographic location" are easily swayed by other cities mentioned on the page, chain brand headquarters, or even ad content—and this is precisely the step that can least afford errors.

Once separated, the model only handles semantic classification—what it's good at—determining whether it's a local owned site, chain branch page, directory site, or lead-gen site; location determination goes to deterministic code, validating step by step through combinations of phone, ZIP code, city name, and state name. This division of "model does perception, code does determination" makes every piece of location evidence reviewable and explainable, avoiding writing hallucinations directly into the directory.

The verification step serves the same goal: the model must quote source sentences, and code checks that the sentence actually exists on the page. Mismatched quotes or confidence below 0.6 go to human review, essentially adding an auditable gate to the automation pipeline.

Priority Ranking of Location Evidence

Code reads the homepage first, then opens up to two more pages if needed: contact page, service area page, or chain store's local page.

Evidence strength from high to low:

PrioritySignalDetails
1Site phone matches phone recorded for the domain in sourcesSource record itself is already within the city; ignore 8xx toll-free numbers since chains share them across locations
2Address or area ZIP code in structured data33701–33716, 33730–33747, 33781, 33782
3City name adjacent to "FL" or "Florida"State name is mandatory validation: Gulfport also exists in Mississippi, and there's a Petersburg in Tennessee
WeakArea name only (Pinellas, Tampa Bay) or 727 area code onlyInsufficient to enter the corpus

Before merging three duplicates, 91 sites had location evidence. The strongest signal was 58 phone matches, 19 city-plus-state matches, and 14 ZIP code matches. Of those 91, 10 were chain brand local pages.

Why is the phone number considered the strongest signal?

Phones (especially local landlines or numbers exactly matching source databases) are nearly impossible for lead-gen and directory sites to fake—they typically list only national customer service or 8xx numbers, and 8xx is explicitly excluded because chains share them across locations. By contrast, city names and ZIP codes are extremely low-cost to put on a page, and SEO sites actively stuff them in, so their credibility is naturally lower.

The "city name + state name" combination solves place-name ambiguity: Gulfport also exists in Mississippi, Petersburg also exists in Tennessee, and matching city name alone directly introduces errors. Requiring city names to appear adjacent to "FL" or "Florida" essentially demands that the page itself provide sufficient geographic context for disambiguation.

The significance of the priority table is that it converts vague "looks like a local business" into sortable, auditable rules—only domains passing the highest-level evidence enter the corpus, thereby converging 200 candidates to 88 high-confidence targets.

Final Classification of 200 Domains (October 1, 2026 Snapshot)

GroupDomain Count
Local plumbers with location evidence88
Local plumbers with area info only10
Local plumbers with no location evidence7
Other trades local businesses (HVAC, appliance repair)13
National chains and brands3
Directory and lead-gen sites17
Non-home services16
Duplicates redirecting to other domains in the list4
Blocked by bot protection even with real browser6
Dead, parked, or DNS-unresolved36
Total200

The 88 domains constitute the corpus, and only they proceed to full crawling and fact extraction. Of these, 45 are inside the city limits and 38 are within 3 kilometers of the boundary.

Three Types of Errors in the Data Itself

Wrong website on local records. Five domains associated with St. Petersburg actually belong to plumbers in Colorado, Idaho, Mississippi, Broward County, and Orange City, Florida. Without evidence from the sites themselves, they would enter the directory.

Blind spots in the starting database. Of the 45 in-city plumbers in the corpus, 21 were not captured by the initial database. Google alone found 11 of those 45.

Fabricated trade. The model once labeled a sewer pipe lining company as window cleaning. This is why the "other trades" group requires human review.

Known Limitations

This is only a single snapshot from October 1, 2026. Websites change, and dead domains may come back to life.

The check reads only homepages plus up to two interior pages. If a plumber marks its service area only on deeper pages, it falls into the "area only" or "no location evidence" groups—these 17 domains still need human review.

Only businesses with known websites are covered. Florida records list 93 active plumbing licenses at St. Petersburg addresses; 69 of them have no known domain, and some have no website at all.

How do you confirm that a website truly belongs to a business in a given place? Feel free to share the signals you rely on.

This article is based on Nearbia's original field notes, written with AI assistance; all numbers come from those notes.

#HTTP request misclassification#browser rendering#local LLM pipeline#business website classification#location evidence#data quality
Share:

Related articles