scrapercity 1.0.21 → 1.0.23

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.
package/AGENT_DOCS.txt CHANGED
@@ -268,6 +268,18 @@ POST /api/v1/database/plan { "query": "dentists in Austin", "database": "local"
268
268
  apiParams, link }] }. Filters use values the database really has; count leaves out
269
269
  records this account already has. Send apiParams as they are to /api/v1/<apiEndpoint>.
270
270
 
271
+ POST /api/v1/autoleads/lookalike { "domains": ["acme.com", "mike@globex.com", "initech.com"] }
272
+ GET /api/v1/autoleads/lookalike?id=<job id>
273
+ $149/mo plan and up. Your customers' websites or work emails -> lookalike groups. POST starts a
274
+ background job (202 { job }); GET it until job.status is done (or failed). When done, job.groups:
275
+ [{ name, description, sites, database, filters, count, tool, addOn, reason }]. Send a group's
276
+ filters to that database's endpoint to pull the records. $0.01 per site read, from credits when the
277
+ job finishes; sites that can't be read are free. 402 = balance under $0.01 x every site (required,
278
+ available). 409 = a job is already running (job_id). At least 3 company sites; personal emails skipped.
279
+ GET /api/v1/autoleads/lookalike?id=<job id>&format=csv returns the finished job as a CSV, one row per
280
+ site: Domain, Read, Group, Sells, Sells To, Business Model, Industry, Keywords, Size, Country,
281
+ Contact Emails, Contact Titles. No extra charge.
282
+
271
283
  ────────────────────────────────────────
272
284
  ERROR CODES
273
285
  ────────────────────────────────────────
package/SKILL.md CHANGED
@@ -82,6 +82,7 @@ Apollo scrapes take **up to 4 days** to deliver. Do NOT poll in a loop.
82
82
  | GET | `/api/v1/database/local-businesses?category=Dentist&country=US&hasContact=true&limit=100` | Local biz DB ($149/mo plan and up, 100k new businesses/day). Search like Google Maps: `keyword` (repeat; names and categories, any keyword) and `location` (repeat; cities, states, countries, postal codes), with `searched_as` in the response and `lead_database` when nothing is found but the Lead Database has 100+ people. Filters: `category`, `country`, `state`, `city`, `postalCode`, `query`, `minRating`/`maxRating`, `minReviews`/`maxReviews`, `hasEmail`, `hasPhone`, `hasWebsite`, `hasContact` (only businesses with a named contact person), `title` (business name), `box=minLat,minLng,maxLat,maxLng` (a map area). Exclusions: `notCategory`. `url=<Google Maps search URL>` searches with that URL's keyword and place. POST with a JSON body takes the same parameters (use it for a long `url`). Same optional `excludeDelivered` / `after` |
83
83
  | GET | `/api/v1/database/ecommerce?...` | Ecommerce DB ($149/mo plan and up). Same optional `excludeDelivered` / `after`. Technologies: `technologies` (any of), `technologiesAll` (all of), `notTechnologies` (none of) |
84
84
  | POST | `/api/v1/database/plan` | $149/mo plan and up (GET with `?query=` also works). `{ "query": "dentists in Austin", "database"?: "leads" or "local" or "ecommerce" }` -> ready-to-run searches: `{ targeting, searches: [{ database, name, filters, count, apiEndpoint, apiParams, link }] }`. Send `apiParams` as they are to `/api/v1/<apiEndpoint>` |
85
+ | POST | `/api/v1/autoleads/lookalike` | $149/mo plan and up, $0.01 per site read. `{ "domains": ["acme.com", "mike@globex.com", ...] }` (websites or work emails, at least 3 company sites) -> 202 `{ job }`. Then GET `/api/v1/autoleads/lookalike?id=<job id>` until `job.status` is `done`: `job.groups` = `[{ name, description, sites, database, filters, count, tool }]`. Send a group's `filters` to that database's endpoint. 402 = balance under $0.01 x every site; 409 = a job is already running. Add `&format=csv` to the GET for every site as a CSV (no extra charge) |
85
86
 
86
87
  ## Status Values
87
88
  `RUNNING` → `SUCCEEDED` or `FAILED` or `CANCELLED`
package/bin/cli.mjs CHANGED
@@ -460,6 +460,46 @@ async function main() {
460
460
  break
461
461
  }
462
462
 
