Find the website
Find the website
Finds a company's official website from its name and city, then proves the match by reading the site itself. No AI: every decision comes from an observable fact, and an unproven candidate is never written.
Purpose
emails, socials, legal_mentions, techstack and pricing all require a site_web. A list
that has names but no websites cannot reach any of them. This module is the bridge.
It runs in two stages:
- Search. A web search on the company name plus its city. Directories, social networks,
marketplaces, registry pages and job boards are discarded: the target is the company's own site,
not its listing on someone else's. If the legal name returns nothing usable, a second search runs
on the most distinctive word of the name plus the city, which is how companies are usually
referenced online (
SAS LABLAUDE Diégois not a phrase anyone writes;Lablaudeplus the town is). The establishment qualifier registries append to the legal name is stripped before searching:NALDEO - SITE DE TARNOSbecomesNALDEO, otherwise the query targets a label nobody ever writes. - Proof. The surviving candidates are opened and read. The site is accepted only when its pages contain the company's postcode or town, or, for an exact name match, the company name. Without proof the cell stays empty.
Between two domains that both prove the same company, the apex domain wins over a subdomain, and
a deployment host address (.vercel.app and the like) is only ever a last resort.
All traffic goes through the VPN proxy pool, one exit per worker.
Inputs
| Field | Required | Notes |
|---|---|---|
nom |
yes | Company name. Aliases accepted (name, raison sociale, entreprise…). |
ville / commune |
no | Strongly recommended: it is both a search term and the main proof. |
code_postal |
no | Used as proof when reading the candidate site. |
adresse |
no | Fallback: the postcode and town are read from it when the dedicated columns are missing. |
Like opendata, this module accepts rows with no contact detail at all: creating one is its
job. Rows with no usable name are kept in the output file untouched.
Outputs
| Column | Type | Description |
|---|---|---|
site_web |
url | The website found. An existing value is never overwritten. |
site_finder_match |
text | Why the decision was made: nom + adresse vérifiés sur le site, nom vérifié sur le site, nom exact (site injoignable), site déjà présent, or the reason nothing was written. |
Every input column is carried through unchanged (column passthrough contract).
Lifecycle
POST /api/jobs/site-finder → pending → running → completed.
Rows that already have a website are written straight to the output and never searched, so chaining this module after a cheaper one costs nothing on the rows the cheaper one already filled.
Cancellation is honoured between items. A row that fails twice is written with
site_finder_match = recherche indisponible rather than dropped.
Pipeline
| input | pois_any |
| output | pois |
| needs | nom |
| produces | site_web, site_finder_match |
Recommended order: sirene → opendata → site_finder → emails. The first two are free and
instant; this one costs network time, so it should only see the rows they could not fill.
Errors
| Case | Behaviour |
|---|---|
| No usable name in the whole list | 400 with the list of columns that were read |
| Search engine unavailable | The row is retried on another VPN exit, then written empty with an explicit reason |
| Candidate site unreachable | Accepted only if the domain is an exact name match, otherwise skipped |
| Same-name company elsewhere | Rejected: the postcode/town proof fails, nothing is written |
What's next
The output feeds any module that needs a website. emails is the usual next step.