Self-hosted Google Maps business lead extraction and AI email enrichment. Free, open-source, bring your own keys.
1
stars
1
commits
TypeScript
primary language
Sep 11, 2026
updated
Search a location and category, pull real business listings, enrich them with AI-verified emails. Free, open-source, bring your own keys, no account with us, no fees.
Locus runs a pipeline: search a place and a category, pull the matching businesses, then run each one through an AI enrichment step that tries to find a verified email. Results land in a table you can filter, sort, export (CSV, Excel, JSON), or push to a webhook.

Built with Next.js (App Router), Prisma and Postgres, NextAuth, BullMQ and Redis for the background worker, and a bring-your-own-key model for the search and AI providers.
Locus pulls data from Google Maps and third-party search APIs. Automated collection of Google Maps data is against Google's Terms of Service. You are responsible for how you run this, which providers and proxies you use, and whether that is allowed where you operate. This project is provided for research and educational purposes with no warranty. See the licence.
There's no hosted version of Locus. Running it, for local development or for real, is entirely up to you: your own Postgres, your own Redis, your own server or machine. Two ways to do that:
This builds and runs everything, the app, the worker, Postgres, and Redis, as one stack.
git clone https://github.com/<you>/locus.git
cd locus
cp .env.example .env
# Fill in NEXTAUTH_URL, NEXTAUTH_SECRET, ENRICHMENT_KEY_SECRET, and
# INTERNAL_API_SECRET in .env before continuing. Docker compose refuses to
# start without them, on purpose.
docker compose up --build -d
Open the app at the NEXTAUTH_URL you set (http://localhost:3000 for a
same-machine test), create an account, and add your provider keys in
Settings. See the comments in docker-compose.yml and .env.example for
what NEXT_PUBLIC_WS_URL needs to be once this is reachable from outside
your own machine (it has to be a real address a browser can reach, not a
Docker-internal one), and put a reverse proxy (nginx, Caddy, Traefik) in
front for HTTPS rather than exposing the app's port directly.
git clone https://github.com/<you>/locus.git
cd locus
npm install
cp .env.example .env
docker compose up -d postgres redis
npx prisma migrate deploy
npm run dev:all
Postgres comes up on port 5433, Redis on 6379, the app on 3000, the worker on 4000, all with reload on file changes.
.env.example documents every variable. The ones you must set for a working
install, Docker or not:
| Variable | What it is |
|---|---|
DATABASE_URL | Postgres connection string. The compose file uses postgresql://locus:locus_dev@localhost:5433/locus by default |
REDIS_URL | Redis connection string for the job queue |
NEXTAUTH_SECRET | Session signing secret. openssl rand -base64 32 |
NEXTAUTH_URL | The app's real public URL, http://localhost:3000 for local testing |
ENRICHMENT_KEY_SECRET | Encrypts provider keys at rest. openssl rand -hex 32 |
INTERNAL_API_SECRET | Shared secret between the app and the worker. openssl rand -hex 32 |
FRONTEND_ORIGIN | Origin allowed to reach the worker, http://localhost:3000 in dev |
Optional: RESEND_API_KEY (password-reset email, logs the link to the console
when unset), SENTRY_*, NINE_ROUTER_BASE_URL (self-hosted model router),
TRUST_PROXY_HEADERS (only if a reverse proxy sits in front of this app).
Once the app is running and you've created an account:
Add provider keys. Settings > API Keys needs one business-search key (SerpApi, Serper.dev, or Google Places) and one AI key (Gemini, OpenAI, Anthropic, or OpenRouter) before anything can run. SerpApi and Serper.dev both have a free way to get started, no card required. A Hunter.io key is optional, it has a free tier (50 lookups/month) and, when added, gets checked before the website-and-AI enrichment step runs.

Run a search, one at a time on the Dashboard, or upload a CSV
for several at once (a query column and a location column, an
optional radius column per row). Progress streams in live: businesses
found, phone numbers extracted, emails verified, as they happen, not
just at the end.
Work the results. The leads table supports filtering by status (verified, needs enrichment, pending, failed), search, sort, and bulk actions. A lead that failed enrichment can be retried individually or in bulk. The same business found again in a later search gets merged into its existing lead instead of duplicated. Export whenever you want a CSV, Excel file, or JSON, or push results to a webhook or CRM connector.
Come back to it later. Every search lands in History with its full config snapshot (radius, provider, enrichment depth). Click any past search to reopen its results, re-run it with the same settings, or select two searches to compare their yield side by side.

npm run dev:all # app plus worker with reload
npm test # Vitest unit and integration
npm run test:e2e # Playwright, needs the app running
npx tsc --noEmit # typecheck
See TESTING.md for the test layers and conventions.
app/, Next.js App Router pages and API routesserver/index.ts, the Express and BullMQ worker that runs searches and
enrichment and pushes progress over WebSocketlib/enrichment/, provider adapters and the enrichment waterfalllib/export/, CSV/Excel/JSON export and the webhook/connector layerprisma/schema.prisma, the data modelSee CONTRIBUTING.md. Security reports go through SECURITY.md, not public
issues.
AGPL-3.0-or-later. If you run a modified version as a network service, you have
to make your changes available to its users. See LICENSE.
Built by Cloudz Computing
1 commits
TypeScript
98.4%
Self-hosted Google Maps business lead extraction and AI email enrichment. Free, open-source, bring your own keys.
1
stars
1
commits
TypeScript
primary language
Sep 11, 2026
updated
Search a location and category, pull real business listings, enrich them with AI-verified emails. Free, open-source, bring your own keys, no account with us, no fees.
Locus runs a pipeline: search a place and a category, pull the matching businesses, then run each one through an AI enrichment step that tries to find a verified email. Results land in a table you can filter, sort, export (CSV, Excel, JSON), or push to a webhook.

