npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

impers

v0.0.1

Published

Node.js libcurl bindings using Koffi

Downloads

10

Readme

impers

Node.js binding for curl-impersonate via Koffi. A TypeScript port of curl_cffi.

impers is an HTTP client library for Node.js that can impersonate browsers' TLS/JA3 and HTTP/2 fingerprints. If you are blocked by some website for no obvious reason, you can give impers a try.

Node.js 18+ is required.

[!WARNING] impers is in a technical preview state, not even alpha. All APIs are provisional, and subject to change.

Features

  • Supports JA3/TLS and HTTP/2 fingerprint impersonation, including recent browsers and custom fingerprints.
  • Fast performance powered by libcurl.
  • Supports HTTP/2 and HTTP/3.
  • Supports WebSocket with impersonation.
  • TypeScript first with full type definitions.
  • MIT licensed.

||node-fetch|axios|got|undici|impers| |---|---|---|---|---|---| |http/2|❌|❌|✅|✅|✅| |http/3|❌|❌|❌|❌|✅| |websocket|❌|❌|❌|✅|✅| |fingerprints|❌|❌|❌|❌|✅|

Install

npm install impers

Requirements

  • libcurl-impersonate: For full fingerprinting support, you need curl-impersonate installed. Standard libcurl works but without impersonation features.

If libcurl isn't on your default library path, set the LIBCURL_PATH environment variable:

export LIBCURL_PATH=/path/to/libcurl-impersonate.so

On macOS with Homebrew:

brew install curl
export LIBCURL_PATH=$(brew --prefix curl)/lib/libcurl.dylib

Usage

impers comes with a low-level Curl API and a high-level requests-like API.

requests-like

import * as impers from "impers";

// Notice the impersonate parameter
const r = await impers.get("https://tls.peet.ws/api/all", { impersonate: "chrome" });

console.log(r.json());
// output: {..., "ja3_hash": "aa56c057ad164ec4fdcb7a5a283be9fc", ...}
// the JA3 fingerprint should be the same as the target browser

// To keep using the latest browser version as impers updates,
// simply set impersonate="chrome" without specifying a version.
// Other similar values are: "safari" and "firefox"
const r2 = await impers.get("https://tls.peet.ws/api/all", { impersonate: "chrome" });

// To pin a specific version, use version numbers together.
const r3 = await impers.get("https://tls.peet.ws/api/all", { impersonate: "chrome124" });

// To impersonate other than browsers, bring your own JA3/Akamai strings
// See examples directory for details.
const r4 = await impers.get("https://tls.peet.ws/api/all", {
  ja3: "771,4865-4866-4867-49195-49199,0-23-65281-10-11-35-16-5-13-18-51-45-43-27-17513,29-23-24,0",
  akamai: "1:65536;3:1000;4:6291456;6:262144|15663105|0|m,a,s,p",
});

// HTTP/SOCKS proxies are supported
const r5 = await impers.get("https://tls.peet.ws/api/all", {
  impersonate: "chrome",
  proxy: "http://localhost:3128",
});

const r6 = await impers.get("https://tls.peet.ws/api/all", {
  impersonate: "chrome",
  proxy: "socks5://localhost:1080",
});

Sessions

import { Session } from "impers";

const session = new Session();

// httpbin is an HTTP test website, this endpoint makes the server set cookies
await session.get("https://httpbin.org/cookies/set/foo/bar");
console.log(session.cookies);
// Cookies { foo: 'bar' }

// retrieve cookies again to verify
const r = await session.get("https://httpbin.org/cookies");
console.log(r.json());
// { cookies: { foo: 'bar' } }

// Don't forget to close the session when done
await session.close();

Supported Impersonate Browsers

impers supports the same browser versions as curl-impersonate:

| Browser | Versions | |---------|----------| | Chrome | chrome99, chrome100, chrome101, chrome104, chrome107, chrome110, chrome116, chrome119, chrome120, chrome123, chrome124, chrome131, chrome133a, chrome136, chrome142 | | Chrome Android | chrome99_android, chrome131_android | | Safari | safari153, safari155, safari170, safari180, safari184, safari260, safari2601 | | Safari iOS | safari172_ios, safari180_ios, safari184_ios, safari260_ios | | Firefox | firefox133, firefox135, firefox144 | | Tor | tor145 | | Edge | edge99, edge101 |

If you are trying to impersonate a target other than a browser, use ja3 and akamai options to specify your own customized fingerprints.

Custom Fingerprints

import * as impers from "impers";