463
+ // ── Lookalike groups from your customers ($149/mo plan and up) ──
464
+ case 'lookalike': {
465
+ const file = flag('--file')
466
+ let domains = [...args]
467
+ if (typeof file === 'string') domains = [...domains, ...fs.readFileSync(file, 'utf-8').split(/[\n,;\t]+/).map(l => l.trim()).filter(Boolean)]
468
+ if (!domains.length) die('Usage: scrapercity lookalike acme.com mike@globex.com ... [--file customers.txt]')
469
+ const start = await sc.lookalikeStart(domains)
470
+ let job = start.job
471
+ console.log(`Job ${job.id}: ${job.sites} sites, up to $${Number(job.max_cost).toFixed(2)}`)
472
+ while (job.status === 'running') {
473
+ await new Promise(r => setTimeout(r, 10000))
474
+ job = (await sc.lookalikeJob(job.id)).job
475
+ process.stdout.write(`\r ${job.stage} ${job.percent}% `)
476
+ }
477
+ console.log('')
478
+ if (job.status !== 'done') die(job.error || 'The job did not finish.')
479
+ console.log(`${job.sites_read} of ${job.sites} sites read, $${Number(job.charged).toFixed(2)} charged, ${job.groups.length} groups`)
480
+ for (const g of job.groups) {
481
+ console.log(`\n${g.name} (${g.database}): ${Number(g.count).toLocaleString()} matches, ${g.sites.length} of your customers`)
482
+ console.log(` ${g.description}`)
483
+ console.log(` Best tool: ${g.tool}${g.addOn ? ' + ' + g.addOn : ''}`)
484
+ console.log(` filters: ${JSON.stringify(g.filters)}`)
485
+ }
486
+ break
487
+ }
488
+
489
+ case 'lookalike-csv': {
490
+ const out = flag('-o') || flag('--output')
491
+ if (!args[0]) die('Usage: scrapercity lookalike-csv <jobId> [-o lookalike.csv]')
492
+ const dl = await sc.lookalikeCsv(args[0], typeof out === 'string' ? out : undefined)
493
+ console.log(`✓ Saved ${dl.path} (${Math.round(dl.bytes / 1024)} KB)`)
494
+ break
495
+ }
496
+
497
+ case 'lookalike-status': {
498
+ if (!args[0]) die('Usage: scrapercity lookalike-status <jobId>')
499
+ console.log(json((await sc.lookalikeJob(args[0])).job))
500
+ break
501
+ }
502
+
463
503
  // ── Poll (convenience) ────────────────────────────────
464
504
  case 'poll': {
465
505
  if (!args[0]) die('Usage: scrapercity poll <runId> [--interval 15]')
@@ -514,6 +554,12 @@ ScraperCity CLI - B2B lead generation from your terminal
514
554
  scrapercity plan "dentists in Austin" Plain English -> ready-to-run database searches
515
555
  --database leads|local|ecommerce Only this database (default: the ones that fit)
516
556
 
557
+ Lookalike groups ($149/mo plan and up, $0.01 per site read):
558
+ scrapercity lookalike acme.com mike@globex.com ... Group your customers into lookalike audiences
559
+ --file customers.txt One website or work email per line
560
+ scrapercity lookalike-status <jobId> Read a lookalike job
561
+ scrapercity lookalike-csv <jobId> [-o f.csv] Every site of a finished job as a CSV
562
+
517
563
  Database ($149/mo plan and up):
518
564
  scrapercity db-leads [filters] Query lead database
519
565
  --title --industry --country --state --city --company --domain --seniority --department
package/bin/mcp.mjs CHANGED
@@ -56,6 +56,28 @@ const UTILITY_TOOLS = [
56
56
  database: { type: 'string', enum: ['leads', 'local', 'ecommerce'], description: 'Optional: plan in this database only (leads = people at companies, local = local businesses, ecommerce = online stores). Leave it out to let the planner pick the databases that fit.' }
57
57
  }, required: ['query'] }
58
58
  },
59
+ {
60
+ name: 'find_lookalike_groups',
61
+ description: 'Group your customers into lookalike audiences. Send their websites or work emails (mike@acme.com reads acme.com and looks up Mike\'s title). Starts a background job and returns it with its id; then call get_lookalike_groups until status is done. Each group comes with ready-to-run database filters, a match count and the best tool to reach businesses like them. $0.01 per site read, from credits; your balance must cover $0.01 x every site. One job at a time. $149/mo plan and up.',
62
+ inputSchema: { type: 'object', properties: {
63
+ domains: { type: 'array', items: { type: 'string' }, description: 'Your customers\' websites, URLs or work emails. At least 3 company sites.' }
64
+ }, required: ['domains'] }
65
+ },
66
+ {
67
+ name: 'get_lookalike_groups',
68
+ description: 'Read a lookalike job started with find_lookalike_groups: status (running, done, failed), percent, and when done the groups. Each group has name, description, sites (your customers in it), database (leads, local, ecommerce), filters, count and tool. Pass its filters to query_lead_database, query_local_business_database or query_ecommerce_database to pull the records.',
69
+ inputSchema: { type: 'object', properties: {
70
+ id: { type: 'string', description: 'The job id from find_lookalike_groups' }
71
+ }, required: ['id'] }
72
+ },
73
+ {
74
+ name: 'download_lookalike_csv',
75
+ description: 'Save a finished lookalike job (find_lookalike_groups) as a CSV file, one row per uploaded site: whether it was read, what it sells and to whom, business model, industry, keywords, size, country, the contacts from your upload with their titles, and its group. No extra charge.',
76
+ inputSchema: { type: 'object', properties: {
77
+ id: { type: 'string', description: 'The job id from find_lookalike_groups' },
78
+ outputPath: { type: 'string', description: 'File path to save the CSV (default: lookalike-{id}.csv)' }
79
+ }, required: ['id'] }
80
+ },
59
81
  {
60
82
  name: 'query_lead_database',
61
83
  description: 'Query the B2B lead database directly. Returns contacts with names, emails, phones, titles, companies (plus company website, revenue, HQ location and keywords). Up to 100 per request. Included with the $149/mo plan (100,000 new leads a day; leads you already have do not count again). For a daily pull of only new leads set excludeDelivered=true. For big pulls page with after: send after="0" first, then the pagination.next_after from each response until it is null. Have a saved people-search URL? Pass it as url to search with its filters right away (search_translation in the response says what was applied). Target by the technologies a company\'s website runs: technologies (any of), technologiesAll (all of), notTechnologies (none of), e.g. technologiesAll ["Klaviyo", "PayPal", "Shopify"] with notTechnologies ["Facebook Pixel"].',
@@ -180,6 +202,13 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
180
202
  }
