proxychecker-dev 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.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Flash AI Solutions (Jaylon Malone)
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,113 @@
1
+ # proxycheck
2
+
3
+ Fast **proxy checker** for the command line. Test HTTP, HTTPS, SOCKS4, and SOCKS5 proxies for liveness, latency, anonymity, and datacenter-vs-residential type. No signup, no ads, no sketchy binary.
4
+
5
+ Powered by [proxychecker.dev](https://proxychecker.dev).
6
+
7
+ ```bash
8
+ npx proxychecker-dev proxies.txt
9
+ ```
10
+
11
+ ```
12
+ ALIVE 45.12.30.9:8080 http datacenter 142ms US Cloudflare
13
+ ALIVE 98.162.25.7:31654 socks5 residential 380ms DE Deutsche Telekom
14
+ DEAD 9.9.9.9:3128 http - - - timeout
15
+
16
+ 2 alive · 1 dead · avg 261ms · 1 dc · 1 res
17
+ ```
18
+
19
+ ## Why
20
+
21
+ If you buy proxies for scraping, half the list is usually dead, slow, or a datacenter IP being sold as "residential." Checking them by hand or hacking together a `curl` loop is a waste of a morning. This does it in one command, tells you which are actually alive, how fast, and what type they really are.
22
+
23
+ ## Install
24
+
25
+ Run it with no install:
26
+
27
+ ```bash
28
+ npx proxychecker-dev proxies.txt
29
+ ```
30
+
31
+ Or install globally for the short `proxycheck` command:
32
+
33
+ ```bash
34
+ npm install -g proxychecker-dev
35
+ proxycheck proxies.txt
36
+ ```
37
+
38
+ Requires Node.js 18+.
39
+
40
+ ## Usage
41
+
42
+ ```bash
43
+ # from a file (one proxy per line, # comments ignored)
44
+ proxycheck proxies.txt
45
+
46
+ # inline
47
+ proxycheck 1.2.3.4:8080 socks5://5.6.7.8:1080
48
+
49
+ # from stdin
50
+ cat proxies.txt | proxycheck
51
+
52
+ # only the live ones, as JSON, piped to jq
53
+ proxycheck proxies.txt --alive --json | jq -r '.results[].input'
54
+
55
+ # export a CSV
56
+ proxycheck proxies.txt --csv > results.csv
57
+ ```
58
+
59
+ ### Accepted formats
60
+
61
+ One per line. Lines starting with `#` are ignored.
62
+
63
+ ```
64
+ ip:port
65
+ ip:port:user:pass
66
+ user:pass@ip:port
67
+ socks5://ip:port
68
+ ```
69
+
70
+ Protocols: `http`, `https`, `socks4`, `socks5`.
71
+
72
+ ### Options
73
+
74
+ | Flag | Description |
75
+ |------|-------------|
76
+ | `-k, --key <pck_...>` | API key for 5,000 proxies/call + auto-chunking (or set `PROXYCHECK_KEY`) |
77
+ | `-t, --timeout <ms>` | Per-proxy timeout, 3000-30000 (default 10000) |
78
+ | `-a, --alive` | Only output proxies that are alive |
79
+ | `--json` | Output raw JSON (pipe to `jq`) |
80
+ | `--csv` | Output CSV |
81
+ | `--fail-if-none` | Exit non-zero if zero proxies are alive (useful in CI) |
82
+ | `-h, --help` | Show help |
83
+ | `-v, --version` | Show version |
84
+
85
+ ## Free vs. keyed
86
+
87
+ The free tier checks **50 proxies per call** with no key and no signup. That covers casual use forever.
88
+
89
+ Got a big list? A key unlocks **5,000 proxies per call**, and the CLI automatically splits larger jobs into chunks and merges the results, so `proxycheck huge-list.txt --key pck_...` just works.
90
+
91
+ Keys are a one-time **$5** (5,000 proxies) or **$15/mo** for ongoing bulk + API access: [proxychecker.dev/pricing](https://proxychecker.dev/pricing). Set it once:
92
+
93
+ ```bash
94
+ export PROXYCHECK_KEY=pck_live_xxxxx
95
+ proxycheck huge-list.txt
96
+ ```
97
+
98
+ ## What you get per proxy
99
+
100
+ - **status** — alive or dead
101
+ - **protocol** — http / https / socks4 / socks5
102
+ - **type** — datacenter, residential, or mobile
103
+ - **latency** — round-trip in ms
104
+ - **country / city / ISP / ASN** — geo + network
105
+ - **suspected_fake_residential** — flags datacenter IPs being sold as residential
106
+
107
+ ## API
108
+
109
+ The CLI is a thin wrapper over the [proxychecker.dev API](https://proxychecker.dev/docs/api). POST a JSON array of proxies to `/api/check` with `Authorization: Bearer pck_...` and get JSON or CSV back. Use whichever you like.
110
+
111
+ ## License
112
+
113
+ MIT © Flash AI Solutions
@@ -0,0 +1,297 @@
1
+ #!/usr/bin/env node
2
+ // proxycheck - CLI for proxychecker.dev
3
+ // Zero runtime dependencies. Node >=18 (uses built-in fetch + util.parseArgs).
4
+ //
5
+ // Free tier: 50 proxies/call, no key needed.
6
+ // Paid tier: pass --key pck_... (or PROXYCHECK_KEY env) for 5,000/call + chunking.
7
+
8
+ import { parseArgs } from "node:util";
9
+ import { readFileSync, existsSync, statSync } from "node:fs";
10
+
11
+ const API_BASE = process.env.PROXYCHECK_API || "https://proxychecker.dev";
12
+ const FREE_CAP = 50; // server truncates free requests above this
13
+ const KEYED_CHUNK = 5000; // pro tier cap; we split larger jobs into 5k calls
14
+
15
+ // ---------- tiny ANSI color helpers (respect NO_COLOR + non-TTY pipes) ----------
16
+ const useColor = process.stdout.isTTY && !process.env.NO_COLOR;
17
+ const paint = (code) => (s) => (useColor ? `\x1b[${code}m${s}\x1b[0m` : String(s));
18
+ const green = paint(32), red = paint(31), dim = paint(90), bold = paint(1),
19
+ cyan = paint(36), yellow = paint(33);
20
+
21
+ const HELP = `
22
+ ${bold("proxycheck")} - test proxies for liveness, speed, and type ${dim("proxychecker.dev")}
23
+
24
+ ${bold("USAGE")}
25
+ proxycheck <file> check proxies from a file (one per line)
26
+ proxycheck <proxy> [proxy...] check proxies passed as arguments
27
+ cat proxies.txt | proxycheck check proxies from stdin
28
+
29
+ ${bold("OPTIONS")}
30
+ -k, --key <pck_...> API key for 5,000/call + auto-chunking (or PROXYCHECK_KEY)
31
+ -t, --timeout <ms> per-proxy timeout, 3000-30000 (default 10000)
32
+ -a, --alive only output proxies that are alive
33
+ --json output raw JSON (good for piping to jq)
34
+ --csv output CSV (input,status,protocol,type,latency,country,isp)
35
+ --fail-if-none exit non-zero if zero proxies are alive (for CI)
36
+ -h, --help show this help
37
+ -v, --version show version
38
+
39
+ ${bold("FORMATS ACCEPTED")} (one per line, # comments ignored)
40
+ ip:port socks5://ip:port
41
+ ip:port:user:pass user:pass@ip:port
42
+
43
+ ${bold("EXAMPLES")}
44
+ proxycheck proxies.txt
45
+ proxycheck 1.2.3.4:8080 socks5://5.6.7.8:1080 --alive
46
+ proxycheck big.txt --key pck_live_xxx --csv > results.csv
47
+ cat proxies.txt | proxycheck --alive --json | jq '.results[].input'
48
+
49
+ Free checks 50 proxies/call. Bulk + API access from $5: ${cyan("https://proxychecker.dev/pricing")}
50
+ `;
51
+
52
+ function fail(msg, code = 1) {
53
+ process.stderr.write(red("error: ") + msg + "\n");
54
+ process.exit(code);
55
+ }
56
+
57
+ // ---------- parse args ----------
58
+ let parsed;
59
+ try {
60
+ parsed = parseArgs({
61
+ allowPositionals: true,
62
+ options: {
63
+ key: { type: "string", short: "k" },
64
+ timeout: { type: "string", short: "t" },
65
+ alive: { type: "boolean", short: "a" },
66
+ json: { type: "boolean" },
67
+ csv: { type: "boolean" },
68
+ "fail-if-none": { type: "boolean" },
69
+ help: { type: "boolean", short: "h" },
70
+ version: { type: "boolean", short: "v" },
71
+ },
72
+ });
73
+ } catch (e) {
74
+ fail(e.message + "\nRun 'proxycheck --help' for usage.");
75
+ }
76
+ const { values: opts, positionals } = parsed;
77
+
78
+ if (opts.help) { process.stdout.write(HELP); process.exit(0); }
79
+ if (opts.version) {
80
+ // read our own version without importing json (keeps it ESM-simple)
81
+ try {
82
+ const pkg = JSON.parse(readFileSync(new URL("../package.json", import.meta.url), "utf8"));
83
+ process.stdout.write(pkg.version + "\n");
84
+ } catch { process.stdout.write("unknown\n"); }
85
+ process.exit(0);
86
+ }
87
+
88
+ const key = opts.key || process.env.PROXYCHECK_KEY || null;
89
+ const timeout = opts.timeout ? parseInt(opts.timeout, 10) : undefined;
90
+ if (opts.timeout && (!Number.isFinite(timeout) || timeout < 1000)) {
91
+ fail("--timeout must be a number of milliseconds (>= 1000)");
92
+ }
93
+
94
+ // ---------- gather proxy list from file / args / stdin ----------
95
+ function looksLikeProxy(s) {
96
+ return /[:@]/.test(s); // has a port or credentials separator
97
+ }
98
+
99
+ async function readStdin() {
100
+ const chunks = [];
101
+ for await (const chunk of process.stdin) chunks.push(chunk);
102
+ return Buffer.concat(chunks).toString("utf8");
103
+ }
104
+
105
+ function parseList(text) {
106
+ return text
107
+ .split(/\r?\n/)
108
+ .map((l) => l.trim())
109
+ .filter((l) => l && !l.startsWith("#"));
110
+ }
111
+
112
+ function isFile(p) {
113
+ try { return existsSync(p) && statSync(p).isFile(); }
114
+ catch { return false; }
115
+ }
116
+
117
+ async function gatherProxies() {
118
+ if (positionals.length) {
119
+ // A real file on disk wins (a Windows path like C:\list.txt also contains
120
+ // ':' so we can't rely on looksLikeProxy alone -- check the filesystem).
121
+ if (isFile(positionals[0])) {
122
+ return parseList(readFileSync(positionals[0], "utf8"));
123
+ }
124
+ // Otherwise treat the positionals as inline proxies.
125
+ if (positionals.every(looksLikeProxy)) {
126
+ return positionals;
127
+ }
128
+ fail(`'${positionals[0]}' is not a readable file or a valid proxy.\n` +
129
+ `Run 'proxycheck --help' for usage.`);
130
+ }
131
+ // Piped stdin (cat proxies.txt | proxycheck)
132
+ if (!process.stdin.isTTY) {
133
+ return parseList(await readStdin());
134
+ }
135
+ // Nothing given
136
+ process.stdout.write(HELP);
137
+ process.exit(0);
138
+ }
139
+
140
+ // ---------- API call (one chunk), with a single 429 backoff/retry ----------
141
+ // Throws on failure (caught by main). We avoid process.exit() here because a
142
+ // hard exit while an undici keep-alive socket is still closing trips a libuv
143
+ // assertion on Windows. Setting exitCode + draining the loop is the safe path.
144
+ async function checkChunk(proxies, attempt = 0) {
145
+ const headers = { "Content-Type": "application/json" };
146
+ if (key) headers["Authorization"] = "Bearer " + key;
147
+ let res;
148
+ try {
149
+ res = await fetch(API_BASE + "/api/check", {
150
+ method: "POST",
151
+ headers,
152
+ body: JSON.stringify(timeout ? { proxies, timeout } : { proxies }),
153
+ });
154
+ } catch (e) {
155
+ throw new Error(`network error reaching ${API_BASE}: ${e.message}`);
156
+ }
157
+ if (res.status === 429 && attempt < 3) {
158
+ process.stderr.write(dim(`rate limited, waiting 6s...\n`));
159
+ await new Promise((r) => setTimeout(r, 6000));
160
+ return checkChunk(proxies, attempt + 1);
161
+ }
162
+ if (!res.ok) {
163
+ const body = await res.text().catch(() => "");
164
+ let msg = `API returned ${res.status}`;
165
+ try { const j = JSON.parse(body); if (j.error) msg = j.error; } catch {}
166
+ throw new Error(msg);
167
+ }
168
+ return res.json();
169
+ }
170
+
171
+ // ---------- rendering ----------
172
+ function statusCell(r) {
173
+ if (r.status === "alive") return green("ALIVE");
174
+ return red("DEAD ");
175
+ }
176
+ function pad(s, n) {
177
+ s = String(s ?? "");
178
+ return s.length >= n ? s : s + " ".repeat(n - s.length);
179
+ }
180
+
181
+ function printTable(results) {
182
+ const rows = opts.alive ? results.filter((r) => r.status === "alive") : results;
183
+ if (!rows.length) {
184
+ process.stdout.write(dim("no proxies to show\n"));
185
+ return;
186
+ }
187
+ const wInput = Math.min(Math.max(...rows.map((r) => (r.input || "").length), 5), 32);
188
+ for (const r of rows) {
189
+ const type = r.status === "alive" ? (r.type || "unknown") : "-";
190
+ const lat = r.latency != null ? r.latency + "ms" : "-";
191
+ const geo = [r.country, r.isp].filter(Boolean).join(" ");
192
+ const fake = r.suspected_fake_residential ? yellow(" fake-res?") : "";
193
+ process.stdout.write(
194
+ ` ${statusCell(r)} ${pad(r.input, wInput)} ${dim(pad(r.protocol || "-", 6))} ` +
195
+ `${pad(type, 11)} ${pad(lat, 7)} ${dim(geo)}${fake}\n`
196
+ );
197
+ }
198
+ }
199
+
200
+ function printSummary(summary, truncated) {
201
+ const s = summary;
202
+ const parts = [
203
+ green(s.alive + " alive"),
204
+ red(s.dead + " dead"),
205
+ dim(`avg ${s.avg_latency_ms}ms`),
206
+ dim(`${s.datacenter} dc`),
207
+ dim(`${s.residential} res`),
208
+ ];
209
+ if (s.mobile) parts.push(dim(`${s.mobile} mobile`));
210
+ if (s.suspected_fake_residential) parts.push(yellow(`${s.suspected_fake_residential} fake-res`));
211
+ process.stdout.write("\n " + parts.join(dim(" · ")) + "\n");
212
+ if (truncated) {
213
+ process.stdout.write(
214
+ "\n " + yellow(`checked ${truncated.checked} of ${truncated.sent} (free tier cap).`) +
215
+ "\n " + `Unlock ${bold("5,000/call")} with a $5 one-time key -> ${cyan("https://proxychecker.dev/pricing")}\n`
216
+ );
217
+ }
218
+ }
219
+
220
+ function toCsv(results) {
221
+ const cols = ["input", "status", "protocol", "type", "latency", "country", "city", "isp", "asn", "suspected_fake_residential", "reason"];
222
+ const esc = (v) => {
223
+ if (v == null) return "";
224
+ const str = String(v);
225
+ return /[",\n]/.test(str) ? '"' + str.replace(/"/g, '""') + '"' : str;
226
+ };
227
+ const lines = [cols.join(",")];
228
+ for (const r of results) lines.push(cols.map((c) => esc(r[c])).join(","));
229
+ return lines.join("\n");
230
+ }
231
+
232
+ // ---------- main ----------
233
+ (async () => {
234
+ const proxies = await gatherProxies();
235
+ if (!proxies.length) fail("no valid proxies found in input");
236
+ try {
237
+
238
+ let merged = [];
239
+ let summaryAcc = null;
240
+ let truncated = null;
241
+
242
+ if (!key) {
243
+ // Free path: send only the first FREE_CAP (server would truncate anyway),
244
+ // but tell the user the true total so the upsell is honest.
245
+ const send = proxies.slice(0, FREE_CAP);
246
+ if (!opts.json && !opts.csv) {
247
+ process.stderr.write(dim(`proxychecker.dev · checking ${send.length} ` +
248
+ `${proxies.length > FREE_CAP ? `of ${proxies.length} ` : ""}proxies...\n\n`));
249
+ }
250
+ const data = await checkChunk(send);
251
+ merged = data.results;
252
+ summaryAcc = data.summary;
253
+ if (proxies.length > FREE_CAP) truncated = { sent: proxies.length, checked: send.length };
254
+ } else {
255
+ // Keyed path: split into 5k chunks and merge.
256
+ const chunks = [];
257
+ for (let i = 0; i < proxies.length; i += KEYED_CHUNK) chunks.push(proxies.slice(i, i + KEYED_CHUNK));
258
+ let done = 0;
259
+ for (const chunk of chunks) {
260
+ if (!opts.json && !opts.csv) {
261
+ process.stderr.write(dim(`\rproxychecker.dev · checking ${done + chunk.length}/${proxies.length}...`));
262
+ }
263
+ const data = await checkChunk(chunk);
264
+ merged.push(...data.results);
265
+ done += chunk.length;
266
+ if (!summaryAcc) summaryAcc = { ...data.summary };
267
+ else for (const k of Object.keys(data.summary)) summaryAcc[k] += data.summary[k];
268
+ }
269
+ if (summaryAcc && merged.length) {
270
+ const alive = merged.filter((r) => r.status === "alive");
271
+ summaryAcc.avg_latency_ms = alive.length
272
+ ? Math.round(alive.reduce((s, r) => s + (r.latency || 0), 0) / alive.length)
273
+ : 0;
274
+ }
275
+ if (!opts.json && !opts.csv) process.stderr.write("\n\n");
276
+ }
277
+
278
+ // ---------- output ----------
279
+ if (opts.csv) {
280
+ const rows = opts.alive ? merged.filter((r) => r.status === "alive") : merged;
281
+ process.stdout.write(toCsv(rows) + "\n");
282
+ } else if (opts.json) {
283
+ const rows = opts.alive ? merged.filter((r) => r.status === "alive") : merged;
284
+ process.stdout.write(JSON.stringify({ summary: summaryAcc, truncated, results: rows }, null, 2) + "\n");
285
+ } else {
286
+ printTable(merged);
287
+ printSummary(summaryAcc, truncated);
288
+ }
289
+
290
+ if (opts["fail-if-none"] && (!summaryAcc || summaryAcc.alive === 0)) process.exitCode = 2;
291
+ } catch (e) {
292
+ // Any post-network failure lands here: report and set a non-zero exit code,
293
+ // then let the event loop drain so we never hard-exit on a closing socket.
294
+ process.stderr.write(red("error: ") + e.message + "\n");
295
+ process.exitCode = 1;
296
+ }
297
+ })();
package/package.json ADDED
@@ -0,0 +1,45 @@
1
+ {
2
+ "name": "proxychecker-dev",
3
+ "version": "1.0.0",
4
+ "description": "Fast proxy checker CLI. Test HTTP/HTTPS/SOCKS proxies for liveness, latency, anonymity, and datacenter-vs-residential type. Free, no signup.",
5
+ "type": "module",
6
+ "bin": {
7
+ "proxycheck": "bin/proxycheck.js",
8
+ "proxychecker-dev": "bin/proxycheck.js"
9
+ },
10
+ "engines": {
11
+ "node": ">=18"
12
+ },
13
+ "keywords": [
14
+ "proxy",
15
+ "proxy-checker",
16
+ "proxy-tester",
17
+ "proxy-validation",
18
+ "socks5",
19
+ "socks4",
20
+ "http-proxy",
21
+ "https-proxy",
22
+ "scraping",
23
+ "web-scraping",
24
+ "proxies",
25
+ "cli",
26
+ "residential-proxy",
27
+ "datacenter-proxy"
28
+ ],
29
+ "author": "Flash AI Solutions",
30
+ "license": "MIT",
31
+ "homepage": "https://proxychecker.dev",
32
+ "repository": {
33
+ "type": "git",
34
+ "url": "git+https://github.com/iFan6oy/proxychecker-cli.git"
35
+ },
36
+ "bugs": {
37
+ "url": "https://github.com/iFan6oy/proxychecker-cli/issues"
38
+ },
39
+ "files": [
40
+ "bin",
41
+ "README.md",
42
+ "LICENSE"
43
+ ],
44
+ "dependencies": {}
45
+ }