/** * Options shared across all SDK modules. Passed to the top-level client; each * module reads only the fields it cares about. Adding a new field here is a * minor version bump; removing one is a major. */ interface ClientOptions { /** * Bearer API key for paid endpoints (residential, datacenter, etc.). * The free module ignores this — it works without any auth. */ apiKey?: string; /** * Override the API base URL. Defaults to https://api.proxyscrape.com/v4 . * Mostly useful for staging environments and integration tests. */ baseUrl?: string; /** * Custom fetch implementation. Defaults to globalThis.fetch which is * available natively in Node 18+, Bun, Deno, and every modern browser. * Override to plug in undici, mocks, or instrumented fetches. */ fetch?: typeof fetch; /** * Per-request timeout in milliseconds. Caller-provided AbortSignals on * individual calls override this. Defaults to 30s. */ timeoutMs?: number; /** * Maximum number of retry attempts on 5xx / 429 / transient network * errors. Defaults to 3 (so 4 total tries: original + 3 retries). * Set to 0 to disable retries. */ maxRetries?: number; /** * User-Agent string appended after our own. Useful for identifying your * app in upstream logs. The final UA will look like: * `proxyscrape-sdk/1.0.3 (node-fetch) ` */ userAgent?: string; } type Protocol = 'http' | 'https' | 'socks4' | 'socks5'; type Anonymity = 'transparent' | 'anonymous' | 'elite'; interface RequestOptions { /** Override the request-level timeout. Falls back to ClientOptions.timeoutMs. */ timeoutMs?: number; /** AbortSignal that lets the caller cancel mid-flight. */ signal?: AbortSignal; /** Extra headers merged into the default set. */ headers?: Record; /** * Skip injection of the `api-token` header. * * Used when the request targets a third-party endpoint (e.g. ipinfo.io * for `getPublicIp`) where we must NOT leak the caller's API key. * Defaults to false; module code should leave it false for any call * hitting api.proxyscrape.com. */ skipAuth?: boolean; } interface RetryableRequest { url: string | URL; init?: RequestInit; /** If false, never retry — used for endpoints where idempotency is unclear. */ retry?: boolean; } declare class HttpClient { private readonly baseUrl; private readonly apiKey; private readonly fetchImpl; private readonly timeoutMs; private readonly maxRetries; private readonly userAgent; constructor(opts?: ClientOptions); /** Resolve a path against the base URL. Pass through absolute URLs as-is. */ url(path: string, query?: Record): URL; /** Issue a request, parse JSON, and return the decoded body. */ getJson(path: string, query?: Record, opts?: RequestOptions): Promise; /** Issue a request and return the raw text body — used for the text/csv export endpoints. */ getText(path: string, query?: Record, opts?: RequestOptions): Promise; /** * Low-level request: handles timeout, retries, headers, error mapping. * Most module code should call getJson / getText instead. */ request(req: RetryableRequest, opts?: RequestOptions): Promise; } /** * Stub for the proxy-checker module. Real implementation lands once the * v4 /proxy-check endpoints are stable. Public methods listed here are * intentional commitments: they will be the surface in v1.x, only their * bodies fill in. * * Until then every call throws so accidental usage is loud, not silent. */ declare class CheckerClient { protected readonly http: HttpClient; constructor(http: HttpClient); /** Submit a batch of proxies to be checked against the public judge servers. */ test(_params: { proxies: string[]; }): Promise; /** Quick single-proxy liveness ping. */ ping(_params: { proxy: string; }): Promise; } /** * Stub for the datacenter proxies module. Real implementation lands once * the v4 endpoints are stable. Public methods are an intentional commitment * to the v1.x surface — only the bodies fill in. * * The HttpClient reference is held even though the stub methods don't use * it yet — that way the constructor isn't "useless" (it really will be * needed) and the eventual implementation slots in without touching the * constructor signature or callers. */ declare class DatacenterClient { protected readonly http: HttpClient; constructor(http: HttpClient); acquire(_params?: Record): Promise; release(_params?: Record): Promise; list(_params?: Record): Promise; } /** * Stub for the dedicated proxies module. Real implementation lands once * the v4 endpoints are stable. Public methods are an intentional commitment * to the v1.x surface — only the bodies fill in. * * The HttpClient reference is held even though the stub methods don't use * it yet — that way the constructor isn't "useless" (it really will be * needed) and the eventual implementation slots in without touching the * constructor signature or callers. */ declare class DedicatedClient { protected readonly http: HttpClient; constructor(http: HttpClient); acquire(_params?: Record): Promise; release(_params?: Record): Promise; list(_params?: Record): Promise; } /** * Public, flattened proxy shape the SDK exposes. The upstream API returns * a richer object with timestamps and check history — the parts most callers * actually need are surfaced as flat fields, and the full upstream record * is exposed via `raw` for callers who want it. */ interface FreeProxy { protocol: Protocol; ip: string; port: number; /** As `protocol://ip:port` — convenient for direct use with HTTP clients. */ url: string; /** ISO-3166 alpha-2, uppercase ("US", "DE", ...). May be empty if geo lookup failed. */ countryCode: string; /** Display name ("United States", "Germany", ...). Matches countryCode. */ country: string; /** Best-effort city. May be empty. */ city: string; anonymity: Anonymity | 'unknown'; /** True when an HTTP proxy supports CONNECT for HTTPS tunnelling. SOCKS always reports true. */ ssl: boolean; /** 0–100, rounded to 2dp. May be null when the checker hasn't run enough times yet. */ uptimePercent: number | null; /** "AS131293" form — empty if upstream didn't classify. */ asn: string; /** ISP / organisation name from the geolocation lookup. May be empty. */ isp: string; /** Single-check latency in ms. */ latencyMs: number | null; /** ISO-8601 string of the most recent successful check. */ lastCheckedAt: string | null; /** The full upstream record, exposed unchanged for callers who need fields we don't flatten. */ raw: FreeProxyRaw; } /** Filters applied to the list / stream / random calls. All optional. */ interface FreeProxyFilters { protocol?: Protocol | Protocol[]; /** ISO-3166 alpha-2 — case-insensitive ("us" or "US" both work). Comma-separated list also accepted. */ country?: string | string[]; anonymity?: Anonymity | Anonymity[]; /** Filter to proxies whose timeout is ≤ this value (ms). */ maxTimeoutMs?: number; /** Filter to proxies with uptime ≥ this value (0–100). */ minUptimePercent?: number; /** Filter to HTTP proxies with `ssl=true` (i.e. HTTPS-tunnel-capable). */ httpsOnly?: boolean; /** Specific port(s). */ port?: number | number[]; } interface FreeProxyListParams extends FreeProxyFilters { /** Page size cap. The API max is 2000. */ limit?: number; /** Pagination offset. */ skip?: number; } interface FreeProxyListResult { proxies: FreeProxy[]; shownRecords: number; totalRecords: number; hasMore: boolean; } /** * Raw upstream proxy shape. Names match the API response 1:1 so callers can * grep the docs and find these fields. We intentionally don't camelCase * these — they're a pass-through of the wire format. */ interface FreeProxyRaw { alive: boolean; alive_since: number; anonymity: string; average_timeout: number; first_seen: number; ip_data: { as?: string; asname?: string; city?: string; continent?: string; continentCode?: string; country?: string; countryCode?: string; hosting?: boolean; isp?: string; lat?: number; lon?: number; mobile?: boolean; org?: string; proxy?: boolean; regionName?: string; status?: string; timezone?: string; zip?: string; }; ip_data_last_update: number; last_seen: number; port: number; protocol: string; proxy: string; ssl: boolean; timeout: number; times_alive: number; times_dead: number; uptime: number; ip: string; } /** Raw API response shape for `request=get_proxies&format=json`. */ interface FreeProxyApiResponse { shown_records: number; total_records: number; limit: number; skip: number; nextpage: boolean; proxies: FreeProxyRaw[]; } /** * Wraps the v4 free-proxy-list endpoints. No auth needed — works against * https://api.proxyscrape.com/v4/free-proxy-list/get . * * Three shapes: * - list(params) — one page, returns an array + pagination metadata * - stream(params) — async iterator that pages through everything * - random(params) — convenience for "give me one good proxy" * * The text and CSV export endpoints are exposed via `listText` / `listCsv` * for callers who don't want to round-trip through JSON. */ declare class FreeProxyListClient { private readonly http; private static readonly PATH; private static readonly MAX_PAGE_SIZE; constructor(http: HttpClient); /** Fetch a single page of proxies. */ list(params?: FreeProxyListParams, opts?: RequestOptions): Promise; /** * Iterate every matching proxy across all pages. Stops automatically when * the API reports `nextpage: false`. Yields one proxy at a time so callers * can break out early without fetching the whole pool. */ stream(params?: FreeProxyFilters, opts?: RequestOptions): AsyncGenerator; /** * Return a single proxy matching the filters, or null if no matches exist. * Picks the first one the API returns — combine with filters (uptime, * country, anonymity) to narrow the pool before calling. */ random(params?: FreeProxyFilters, opts?: RequestOptions): Promise; /** * Plain-text export — one line per proxy as `protocol://ip:port`. Hands * back the raw body string so callers can pipe it straight to a file or * feed it line-by-line into another tool. */ listText(params?: FreeProxyFilters, opts?: RequestOptions): Promise; /** CSV export — full per-proxy fields, header row included. */ listCsv(params?: FreeProxyFilters, opts?: RequestOptions): Promise; private fetchPage; } /** * Normalized public-IP lookup result. Fields beyond `ip` are best-effort — * any of them may be absent if the underlying provider doesn't return them * or if the IP didn't resolve to a known location. */ interface PublicIpInfo { /** The caller's public IPv4 or IPv6 address. Always present. */ ip: string; /** ISO-3166 alpha-2 country code (`US`, `DE`, ...). */ country?: string; /** Region / state / province name. */ region?: string; /** City name. */ city?: string; /** `","` (ipinfo's native format). */ loc?: string; /** Latitude as a number — derived from `loc` when available. */ latitude?: number; /** Longitude as a number — derived from `loc` when available. */ longitude?: number; /** AS organisation / ISP name. May be empty. */ org?: string; /** Postal / ZIP code, when the provider returns one. */ postal?: string; /** IANA timezone (`Europe/Brussels`, `America/Los_Angeles`, ...). */ timezone?: string; /** The raw upstream response, untouched, for callers who need fields we didn't surface. */ raw: Record; } /** * Which public-IP service to use. Defaults to `ipinfo` — its free tier * (50k req/month, no key) is sufficient for casual SDK use. For production * volumes, pass an `ipinfoToken` or override `endpoint` entirely. */ type PublicIpService = 'ipinfo' | 'ipify'; interface GetPublicIpOptions { /** * Which lookup provider to use. Default: `ipinfo`. * - `ipinfo` → returns IP + city/region/country/org/timezone * - `ipify` → returns IP only (lightweight; no geolocation) */ service?: PublicIpService; /** * Override the endpoint URL entirely. Useful for plugging in your own * IP-echo service or pointing at a paid provider you already use. * If set, `service` is ignored. */ endpoint?: string; /** * ipinfo access token. Without one you're on the anonymous 50k req/month * tier; with one you get your account's quota. Ignored for `ipify`. */ ipinfoToken?: string; } /** * Public-IP lookup helper. Wraps third-party IP-echo services behind a * single normalised response shape. Lives on the top-level `ProxyScrape` * client (as `.getPublicIp()`) because it doesn't fit cleanly inside any * one product module — it's an environmental fact about the caller, not * a free-list or checker or residential concern. * * Two providers are baked in: * - ipinfo.io → IP + geo + ASN, no key needed up to 50k req/month * - api.ipify → IP only, unlimited, no rate limits worth thinking about * * Anyone hitting our SDK at production volume should either pass an * `ipinfoToken` (their own ipinfo account) or override `endpoint` with * their own infrastructure. We don't proxy these calls through ProxyScrape * — they go directly from the caller to the chosen provider. * * The HttpClient is used with `skipAuth: true` so the caller's ProxyScrape * API key never leaves proxyscrape.com. */ declare class PublicIpClient { private readonly http; constructor(http: HttpClient); get(opts?: GetPublicIpOptions): Promise; } /** * Stub for the mobile proxies module. Real implementation lands once * the v4 endpoints are stable. Public methods are an intentional commitment * to the v1.x surface — only the bodies fill in. * * The HttpClient reference is held even though the stub methods don't use * it yet — that way the constructor isn't "useless" (it really will be * needed) and the eventual implementation slots in without touching the * constructor signature or callers. */ declare class MobileClient { protected readonly http: HttpClient; constructor(http: HttpClient); acquire(_params?: Record): Promise; release(_params?: Record): Promise; list(_params?: Record): Promise; } /** * Stub for the residential proxies module. Real implementation lands once * the v4 endpoints are stable. Public methods are an intentional commitment * to the v1.x surface — only the bodies fill in. * * The HttpClient reference is held even though the stub methods don't use * it yet — that way the constructor isn't "useless" (it really will be * needed) and the eventual implementation slots in without touching the * constructor signature or callers. */ declare class ResidentialClient { protected readonly http: HttpClient; constructor(http: HttpClient); acquire(_params?: Record): Promise; release(_params?: Record): Promise; listSessions(_params?: Record): Promise; } /** * Stub for the serp proxies module. Real implementation lands once * the v4 endpoints are stable. Public methods are an intentional commitment * to the v1.x surface — only the bodies fill in. * * The HttpClient reference is held even though the stub methods don't use * it yet — that way the constructor isn't "useless" (it really will be * needed) and the eventual implementation slots in without touching the * constructor signature or callers. */ declare class SerpClient { protected readonly http: HttpClient; constructor(http: HttpClient); search(_params?: Record): Promise; raw(_params?: Record): Promise; } /** * The single public class users import. Holds a configured HTTP client and * exposes each product as a namespace on the instance: * * const ps = new ProxyScrape(); // free tier — no auth * await ps.free.list({ protocol: 'socks5' }); * * const ps = new ProxyScrape({ apiKey }); * await ps.residential.acquire({ ... }); // throws until implemented * * Adding a new product later means: * 1. Create src/modules//{index.ts,client.ts,types.ts} * 2. Add the field below * 3. No change to the public API contract — existing callers are unaffected. */ declare class ProxyScrape { /** Free, unauthenticated proxy list. v0.1 — implemented. */ readonly free: FreeProxyListClient; /** Proxy checker. v0.1 — stub, throws on use. */ readonly checker: CheckerClient; /** Residential proxy pool. v0.1 — stub. */ readonly residential: ResidentialClient; /** Datacenter proxy pool. v0.1 — stub. */ readonly datacenter: DatacenterClient; /** Dedicated datacenter proxies. v0.1 — stub. */ readonly dedicated: DedicatedClient; /** Mobile proxies. v0.1 — stub. */ readonly mobile: MobileClient; /** Google SERP API. v0.1 — stub. */ readonly serp: SerpClient; private readonly publicIp; constructor(opts?: ClientOptions); /** * Shortcut for `client.free.random(filters)` — picks one matching free * proxy, or null if none match. * * Lives at the top level because "give me a proxy" is the canonical * first call into the SDK. Functionally identical to `.free.random()`; * use whichever reads better in your code. * * const p = await ps.proxy({ country: 'us', anonymity: 'elite' }); * if (p) console.log(p.url); */ proxy(filters?: FreeProxyFilters, opts?: RequestOptions): Promise; /** * Look up the caller's public IP address. Useful for proxy * before/after verification: * * const real = await ps.getPublicIp(); * // ...route a request through a proxy... * if (proxiedIp.ip === real.ip) console.warn('Proxy leaked real IP'); * * Defaults to ipinfo.io (no key, ~50k req/month free, returns IP + geo). * Pass `{ service: 'ipify' }` for IP-only / unlimited, or `{ endpoint }` * to point at your own service. The caller's ProxyScrape API key is * never forwarded to the third-party provider. */ getPublicIp(opts?: GetPublicIpOptions): Promise; } /** * Base class for every error the SDK throws. * * Catch this if you want a single instanceof check that covers any failure * from the client; subclasses below let you discriminate the cause when you * need to. */ declare class ProxyScrapeError extends Error { name: string; constructor(message: string, cause?: unknown); } /** * Thrown when the API returns 401/403. The message echoes the upstream body * when available so callers don't have to inspect `response` to debug. */ declare class AuthError extends ProxyScrapeError { readonly status: number; name: string; constructor(message: string, status: number, cause?: unknown); } /** * Thrown after retries are exhausted on a 429 or after the API's documented * rate-limit codes (433 hourly / 434 daily / 432 per-second) come back. The * `retryAfterSeconds` is read from the Retry-After header when present. */ declare class RateLimitError extends ProxyScrapeError { readonly status: number; readonly retryAfterSeconds: number | null; name: string; constructor(message: string, status: number, retryAfterSeconds: number | null, cause?: unknown); } /** * Thrown for non-recoverable 4xx responses (validation errors, malformed * params, etc.). 5xx is treated separately as transient and retried by the * HTTP wrapper. */ declare class BadRequestError extends ProxyScrapeError { readonly status: number; name: string; constructor(message: string, status: number, cause?: unknown); } /** * Wraps fetch-level failures (DNS, TCP, TLS, AbortError, etc.) so callers * can distinguish "the API said no" from "we couldn't reach the API". */ declare class NetworkError extends ProxyScrapeError { name: string; } /** * Single source of truth for the SDK version, embedded into the User-Agent * header and any public version surface. * * Kept in sync with package.json by the release workflow — the changeset * action edits both files in the same PR. If you update package.json by * hand, update this constant too. */ declare const SDK_VERSION = "1.0.3"; export { type Anonymity, AuthError, BadRequestError, CheckerClient, type ClientOptions, DatacenterClient, DedicatedClient, type FreeProxy, type FreeProxyApiResponse, type FreeProxyFilters, FreeProxyListClient, type FreeProxyListParams, type FreeProxyListResult, type FreeProxyRaw, type GetPublicIpOptions, MobileClient, NetworkError, type Protocol, ProxyScrape, ProxyScrapeError, PublicIpClient, type PublicIpInfo, type PublicIpService, RateLimitError, ResidentialClient, SDK_VERSION, SerpClient };