181
203
  case 'cancel_run': result = await sc.cancel(args.runId); break
182
204
  case 'plan_search': result = await sc.planSearch(args.query, args.database); break
205
+ case 'find_lookalike_groups': result = await sc.lookalikeStart(args.domains); break
206
+ case 'get_lookalike_groups': result = await sc.lookalikeJob(args.id); break
207
+ case 'download_lookalike_csv': {
208
+ const dl = await sc.lookalikeCsv(args.id, args.outputPath)
209
+ result = { success: true, path: dl.path, sizeKB: Math.round(dl.bytes / 1024) }
210
+ break
211
+ }
183
212
  case 'query_lead_database': {
184
213
  const params = { ...args }
185
214
  if (params.hasEmail) params.hasEmail = 'true'
@@ -1,6 +1,6 @@
1
1
  // AUTO-GENERATED by scripts/generate.mjs — DO NOT EDIT BY HAND.
2
2
  // Source of truth: scraperConfigs.ts (+ src/config/scrapers.ts). Regenerate: npm run generate
3
- // Generated: 2026-10-26T17:52:08.300Z
3
+ // Generated: 2026-10-27T22:10:20.665Z
4
4
  export const TOOLS = [
5
5
  {
6
6
  "name": "scrape_apollo",
@@ -662,5 +662,6 @@ export const ENDPOINTS = {
662
662
  "database-local-business-enrichment": "/api/v1/enrich/local-businesses",
663
663
  "database-local-businesses": "/api/v1/database/local-businesses",
664
664
  "database-ecommerce": "/api/v1/database/ecommerce",
665
- "database-plan": "/api/v1/database/plan"
665
+ "database-plan": "/api/v1/database/plan",
666
+ "autoleads-lookalike": "/api/v1/autoleads/lookalike"
666
667
  }
package/lib/client.mjs CHANGED
@@ -212,6 +212,28 @@ export const dbEcommerce = (params) => {
212
212
  export const planSearch = (query, database) =>
213
213
  post('/api/v1/database/plan', database ? { query, database } : { query })
214
214
 
215
+ // Lookalike groups from your customers ($149/mo plan and up): start a job with websites or work emails, then read it by id.
216
+ export const lookalikeStart = (domains) => post('/api/v1/autoleads/lookalike', { domains })
217
+ export const lookalikeJob = (id) => get(`/api/v1/autoleads/lookalike?id=${encodeURIComponent(id)}`)
218
+
219
+ // A finished lookalike job as a CSV file, one row per site (what was read, contacts, group).
220
+ export async function lookalikeCsv(id, outputPath) {
221
+ const key = getApiKey()
222
+ if (!key) throw new Error('No API key. Run: scrapercity login OR export SCRAPERCITY_API_KEY=...')
223
+ const res = await fetch(`${BASE}/api/v1/autoleads/lookalike?id=${encodeURIComponent(id)}&format=csv`, {
224
+ headers: { 'Authorization': `Bearer ${key}`, 'User-Agent': 'scrapercity-cli/1.0' }
225
+ })
226
+ if (!res.ok) {
227
+ let msg = `HTTP ${res.status}`
228
+ try { msg = (await res.json()).error || msg } catch {}
229
+ throw new Error(msg)
230
+ }
231
+ const buf = Buffer.from(await res.arrayBuffer())
232
+ const out = outputPath || `lookalike-${id}.csv`
233
+ fs.writeFileSync(out, buf)
234
+ return { path: out, bytes: buf.length }
235
+ }
236
+
215
237
  // ── Poll until done ───────────────────────────────────────────
216
238
  export async function pollUntilDone(runId, { interval = 15000, timeout = 3600000, onStatus } = {}) {
217
239
  const start = Date.now()
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "scrapercity",
3
- "version": "1.0.21",
3
+ "version": "1.0.23",
4
4
  "description": "ScraperCity CLI & MCP Server - B2B lead generation for AI agents",
5
5
  "type": "module",
6
6
  "bin": {