lacspace-leads 1.7.0 → 1.7.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/LICENSE +51 -0
  2. package/README.md +127 -0
  3. package/package.json +4 -3
package/LICENSE ADDED
@@ -0,0 +1,51 @@
1
+ Lacspace Free Licence
2
+ Version 1.0, August 2026
3
+
4
+ Copyright (c) 2026 Lacspace
5
+
6
+ PREAMBLE
7
+
8
+ This software is published by Lacspace under the Lacspace Free Licence — a free,
9
+ permissive licence that lets you use this software for any purpose, including in
10
+ commercial products and services, at no cost. It grants the same freedoms as
11
+ common permissive open-source licences; the only condition is that this notice
12
+ travels with the software. The canonical, always-current text of this licence is
13
+ maintained at https://lacspace.com/licenses/lacspace-free-1.0
14
+
15
+ GRANT OF RIGHTS
16
+
17
+ Permission is hereby granted, free of charge, to any person or organisation
18
+ obtaining a copy of this software and its associated documentation and data files
19
+ (the "Software"), to deal in the Software without restriction, including without
20
+ limitation the rights to use, copy, modify, merge, publish, distribute,
21
+ sublicense, and/or sell copies of the Software, and to permit persons to whom the
22
+ Software is furnished to do so, subject to the conditions below. These rights are
23
+ granted for any purpose, personal or commercial, and are perpetual, worldwide,
24
+ non-exclusive, and royalty-free.
25
+
26
+ CONDITIONS
27
+
28
+ The above copyright notice, this permission notice, and the name of this licence
29
+ ("Lacspace Free Licence") shall be included in all copies or substantial portions
30
+ of the Software.
31
+
32
+ TRADEMARKS
33
+
34
+ This licence does not grant permission to use the trade names, trademarks, service
35
+ marks, logos, or product names of Lacspace, except as required to reproduce the
36
+ notice above or to describe the origin of the Software in a truthful manner.
37
+
38
+ DISCLAIMER OF WARRANTY AND LIMITATION OF LIABILITY
39
+
40
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
41
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
42
+ FOR A PARTICULAR PURPOSE, AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
43
+ COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES, OR OTHER LIABILITY, WHETHER IN
44
+ AN ACTION OF CONTRACT, TORT, OR OTHERWISE, ARISING FROM, OUT OF, OR IN CONNECTION
45
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
46
+
47
+ ---
48
+
49
+ The Lacspace Free Licence is a source-available, permissive licence and is not (as
50
+ of this version) an OSI-approved licence. In substance it grants the same freedoms
51
+ as the MIT Licence. Learn more at https://lacspace.com/licenses
package/README.md CHANGED
@@ -11,6 +11,7 @@ That opens a browser, searches Maps for *"restaurants in Baneshwor, Kathmandu"*,
11
11
  ## Why it's different
12
12
 