// JA3 TLS fingerprint format: tls_version,ciphers,extensions,curves,curve_formats
const ja3 = "771,4865-4866-4867-49195-49199,0-23-65281-10-11-35-16-5-13-18-51-45-43-27-17513,29-23-24,0";

// Akamai HTTP/2 fingerprint format: settings|window_update|streams|header_order
const akamai = "1:65536;3:1000;4:6291456;6:262144|15663105|0|m,a,s,p";

const r = await impers.get("https://tls.peet.ws/api/all", { ja3, akamai });

// For fine-grained control, use extraFp
const extraFp: impers.ExtraFingerprint = {
  tlsSigAlgs: ["ecdsa_secp256r1_sha256", "rsa_pss_rsae_sha256"],
  tlsSupportedGroups: ["X25519", "P-256", "P-384"],
  http2Settings: {
    1: 65536,   // HEADER_TABLE_SIZE
    3: 1000,    // MAX_CONCURRENT_STREAMS
    4: 6291456, // INITIAL_WINDOW_SIZE
    6: 262144,  // MAX_HEADER_LIST_SIZE
  },
  http2PseudoHeaderOrder: ["m", "a", "s", "p"],
};

const r2 = await impers.get("https://tls.peet.ws/api/all", { extraFp });

Concurrent Requests

import { Session } from "impers";

const urls = [
  "https://httpbin.org/get",
  "https://httpbin.org/ip",
  "https://httpbin.org/user-agent",
];

const session = new Session({ impersonate: "chrome124" });

try {
  const results = await Promise.all(
    urls.map(url => session.get(url))
  );

  for (const r of results) {
    console.log(r.status, r.url);
  }
} finally {
  await session.close();
}

WebSockets

import * as impers from "impers";

const ws = await impers.wsConnect("wss://echo.websocket.org", {
  impersonate: "chrome124",
});

await ws.send("Hello, World!");

for await (const message of ws) {
  console.log("Received:", message);
  if (message.type === "text" && message.data === "Hello, World!") {
    break;
  }
}

await ws.close();

Low-level Curl API

import { Curl, CurlOpt } from "impers";

const curl = new Curl();
const chunks: Buffer[] = [];

try {
  curl.setOpt(CurlOpt.CURLOPT_URL, "https://example.com");
  curl.setWriteFunction((chunk) => chunks.push(Buffer.from(chunk)));

  // Browser impersonation (requires curl-impersonate)
  curl.impersonate("chrome124");

  // Or use manual fingerprinting
  // curl.setJa3("771,4865-4866-...");
  // curl.setAkamai("1:65536;...");

  curl.perform();
  console.log(Buffer.concat(chunks).toString());
} finally {
  curl.cleanup();
}

API Reference

Standalone Functions

import * as impers from "impers";

// Generic request
const r = await impers.request("POST", url, options);

// Convenience methods
const r1 = await impers.get(url, options);
const r2 = await impers.post(url, { json: { key: "value" } });
const r3 = await impers.put(url, { data: { key: "value" } });
const r4 = await impers.del(url);

Session

import { Session } from "impers";

const session = new Session({
  baseUrl: "https://api.example.com",
  impersonate: "chrome124",
  headers: { "X-Custom-Header": "value" },
  timeout: 30,
  proxy: "http://localhost:3128",
});

const r = await session.get("/endpoint");
await session.close();

Request Options

| Option | Type | Description | |--------|------|-------------| | params | object | URL query parameters | | headers | object | Request headers | | cookies | object | Request cookies | | data | object \| string | Form data (application/x-www-form-urlencoded) | | json | any | JSON body (automatically sets Content-Type) | | content | string \| Buffer | Raw body content | | auth | object | HTTP authentication (basic, digest, bearer) | | proxy | string | Proxy URL |

impers

Node.js binding for curl-impersonate via Koffi. A TypeScript port of curl_cffi.

impers is an HTTP client library for Node.js that can impersonate browsers' TLS/JA3 and HTTP/2 fingerprints. If you are blocked by some website for no obvious reason, you can give impers a try.

Node.js 18+ is required.

Features

  • Supports JA3/TLS and HTTP/2 fingerprint impersonation, including recent browsers and custom fingerprints.
  • Fast performance powered by libcurl.
  • Supports HTTP/2 and HTTP/3.
  • Supports WebSocket with impersonation.
  • TypeScript first with full type definitions.
  • MIT licensed.

||node-fetch|axios|got|undici|impers| |---|---|---|---|---|---| |http/2|❌|❌|✅|✅|✅| |http/3|❌|❌|❌|❌|✅| |websocket|❌|❌|❌|✅|✅| |fingerprints|❌|❌|❌|❌|✅|

