enrich-companies 1.0.0

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/README.md +111 -0
  2. package/index.js +279 -0
  3. package/package.json +36 -0
package/README.md ADDED
@@ -0,0 +1,111 @@
1
+ # enrich-csv
2
+
3
+ Enrich a CSV of companies with revenue, credit score, employees, and financial data. Powered by the [Score API](https://score.get-scala.com/api) — 272M+ companies from 265 countries.
4
+
5
+ **The Clearbit / ZoomInfo / D&B alternative that costs €0 to start.**
6
+
7
+ ## Quick Start
8
+
9
+ ```bash
10
+ npx enrich-csv leads.csv -o enriched.csv
11
+ ```
12
+
13
+ That's it. No signup needed. Free tier: 50 lookups/month.
14
+
15
+ ## Installation
16
+
17
+ ```bash
18
+ npm install -g enrich-csv
19
+ ```
20
+
21
+ Or use directly with `npx` (no install needed):
22
+
23
+ ```bash
24
+ npx enrich-csv input.csv -o output.csv
25
+ ```
26
+
27
+ ## Usage
28
+
29
+ ```bash
30
+ # Basic: enrich a CSV file
31
+ enrich-csv companies.csv -o enriched.csv
32
+
33
+ # Filter by country
34
+ enrich-csv leads.csv --country IT -o italian-leads.csv
35
+
36
+ # Specify which column contains company names
37
+ enrich-csv data.csv --column "Company Name" -o enriched.csv
38
+
39
+ # Choose which fields to add
40
+ enrich-csv leads.csv --fields revenue,score,grade,employees -o enriched.csv
41
+
42
+ # Use with API key for higher limits
43
+ enrich-csv leads.csv -k sk_live_your_key -o enriched.csv
44
+
45
+ # Pipe from stdin
46
+ cat companies.txt | enrich-csv - -o results.csv
47
+
48
+ # Dry run (see what would be enriched)
49
+ enrich-csv leads.csv --dry-run
50
+ ```
51
+
52
+ ## What Gets Added
53
+
54
+ For each company in your CSV, enrich-csv adds:
55
+
56
+ | Field | Description |
57
+ |-------|-------------|
58
+ | `score_revenue` | Annual revenue (EUR) |
59
+ | `score_employees` | Employee count |
60
+ | `score_score` | Credit score 0-100 |
61
+ | `score_grade` | Letter grade (AA to E) |
62
+ | `score_country` | ISO country code |
63
+ | `score_city` | City |
64
+ | `score_sector_desc` | Industry description |
65
+
66
+ ## Example
67
+
68
+ **Input (leads.csv):**
69
+ ```csv
70
+ Company,Contact,Email
71
+ Tesla,John,john@tesla.com
72
+ Ferrero,Maria,maria@ferrero.com
73
+ SAP,Hans,hans@sap.com
74
+ ```
75
+
76
+ **Output (enriched.csv):**
77
+ ```csv
78
+ Company,Contact,Email,score_revenue,score_employees,score_score,score_grade,score_country,score_city,score_sector_desc
79
+ Tesla,John,john@tesla.com,96773000000,140000,85,A,US,Austin,Motor Vehicle Manufacturing
80
+ Ferrero,Maria,maria@ferrero.com,19300000000,48697,75,BBB,IT,Alba,Food Manufacturing
81
+ SAP,Hans,hans@sap.com,35000000000,107000,88,A,DE,Walldorf,Software Publishing
82
+ ```
83
+
84
+ ## API Pricing
85
+
86
+ | Plan | Lookups/month | Price |
87
+ |------|--------------|-------|
88
+ | Free | 50 | €0 |
89
+ | Starter | 500 | €19/mo |
90
+ | Growth | 5,000 | €49/mo |
91
+ | Enterprise | 50,000 | €149/mo |
92
+
93
+ Get your free API key: https://score.get-scala.com/api/free-key
94
+
95
+ ## Data Coverage
96
+
97
+ 272,116,630 companies across 265 countries. Top coverage:
98
+
99
+ 🇧🇷 Brazil 47M · 🇺🇸 USA 39M · 🇦🇺 Australia 20M · 🇫🇷 France 17M · 🇬🇧 UK 14M · 🇩🇪 Germany 10M · 🇮🇳 India 8M · 🇯🇵 Japan 7M · 🇮🇹 Italy 6M · 🇪🇸 Spain 5M
100
+
101
+ ## Related
102
+
103
+ - [Score API](https://score.get-scala.com/api) — Full REST API
104
+ - [scala-score](https://www.npmjs.com/package/scala-score) — Node.js SDK
105
+ - [scala-mcp-server](https://www.npmjs.com/package/scala-mcp-server) — MCP server for AI agents
106
+ - [Dataset on Kaggle](https://www.kaggle.com/datasets/yorkiealfbroth/global-company-data-994k) — Free 994K sample
107
+ - [Dataset on HuggingFace](https://huggingface.co/datasets/alf1990mi/global-company-database-1m) — Free 1M sample
108
+
109
+ ## License
110
+
111
+ MIT — Built by [SCALA AI](https://get-scala.com)
package/index.js ADDED
@@ -0,0 +1,279 @@
1
+ #!/usr/bin/env node
2
+
3
+ const fs = require('fs');
4
+ const https = require('https');
5
+ const path = require('path');
6
+
7
+ const VERSION = '1.0.0';
8
+ const API_BASE = 'https://score.get-scala.com/api/search';
9
+ const RATE_LIMIT_MS = 200;
10
+
11
+ function usage() {
12
+ console.log(`
13
+ enrich-csv v${VERSION} — Enrich company data from 272M+ businesses
14
+
15
+ USAGE:
16
+ enrich-csv input.csv [options]
17
+ cat companies.csv | enrich-csv - [options]
18
+
19
+ OPTIONS:
20
+ -o, --output FILE Output file (default: stdout)
21
+ -k, --key KEY Score API key (or set SCORE_API_KEY env var)
22
+ -c, --column NAME Column name containing company names (default: auto-detect)
23
+ --country CC Filter by ISO country code (e.g., IT, US, DE)
24
+ --delimiter CHAR CSV delimiter (default: auto-detect , or ;)
25
+ --limit N Max results per lookup (default: 1)
26
+ --fields FIELDS Fields to add (default: revenue,employees,score,grade,country,city,sector_desc)
27
+ --dry-run Show what would be enriched without calling API
28
+ -h, --help Show this help
29
+ -v, --version Show version
30
+
31
+ EXAMPLES:
32
+ enrich-csv leads.csv -o enriched.csv
33
+ enrich-csv leads.csv --country IT --key sk_live_xxx
34
+ enrich-csv leads.csv --column "Company Name" --fields revenue,score,grade
35
+ cat names.txt | enrich-csv - -o results.csv
36
+
37
+ FREE TIER: 50 lookups/month (no key needed)
38
+ Get API key: https://score.get-scala.com/api/free-key
39
+
40
+ PRICING:
41
+ Free 50 lookups/month €0
42
+ Starter 500 lookups/month €19/mo
43
+ Growth 5,000 lookups/month €49/mo
44
+ Enterprise 50,000 lookups/mo €149/mo
45
+
46
+ More info: https://score.get-scala.com/api
47
+ `);
48
+ }
49
+
50
+ function parseArgs() {
51
+ const args = process.argv.slice(2);
52
+ const opts = {
53
+ input: null,
54
+ output: null,
55
+ key: process.env.SCORE_API_KEY || null,
56
+ column: null,
57
+ country: null,
58
+ delimiter: null,
59
+ limit: 1,
60
+ fields: ['revenue', 'employees', 'score', 'grade', 'country', 'city', 'sector_desc'],
61
+ dryRun: false,
62
+ };
63
+
64
+ for (let i = 0; i < args.length; i++) {
65
+ const a = args[i];
66
+ if (a === '-h' || a === '--help') { usage(); process.exit(0); }
67
+ if (a === '-v' || a === '--version') { console.log(VERSION); process.exit(0); }
68
+ if (a === '-o' || a === '--output') { opts.output = args[++i]; continue; }
69
+ if (a === '-k' || a === '--key') { opts.key = args[++i]; continue; }
70
+ if (a === '-c' || a === '--column') { opts.column = args[++i]; continue; }
71
+ if (a === '--country') { opts.country = args[++i]; continue; }
72
+ if (a === '--delimiter') { opts.delimiter = args[++i]; continue; }
73
+ if (a === '--limit') { opts.limit = parseInt(args[++i], 10); continue; }
74
+ if (a === '--fields') { opts.fields = args[++i].split(','); continue; }
75
+ if (a === '--dry-run') { opts.dryRun = true; continue; }
76
+ if (!opts.input) { opts.input = a; continue; }
77
+ }
78
+
79
+ return opts;
80
+ }
81
+
82
+ function detectDelimiter(line) {
83
+ const semicolons = (line.match(/;/g) || []).length;
84
+ const commas = (line.match(/,/g) || []).length;
85
+ const tabs = (line.match(/\t/g) || []).length;
86
+ if (tabs > commas && tabs > semicolons) return '\t';
87
+ if (semicolons > commas) return ';';
88
+ return ',';
89
+ }
90
+
91
+ function parseCSVLine(line, delimiter) {
92
+ const fields = [];
93
+ let current = '';
94
+ let inQuotes = false;
95
+
96
+ for (let i = 0; i < line.length; i++) {
97
+ const c = line[i];
98
+ if (c === '"') {
99
+ if (inQuotes && line[i + 1] === '"') { current += '"'; i++; }
100
+ else { inQuotes = !inQuotes; }
101
+ } else if (c === delimiter && !inQuotes) {
102
+ fields.push(current.trim());
103
+ current = '';
104
+ } else {
105
+ current += c;
106
+ }
107
+ }
108
+ fields.push(current.trim());
109
+ return fields;
110
+ }
111
+
112
+ function escapeCSV(val, delimiter) {
113
+ if (val == null) return '';
114
+ const s = String(val);
115
+ if (s.includes(delimiter) || s.includes('"') || s.includes('\n')) {
116
+ return '"' + s.replace(/"/g, '""') + '"';
117
+ }
118
+ return s;
119
+ }
120
+
121
+ function detectNameColumn(headers) {
122
+ const patterns = [
123
+ /^company$/i, /^company.?name$/i, /^name$/i, /^business$/i,
124
+ /^business.?name$/i, /^organization$/i, /^org$/i, /^azienda$/i,
125
+ /^ragione.?sociale$/i, /^empresa$/i, /^entreprise$/i, /^firma$/i,
126
+ /^unternehmen$/i, /^nome$/i, /^denominazione$/i,
127
+ ];
128
+
129
+ for (const p of patterns) {
130
+ const idx = headers.findIndex(h => p.test(h));
131
+ if (idx >= 0) return idx;
132
+ }
133
+ return 0;
134
+ }
135
+
136
+ function apiSearch(query, country, key, limit) {
137
+ return new Promise((resolve, reject) => {
138
+ let url = `${API_BASE}?q=${encodeURIComponent(query)}&limit=${limit}`;
139
+ if (country) url += `&country=${country}`;
140
+ if (key) url += `&key=${key}`;
141
+
142
+ https.get(url, { headers: { 'User-Agent': `enrich-csv/${VERSION}` } }, (res) => {
143
+ let data = '';
144
+ res.on('data', c => data += c);
145
+ res.on('end', () => {
146
+ try {
147
+ const json = JSON.parse(data);
148
+ if (json.results && json.results.length > 0) {
149
+ resolve(json.results[0]);
150
+ } else {
151
+ resolve(null);
152
+ }
153
+ } catch {
154
+ resolve(null);
155
+ }
156
+ });
157
+ }).on('error', () => resolve(null));
158
+ });
159
+ }
160
+
161
+ function sleep(ms) { return new Promise(r => setTimeout(r, ms)); }
162
+
163
+ function formatRevenue(val) {
164
+ if (!val || val === 0) return '';
165
+ if (val >= 1e9) return `€${(val / 1e9).toFixed(1)}B`;
166
+ if (val >= 1e6) return `€${(val / 1e6).toFixed(1)}M`;
167
+ if (val >= 1e3) return `€${(val / 1e3).toFixed(0)}K`;
168
+ return `€${val}`;
169
+ }
170
+
171
+ async function main() {
172
+ const opts = parseArgs();
173
+
174
+ if (!opts.input) {
175
+ console.error('Error: no input file specified. Use --help for usage.');
176
+ process.exit(1);
177
+ }
178
+
179
+ let inputData;
180
+ if (opts.input === '-') {
181
+ const chunks = [];
182
+ for await (const chunk of process.stdin) chunks.push(chunk);
183
+ inputData = Buffer.concat(chunks).toString('utf-8');
184
+ } else {
185
+ if (!fs.existsSync(opts.input)) {
186
+ console.error(`Error: file not found: ${opts.input}`);
187
+ process.exit(1);
188
+ }
189
+ inputData = fs.readFileSync(opts.input, 'utf-8');
190
+ }
191
+
192
+ const lines = inputData.split('\n').filter(l => l.trim());
193
+ if (lines.length < 2) {
194
+ console.error('Error: CSV must have a header row and at least one data row.');
195
+ process.exit(1);
196
+ }
197
+
198
+ const delimiter = opts.delimiter || detectDelimiter(lines[0]);
199
+ const headers = parseCSVLine(lines[0], delimiter);
200
+ const nameColIdx = opts.column
201
+ ? headers.findIndex(h => h.toLowerCase() === opts.column.toLowerCase())
202
+ : detectNameColumn(headers);
203
+
204
+ if (nameColIdx < 0) {
205
+ console.error(`Error: column "${opts.column}" not found. Available: ${headers.join(', ')}`);
206
+ process.exit(1);
207
+ }
208
+
209
+ const enrichedHeaders = [...headers, ...opts.fields.map(f => `score_${f}`)];
210
+ const outputLines = [enrichedHeaders.map(h => escapeCSV(h, delimiter)).join(delimiter)];
211
+
212
+ const total = lines.length - 1;
213
+ let enriched = 0;
214
+ let notFound = 0;
215
+
216
+ process.stderr.write(`\nEnriching ${total} companies from column "${headers[nameColIdx]}"...\n`);
217
+ if (!opts.key) {
218
+ process.stderr.write('No API key — using free tier (50 lookups/month)\n');
219
+ process.stderr.write('Get unlimited: https://score.get-scala.com/api/free-key\n');
220
+ }
221
+ process.stderr.write('\n');
222
+
223
+ for (let i = 1; i < lines.length; i++) {
224
+ const row = parseCSVLine(lines[i], delimiter);
225
+ const companyName = row[nameColIdx];
226
+
227
+ if (!companyName || !companyName.trim()) {
228
+ const emptyRow = [...row, ...opts.fields.map(() => '')];
229
+ outputLines.push(emptyRow.map(v => escapeCSV(v, delimiter)).join(delimiter));
230
+ continue;
231
+ }
232
+
233
+ process.stderr.write(` [${i}/${total}] ${companyName.substring(0, 40).padEnd(40)} `);
234
+
235
+ if (opts.dryRun) {
236
+ process.stderr.write('(dry run)\n');
237
+ const emptyRow = [...row, ...opts.fields.map(() => '')];
238
+ outputLines.push(emptyRow.map(v => escapeCSV(v, delimiter)).join(delimiter));
239
+ continue;
240
+ }
241
+
242
+ const result = await apiSearch(companyName, opts.country, opts.key, opts.limit);
243
+
244
+ if (result) {
245
+ enriched++;
246
+ const enrichedValues = opts.fields.map(f => {
247
+ if (f === 'revenue') return result.revenue || '';
248
+ return result[f] != null ? result[f] : '';
249
+ });
250
+ const enrichedRow = [...row, ...enrichedValues];
251
+ outputLines.push(enrichedRow.map(v => escapeCSV(v, delimiter)).join(delimiter));
252
+ process.stderr.write(`✓ ${result.name} (${result.country}) rev=${formatRevenue(result.revenue)}\n`);
253
+ } else {
254
+ notFound++;
255
+ const emptyRow = [...row, ...opts.fields.map(() => '')];
256
+ outputLines.push(emptyRow.map(v => escapeCSV(v, delimiter)).join(delimiter));
257
+ process.stderr.write('✗ not found\n');
258
+ }
259
+
260
+ await sleep(RATE_LIMIT_MS);
261
+ }
262
+
263
+ const output = outputLines.join('\n') + '\n';
264
+
265
+ if (opts.output) {
266
+ fs.writeFileSync(opts.output, output, 'utf-8');
267
+ process.stderr.write(`\nDone! ${enriched}/${total} enriched, ${notFound} not found.\n`);
268
+ process.stderr.write(`Output: ${opts.output}\n`);
269
+ } else {
270
+ process.stdout.write(output);
271
+ process.stderr.write(`\n${enriched}/${total} enriched, ${notFound} not found.\n`);
272
+ }
273
+
274
+ process.stderr.write(`\n--- Score API by SCALA AI ---\n`);
275
+ process.stderr.write(`272M+ companies | 265 countries | Free tier available\n`);
276
+ process.stderr.write(`https://score.get-scala.com/api\n\n`);
277
+ }
278
+
279
+ main().catch(e => { console.error(`Fatal: ${e.message}`); process.exit(1); });
package/package.json ADDED
@@ -0,0 +1,36 @@
1
+ {
2
+ "name": "enrich-companies",
3
+ "version": "1.0.0",
4
+ "description": "Enrich a CSV of companies with revenue, credit score, employees, and financial data from 272M+ companies. Free tier: 50 lookups/month.",
5
+ "main": "index.js",
6
+ "bin": {
7
+ "enrich-companies": "./index.js"
8
+ },
9
+ "keywords": [
10
+ "csv",
11
+ "enrichment",
12
+ "company data",
13
+ "business intelligence",
14
+ "lead enrichment",
15
+ "crm",
16
+ "revenue data",
17
+ "credit score",
18
+ "dun and bradstreet alternative",
19
+ "zoominfo alternative",
20
+ "clearbit alternative",
21
+ "data enrichment",
22
+ "b2b data",
23
+ "company lookup",
24
+ "sales intelligence"
25
+ ],
26
+ "author": "SCALA AI <ale@get-scala.com>",
27
+ "license": "MIT",
28
+ "homepage": "https://score.get-scala.com/api",
29
+ "repository": {
30
+ "type": "git",
31
+ "url": "https://github.com/Alessandro114/enrich-csv"
32
+ },
33
+ "engines": {
34
+ "node": ">=16"
35
+ }
36
+ }