Built with Next.js (App Router), Prisma and Postgres, NextAuth, BullMQ and Redis for the background worker, and a bring-your-own-key model for the search and AI providers.
Locus pulls data from Google Maps and third-party search APIs. Automated collection of Google Maps data is against Google's Terms of Service. You are responsible for how you run this, which providers and proxies you use, and whether that is allowed where you operate. This project is provided for research and educational purposes with no warranty. See the licence.
There's no hosted version of Locus. Running it, for local development or for real, is entirely up to you: your own Postgres, your own Redis, your own server or machine. Two ways to do that:
This builds and runs everything, the app, the worker, Postgres, and Redis, as one stack.
git clone https://github.com/<you>/locus.git
cd locus
cp .env.example .env
# Fill in NEXTAUTH_URL, NEXTAUTH_SECRET, ENRICHMENT_KEY_SECRET, and
# INTERNAL_API_SECRET in .env before continuing. Docker compose refuses to
# start without them, on purpose.
docker compose up --build -d
Open the app at the NEXTAUTH_URL you set (http://localhost:3000 for a
same-machine test), create an account, and add your provider keys in
Settings. See the comments in docker-compose.yml and .env.example for
what NEXT_PUBLIC_WS_URL needs to be once this is reachable from outside
your own machine (it has to be a real address a browser can reach, not a
Docker-internal one), and put a reverse proxy (nginx, Caddy, Traefik) in
front for HTTPS rather than exposing the app's port directly.
git clone https://github.com/<you>/locus.git
cd locus
npm install
cp .env.example .env
docker compose up -d postgres redis
npx prisma migrate deploy
npm run dev:all
Postgres comes up on port 5433, Redis on 6379, the app on 3000, the worker on 4000, all with reload on file changes.
.env.example documents every variable. The ones you must set for a working
install, Docker or not:
| Variable | What it is |
|---|---|
DATABASE_URL | Postgres connection string. The compose file uses postgresql://locus:locus_dev@localhost:5433/locus by default |
REDIS_URL | Redis connection string for the job queue |
NEXTAUTH_SECRET | Session signing secret. openssl rand -base64 32 |
NEXTAUTH_URL | The app's real public URL, http://localhost:3000 for local testing |
ENRICHMENT_KEY_SECRET | Encrypts provider keys at rest. openssl rand -hex 32 |
INTERNAL_API_SECRET | Shared secret between the app and the worker. openssl rand -hex 32 |
FRONTEND_ORIGIN | Origin allowed to reach the worker, http://localhost:3000 in dev |
Optional: RESEND_API_KEY (password-reset email, logs the link to the console
when unset), SENTRY_*, NINE_ROUTER_BASE_URL (self-hosted model router),
TRUST_PROXY_HEADERS (only if a reverse proxy sits in front of this app).
Once the app is running and you've created an account:
Add provider keys. Settings > API Keys needs one business-search key (SerpApi, Serper.dev, or Google Places) and one AI key (Gemini, OpenAI, Anthropic, or OpenRouter) before anything can run. SerpApi and Serper.dev both have a free way to get started, no card required. A Hunter.io key is optional, it has a free tier (50 lookups/month) and, when added, gets checked before the website-and-AI enrichment step runs.

Run a search, one at a time on the Dashboard, or upload a CSV
for several at once (a query column and a location column, an
optional radius column per row). Progress streams in live: businesses
found, phone numbers extracted, emails verified, as they happen, not
just at the end.
Work the results. The leads table supports filtering by status (verified, needs enrichment, pending, failed), search, sort, and bulk actions. A lead that failed enrichment can be retried individually or in bulk. The same business found again in a later search gets merged into its existing lead instead of duplicated. Export whenever you want a CSV, Excel file, or JSON, or push results to a webhook or CRM connector.
Come back to it later. Every search lands in History with its full config snapshot (radius, provider, enrichment depth). Click any past search to reopen its results, re-run it with the same settings, or select two searches to compare their yield side by side.

npm run dev:all # app plus worker with reload
npm test # Vitest unit and integration
npm run test:e2e # Playwright, needs the app running
npx tsc --noEmit # typecheck
See TESTING.md for the test layers and conventions.
app/, Next.js App Router pages and API routesserver/index.ts, the Express and BullMQ worker that runs searches and
enrichment and pushes progress over WebSocketlib/enrichment/, provider adapters and the enrichment waterfalllib/export/, CSV/Excel/JSON export and the webhook/connector layerprisma/schema.prisma, the data modelSee CONTRIBUTING.md. Security reports go through SECURITY.md, not public
issues.
AGPL-3.0-or-later. If you run a modified version as a network service, you have
to make your changes available to its users. See LICENSE.
Built by Cloudz Computing
1 commits
TypeScript
98.4%