Install

npm install impers

Requirements

  • libcurl-impersonate: For full fingerprinting support, you need curl-impersonate installed. Standard libcurl works but without impersonation features.

If libcurl isn't on your default library path, set the LIBCURL_PATH environment variable:

export LIBCURL_PATH=/path/to/libcurl-impersonate.so

On macOS with Homebrew:

brew install curl
export LIBCURL_PATH=$(brew --prefix curl)/lib/libcurl.dylib

Usage

impers comes with a low-level Curl API and a high-level requests-like API.

requests-like

import * as impers from "impers";

// Notice the impersonate parameter
const r = await impers.get("https://tls.peet.ws/api/all", { impersonate: "chrome" });

console.log(r.json());
// output: {..., "ja3_hash": "aa56c057ad164ec4fdcb7a5a283be9fc", ...}
// the JA3 fingerprint should be the same as the target browser

// To keep using the latest browser version as impers updates,
// simply set impersonate="chrome" without specifying a version.
// Other similar values are: "safari" and "firefox"
const r2 = await impers.get("https://tls.peet.ws/api/all", { impersonate: "chrome" });

// To pin a specific version, use version numbers together.
const r3 = await impers.get("https://tls.peet.ws/api/all", { impersonate: "chrome124" });

// To impersonate other than browsers, bring your own JA3/Akamai strings
// See examples directory for details.
const r4 = await impers.get("https://tls.peet.ws/api/all", {
  ja3: "771,4865-4866-4867-49195-49199,0-23-65281-10-11-35-16-5-13-18-51-45-43-27-17513,29-23-24,0",
  akamai: "1:65536;3:1000;4:6291456;6:262144|15663105|0|m,a,s,p",
});

// HTTP/SOCKS proxies are supported
const r5 = await impers.get("https://tls.peet.ws/api/all", {
  impersonate: "chrome",
  proxy: "http://localhost:3128",
});

const r6 = await impers.get("https://tls.peet.ws/api/all", {
  impersonate: "chrome",
  proxy: "socks5://localhost:1080",
});

Sessions

import { Session } from "impers";

const session = new Session();

// httpbin is an HTTP test website, this endpoint makes the server set cookies
await session.get("https://httpbin.org/cookies/set/foo/bar");
console.log(session.cookies);
// Cookies { foo: 'bar' }

// retrieve cookies again to verify
const r = await session.get("https://httpbin.org/cookies");
console.log(r.json());
// { cookies: { foo: 'bar' } }

// Don't forget to close the session when done
await session.close();

Supported Impersonate Browsers

impers supports the same browser versions as curl-impersonate:

| Browser | Versions | |---------|----------| | Chrome | chrome99, chrome100, chrome101, chrome104, chrome107, chrome110, chrome116, chrome119, chrome120, chrome123, chrome124, chrome131, chrome133a, chrome136, chrome142 | | Chrome Android | chrome99_android, chrome131_android | | Safari | safari153, safari155, safari170, safari180, safari184, safari260, safari2601 | | Safari iOS | safari172_ios, safari180_ios, safari184_ios, safari260_ios | | Firefox | firefox133, firefox135, firefox144 | | Tor | tor145 | | Edge | edge99, edge101 |

If you are trying to impersonate a target other than a browser, use ja3 and akamai options to specify your own customized fingerprints.

Custom Fingerprints

import * as impers from "impers";

// JA3 TLS fingerprint format: tls_version,ciphers,extensions,curves,curve_formats
const ja3 = "771,4865-4866-4867-49195-49199,0-23-65281-10-11-35-16-5-13-18-51-45-43-27-17513,29-23-24,0";

// Akamai HTTP/2 fingerprint format: settings|window_update|streams|header_order
const akamai = "1:65536;3:1000;4:6291456;6:262144|15663105|0|m,a,s,p";

const r = await impers.get("https://tls.peet.ws/api/all", { ja3, akamai });

// For fine-grained control, use extraFp
const extraFp: impers.ExtraFingerprint = {
  tlsSigAlgs: ["ecdsa_secp256r1_sha256", "rsa_pss_rsae_sha256"],
  tlsSupportedGroups: ["X25519", "P-256", "P-384"],
  http2Settings: {
    1: 65536,   // HEADER_TABLE_SIZE
    3: 1000,    // MAX_CONCURRENT_STREAMS
    4: 6291456, // INITIAL_WINDOW_SIZE
    6: 262144,  // MAX_HEADER_LIST_SIZE
  },
  http2PseudoHeaderOrder: ["m", "a", "s", "p"],
};

const r2 = await impers.get("https://tls.peet.ws/api/all", { extraFp });

