scrapercity 1.0.18 → 1.0.19
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 +10 -0
- package/SKILL.md +1 -0
- package/bin/cli.mjs +20 -0
- package/bin/mcp.mjs +9 -0
- package/lib/catalog.generated.mjs +3 -2
- package/lib/client.mjs +4 -0
- package/package.json +1 -1
package/AGENT_DOCS.txt
CHANGED
|
@@ -252,6 +252,16 @@ GET /api/v1/database/local-businesses?category=Dentist&country=US&hasContact=tru
|
|
|
252
252
|
GET /api/v1/database/ecommerce?...
|
|
253
253
|
Same pattern (including excludeDelivered and after), different dataset.
|
|
254
254
|
|
|
255
|
+
POST /api/v1/database/plan { "query": "dentists in Austin", "database": "local" }
|
|
256
|
+
(or GET /api/v1/database/plan?query=...&database=...)
|
|
257
|
+
$149/mo plan and up; planning does not count toward the daily limit. Plain English -> ready-to-run
|
|
258
|
+
database searches. query is who you want ("websites that
|
|
259
|
+
use shopify", "CEOs of SaaS companies in Texas") or what you sell ("I sell life insurance").
|
|
260
|
+
database (optional): leads | local | ecommerce; leave it out to let the planner pick.
|
|
261
|
+
Returns: { targeting, searches: [{ database, name, description, filters, count, apiEndpoint,
|
|
262
|
+
apiParams, link }] }. Filters use values the database really has; count leaves out
|
|
263
|
+
records this account already has. Send apiParams as they are to /api/v1/<apiEndpoint>.
|
|
264
|
+
|
|
255
265
|
────────────────────────────────────────
|
|
256
266
|
ERROR CODES
|
|
257
267
|
────────────────────────────────────────
|
package/SKILL.md
CHANGED
|
@@ -81,6 +81,7 @@ Apollo scrapes take **up to 4 days** to deliver. Do NOT poll in a loop.
|
|
|
81
81
|
| GET | `/api/v1/database/leads?title=CTO&country=United%20States&hasEmail=true&limit=100` | Lead DB ($149/mo plan and up, 100k new leads/day). Optional: `excludeDelivered=true` (only leads you don't have yet), `after=0` then `pagination.next_after` (cursor paging). Company filters: `keywords`, `revenueMin`/`revenueMax`, `companyCountry`/`companyState`/`companyCity`. Exclusions: `notTitle`, `notKeywords`, `notIndustry`. `url=<people-search URL>` searches with that URL's filters. POST with a JSON body takes the same parameters (use it for a long `url`) |
|
|
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` |
|
|
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>` |
|
|
84
85
|
|
|
85
86
|
## Status Values
|
|
86
87
|
`RUNNING` → `SUCCEEDED` or `FAILED` or `CANCELLED`
|
package/bin/cli.mjs
CHANGED
|
@@ -441,6 +441,22 @@ async function main() {
|
|
|
441
441
|
break
|
|
442
442
|
}
|
|
443
443
|
|
|
444
|
+
// ── Plan: plain English -> database searches ($149/mo plan and up) ──
|
|
445
|
+
case 'plan': {
|
|
446
|
+
const database = flag('--database')
|
|
447
|
+
const query = args.join(' ').trim()
|
|
448
|
+
if (!query) die('Usage: scrapercity plan "dentists in Austin" [--database leads|local|ecommerce]')
|
|
449
|
+
const r = await sc.planSearch(query, typeof database === 'string' ? database : undefined)
|
|
450
|
+
if (r.targeting) console.log(r.targeting)
|
|
451
|
+
for (const s of r.searches || []) {
|
|
452
|
+
console.log(`\n${s.name} (${s.database}): ${Number(s.count || 0).toLocaleString()} matches`)
|
|
453
|
+
console.log(` ${s.apiEndpoint}: ${JSON.stringify(s.apiParams)}`)
|
|
454
|
+
console.log(` ${s.link}`)
|
|
455
|
+
}
|
|
456
|
+
if (!(r.searches || []).length) console.log('No search found for that. Try naming who or what you want.')
|
|
457
|
+
break
|
|
458
|
+
}
|
|
459
|
+
|
|
444
460
|
// ── Poll (convenience) ────────────────────────────────
|
|
445
461
|
case 'poll': {
|
|
446
462
|
if (!args[0]) die('Usage: scrapercity poll <runId> [--interval 15]')
|
|
@@ -491,6 +507,10 @@ ScraperCity CLI - B2B lead generation from your terminal
|
|
|
491
507
|
scrapercity logs <runId> View run logs
|
|
492
508
|
scrapercity runs [--hours 24] List recent runs
|
|
493
509
|
|
|
510
|
+
Plan ($149/mo plan and up):
|
|
511
|
+
scrapercity plan "dentists in Austin" Plain English -> ready-to-run database searches
|
|
512
|
+
--database leads|local|ecommerce Only this database (default: the ones that fit)
|
|
513
|
+
|
|
494
514
|
Database ($149/mo plan and up):
|
|
495
515
|
scrapercity db-leads [filters] Query lead database
|
|
496
516
|
--title --industry --country --state --city --company --domain --seniority --department
|
package/bin/mcp.mjs
CHANGED
|
@@ -48,6 +48,14 @@ const UTILITY_TOOLS = [
|
|
|
48
48
|
description: 'Cancel a running scraper job.',
|
|
49
49
|
inputSchema: { type: 'object', properties: { runId: { type: 'string', description: 'The run ID to cancel' } }, required: ['runId'] }
|
|
50
50
|
},
|
|
51
|
+
{
|
|
52
|
+
name: 'plan_search',
|
|
53
|
+
description: 'Turn a plain-English request into ready-to-run database searches. Describe who you want ("dentists in Austin", "websites that use Shopify", "CEOs of SaaS companies in Texas") or what you sell ("I sell life insurance"). Returns a few searches, each with its database, filters set to values that database really has, how many records match (leaving out what this account already has), a dashboard link, and apiParams to pass as they are to the matching query tool: query_lead_database for database/leads, query_local_business_database for database/local-businesses, query_ecommerce_database for database/ecommerce. Included with the $149/mo plan, like the database tools. Use it before a database query whenever you are not sure of the exact filter values.',
|
|
54
|
+
inputSchema: { type: 'object', properties: {
|
|
55
|
+
query: { type: 'string', description: 'What you are looking for or what you sell, in plain English' },
|
|
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
|
+
}, required: ['query'] }
|
|
58
|
+
},
|
|
51
59
|
{
|
|
52
60
|
name: 'query_lead_database',
|
|
53
61
|
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).',
|
|
@@ -166,6 +174,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
166
174
|
break
|
|
167
175
|
}
|
|
168
176
|
case 'cancel_run': result = await sc.cancel(args.runId); break
|
|
177
|
+
case 'plan_search': result = await sc.planSearch(args.query, args.database); break
|
|
169
178
|
case 'query_lead_database': {
|
|
170
179
|
const params = { ...args }
|
|
171
180
|
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-
|
|
3
|
+
// Generated: 2026-10-19T21:46:11.092Z
|
|
4
4
|
export const TOOLS = [
|
|
5
5
|
{
|
|
6
6
|
"name": "scrape_apollo",
|
|
@@ -661,5 +661,6 @@ export const ENDPOINTS = {
|
|
|
661
661
|
"database-enrichment": "/api/v1/enrich",
|
|
662
662
|
"database-local-business-enrichment": "/api/v1/enrich/local-businesses",
|
|
663
663
|
"database-local-businesses": "/api/v1/database/local-businesses",
|
|
664
|
-
"database-ecommerce": "/api/v1/database/ecommerce"
|
|
664
|
+
"database-ecommerce": "/api/v1/database/ecommerce",
|
|
665
|
+
"database-plan": "/api/v1/database/plan"
|
|
665
666
|
}
|
package/lib/client.mjs
CHANGED
|
@@ -208,6 +208,10 @@ export const dbEcommerce = (params) => {
|
|
|
208
208
|
return get(`${ENDPOINTS['database-ecommerce']}?${qs}`)
|
|
209
209
|
}
|
|
210
210
|
|
|
211
|
+
// Plain English -> ready-to-run database searches ($149/mo plan and up). database: 'leads' | 'local' | 'ecommerce', or leave it out.
|
|
212
|
+
export const planSearch = (query, database) =>
|
|
213
|
+
post('/api/v1/database/plan', database ? { query, database } : { query })
|
|
214
|
+
|
|
211
215
|
// ── Poll until done ───────────────────────────────────────────
|
|
212
216
|
export async function pollUntilDone(runId, { interval = 15000, timeout = 3600000, onStatus } = {}) {
|
|
213
217
|
const start = Date.now()
|