13
13
  - **Free & keyless** — uses a real browser (via [Playwright](https://playwright.dev)), not a paid Places API.
14
+ - **Ask for a number, get that number** — one Google search stops at ~120 results; `--target 500` keeps searching (every area you name, then tiles of the map) until it has 500 unique businesses.
14
15
  - **Sweep a whole city** — comma-separate areas and it runs each search, then **merges and de-duplicates** into one list: `--area "Thamel,Baneshwor,Patan"`.
15
16
  - **Search by radius** — centre on a coordinate and keep only what's within range: `--near "27.72,85.32" --radius 2km`. Each lead gets a `distanceKm`, sorted nearest-first.
16
17
  - **Accumulate a master list** — `--append` merges each run into your existing file and de-duplicates, so daily runs build one clean database.
@@ -27,6 +28,132 @@ That opens a browser, searches Maps for *"restaurants in Baneshwor, Kathmandu"*,
27
28
  - **Robust & polite** — `--proxy`, `--retries`, `--jitter`, per-listing delays and a permission-first prompt before it opens a browser.
28
29
  - **Library too** — `import { searchLeads, searchLeadsBatch, searchLeadsDetailed } from "lacspace-leads"`.
29
30
 
31
+ ## How to use it
32
+
33
+ ### 1. Run it and answer the questions
34
+
35
+ No flags to learn first. Run it bare and it walks you through business type, city, areas, how many leads you want and the file format — every question has a default, so you can press Enter through it:
36
+
37
+ ```bash
38
+ npx lacspace-leads
39
+ ```
40
+
41
+ Type more than ~120 at the "how many" question and it automatically switches to a full sweep, because a single Google search cannot return more than that.
42
+
43
+ ### 2. Or say it in one line
44
+
45
+ ```bash
46
+ npx lacspace-leads restaurants --city Kathmandu --area Baneshwor -f xlsx
47
+ ```
48
+
49
+ A real Chromium window opens, walks the map and writes the file into the folder you ran it from. Add `--headless` once you trust it and the window stays hidden.
50
+
51
+ ### 3. Ask for the number of leads you actually want
52
+
53
+ `-n / --limit` is the cap for **one search**, and Google itself stops serving a single search at roughly **120 results** — which is why `--limit 500` comes back with about 114. `--target` is the flag that goes past that:
54
+
55
+ ```bash
56
+ # 500 unique restaurants, however many searches that takes
57
+ npx lacspace-leads restaurants --city Kathmandu --target 500 -f xlsx
58
+ ```
59
+
60
+ It expands coverage in this order, stopping the moment it has enough:
61
+
62
+ 1. **Every place you named** — each city x area x type is its own search.
63
+ 2. **Map tiles** — the same query re-centred on a grid of points walking outwards from the city centre. The centre comes from the map itself, so no geocoding service and no API key.
64
+
65
+ Name your neighbourhoods to put the areas you care about first, and to finish sooner:
66
+
67
+ ```bash
68
+ npx lacspace-leads restaurants --city Kathmandu \
69
+ --areas "Baneshwor, Thamel, Patan, Lazimpat" --target 400
70
+ ```
71
+
72
+ Tune the grid with `--step` (spacing between tiles — tighter finds more in a dense city) and `--tiles` (how far out to go):
73
+
74
+ ```bash
75
+ npx lacspace-leads cafes --city Lalitpur --target 600 --step 1.5km --tiles 60
76
+ ```
77
+
78
+ If the map genuinely runs out before the target, it tells you instead of pretending:
79
+
80
+ ```
81
+ ! The map ran out of new results at 214 — that is everything Google lists here.
82
+ Widen it: more areas, more cities, a bigger --step, or related --types.
83
+ ```
84
+
85
+ ### 4. Several cities, several areas, several business types
86
+
87
+ Comma-separate any of them. Every combination becomes its own search and the results merge into one de-duplicated list:
88
+
89
+ ```bash
90
+ npx lacspace-leads \
91
+ --type "restaurant, cafe" \
92
+ --cities "Kathmandu, Lalitpur, Bhaktapur" \
93
+ --areas "Baneshwor, Thamel" \
94
+ --target 600 -f xlsx
95
+ ```
96
+
97
+ Add `--split city` to also write one file per city (or `area`, or `type`) beside the master export:
98
+
99
+ ```bash
100
+ npx lacspace-leads gyms --cities "Kathmandu, Pokhara" --target 300 --split city -f xlsx
101
+ ```
102
+
103
+ Long sweeps are resumable — `--resume` checkpoints after every search, so an interruption continues instead of re-scraping.
104
+
105
+ ### 5. Turn listings into contacts
106
+
107
+ ```bash
108
+ # Emails + social profiles from each business website
109
+ npx lacspace-leads clinics --city Pokhara --target 200 --enrich -f xlsx
110
+
111
+ # Only addresses whose domain really accepts mail
112
+ npx lacspace-leads hotels --city Kathmandu --target 150 \
113
+ --enrich --verify-emails --has-valid-email -f csv
114
+
115
+ # Phones in international format, ready for a CRM or bulk SMS
116
+ npx lacspace-leads pharmacies --city Biratnagar --target 200 --country NP --has-phone
117
+ ```
118
+
119
+ ### 6. Narrow it to the right prospects
120
+
121
+ ```bash
122
+ # Businesses with NO website — the pitch list for web work
123
+ npx lacspace-leads "beauty salon" --city Pokhara --target 200 --no-website --has-phone -f csv
124
+
125
+ # Only well-reviewed places, best first
126
+ npx lacspace-leads restaurants --city Kathmandu --target 300 \
127
+ --min-rating 4 --min-reviews 50 --sort reviews --desc
128
+
129
+ # Just the columns an outreach list needs
130
+ npx lacspace-leads cafes --city Lalitpur --target 200 --preset outreach
131
+ ```
132
+
133
+ ### 7. Make it repeatable
134
+
135
+ ```bash
136
+ # One master file that grows and de-duplicates every run
137
+ npx lacspace-leads restaurants --city Kathmandu --target 300 --append -o master.csv
138
+
139
+ # Export only the businesses that aren't already in it
140
+ npx lacspace-leads restaurants --city Kathmandu --target 300 \
141
+ --dedupe-across master.csv -o new-this-week.csv
142
+
143
+ # The whole campaign saved in one file, ready for a schedule
144
+ npx lacspace-leads --config campaign.json
145
+ ```
146
+
147
+ ### When something looks wrong
148
+
149
+ | What you see | What it means | What to do |
150
+ | --- | --- | --- |
151
+ | Far fewer leads than the target | The map ran out — that really is everything Google lists for that query | More areas or cities, related `--type`s, or a bigger `--step` |
152
+ | About 114 back from `--limit 500` | Google's per-search ceiling | Use `--target 500` instead |
153
+ | No leads at all | Usually a CAPTCHA or a page change | Drop `--headless` to watch it, raise `--delay`, try again shortly |
154
+ | Empty address / website columns | `--no-details` was on, so listings were never opened | Remove it (`--no-website` and `--split` turn details back on for you) |
155
+ | Everything grouped under "other" with `--split` | Same cause — no addresses to match against | Same fix |
156
+
30
157
  ## Install
31
158
 
32
159
  Needs **Node 20+** and a Chromium-based browser. It uses your installed **Google Chrome** or **Microsoft Edge** automatically; if you have neither, run:
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "lacspace-leads",
3
- "version": "1.7.0",
4
- "description": "Free, open-source local-business lead finder — name a city/area/business type, or search by coordinates + radius, and it drives a real browser over Google Maps to collect names, phones, websites, ratings, addresses, emails and social links, then exports to JSON, NDJSON, CSV or Excel. Sweep multiple areas at once, verify emails by MX, normalise phones to E.164, accumulate into a master file, and enrich in parallel through an optional proxy. No API keys.",
3
+ "version": "1.7.2",
4
+ "description": "Free, open-source local-business lead finder \u2014 name a city/area/business type, or search by coordinates + radius, and it drives a real browser over Google Maps to collect names, phones, websites, ratings, addresses, emails and social links, then exports to JSON, NDJSON, CSV or Excel. Sweep multiple areas at once, verify emails by MX, normalise phones to E.164, accumulate into a master file, and enrich in parallel through an optional proxy. No API keys.",
5
5
  "type": "module",
6
6
  "bin": {
7
7
  "lacspace-leads": "dist/cli.js"
@@ -19,7 +19,8 @@
19
19
  },
20
20
  "files": [
21
21
  "dist",
22
- "README.md"
22
+ "README.md",
23
+ "LICENSE"
23
24
  ],
24
25
  "scripts": {
25
26
  "build": "tsup",