Concurrent Requests

import { Session } from "impers";

const urls = [
  "https://httpbin.org/get",
  "https://httpbin.org/ip",
  "https://httpbin.org/user-agent",
];

const session = new Session({ impersonate: "chrome124" });

try {
  const results = await Promise.all(
    urls.map(url => session.get(url))
  );

  for (const r of results) {
    console.log(r.status, r.url);
  }
} finally {
  await session.close();
}

WebSockets

import * as impers from "impers";

const ws = await impers.wsConnect("wss://echo.websocket.org", {
  impersonate: "chrome124",
});

await ws.send("Hello, World!");

for await (const message of ws) {
  console.log("Received:", message);
  if (message.type === "text" && message.data === "Hello, World!") {
    break;
  }
}

await ws.close();

Low-level Curl API

import { Curl, CurlOpt } from "impers";

const curl = new Curl();
const chunks: Buffer[] = [];

try {
  curl.setOpt(CurlOpt.CURLOPT_URL, "https://example.com");
  curl.setWriteFunction((chunk) => chunks.push(Buffer.from(chunk)));

  // Browser impersonation (requires curl-impersonate)
  curl.impersonate("chrome124");

  // Or use manual fingerprinting
  // curl.setJa3("771,4865-4866-...");
  // curl.setAkamai("1:65536;...");

  curl.perform();
  console.log(Buffer.concat(chunks).toString());
} finally {
  curl.cleanup();
}

API Reference

Standalone Functions

import * as impers from "impers";

// Generic request
const r = await impers.request("POST", url, options);

// Convenience methods
const r1 = await impers.get(url, options);
const r2 = await impers.post(url, { json: { key: "value" } });
const r3 = await impers.put(url, { data: { key: "value" } });
const r4 = await impers.del(url);

Session

import { Session } from "impers";

const session = new Session({
  baseUrl: "https://api.example.com",
  impersonate: "chrome124",
  headers: { "X-Custom-Header": "value" },
  timeout: 30,
  proxy: "http://localhost:3128",
});

const r = await session.get("/endpoint");
await session.close();

Request Options

| Option | Type | Description | |--------|------|-------------| | params | object | URL query parameters | | headers | object | Request headers | | cookies | object | Request cookies | | data | object \| string | Form data (application/x-www-form-urlencoded) | | json | any | JSON body (automatically sets Content-Type) | | content | string \| Buffer | Raw body content | | auth | object | HTTP authentication (basic, digest, bearer) | | proxy | string | Proxy URL | | timeout | number | Request timeout in seconds | | connectTimeout | number | Connection timeout in seconds | | allowRedirects | boolean | Follow redirects (default: true) | | maxRedirects | number | Maximum redirects (default: 30) | | verify | boolean | Verify SSL certificates (default: true) | | impersonate | string | Browser to impersonate | | ja3 | string | JA3 TLS fingerprint string | | akamai | string | Akamai HTTP/2 fingerprint string | | extraFp | ExtraFingerprint | Fine-grained fingerprint options | | httpVersion | string | Force HTTP version ("1.0", "1.1", "2", "3") |

Response Object

import * as impers from "impers";

const r = await impers.get(url);

r.status;       // HTTP status code
r.statusText;   // HTTP status text
r.headers;      // Response headers
r.cookies;      // Response cookies
r.url;          // Final URL (after redirects)
r.text;         // Response body as string
r.content;      // Response body as Buffer
r.json();       // Parse response as JSON
r.elapsed;      // Request duration in seconds

Acknowledgement

License

MIT | timeout | number | Request timeout in seconds | | connectTimeout | number | Connection timeout in seconds | | allowRedirects | boolean | Follow redirects (default: true) | | maxRedirects | number | Maximum redirects (default: 30) | | verify | boolean | Verify SSL certificates (default: true) | | impersonate | string | Browser to impersonate | | ja3 | string | JA3 TLS fingerprint string | | akamai | string | Akamai HTTP/2 fingerprint string | | extraFp | ExtraFingerprint | Fine-grained fingerprint options | | httpVersion | string | Force HTTP version ("1.0", "1.1", "2", "3") |

Response Object

import * as impers from "impers";

const r = await impers.get(url);

r.status;       // HTTP status code
r.statusText;   // HTTP status text
r.headers;      // Response headers
r.cookies;      // Response cookies
r.url;          // Final URL (after redirects)
r.text;         // Response body as string
r.content;      // Response body as Buffer
r.json();       // Parse response as JSON
r.elapsed;      // Request duration in seconds

Acknowledgement

License

MIT