A powerful Node.js wrapper for curl-impersonate
Make HTTP requests that mimic real browser behavior and bypass anti-bot protections with ease.
Features • Installation • Quick Start • Documentation • Examples
- 🚀 Browser Impersonation: Mimic Chrome, Firefox, Safari, and Edge browsers
- 🔧 Easy to Use: Simple API similar to axios/fetch
- 📦 Zero Dependencies: Only requires
tarfor binary extraction - 🎯 TypeScript Support: Full type definitions included
- 🔄 Auto Binary Management: Automatically downloads and manages curl-impersonate binaries
- 🌐 Cross-Platform: Works on Linux, macOS, and Windows
- 🔒 Proxy Support: Built-in support for HTTP, HTTPS, and SOCKS proxies with authentication
- 📁 Clean Installation: Binaries stored in package directory, not your project root
- 🍪 Cookie Management: Automatic cookie storage and sending across requests
- ✅ Error Response Bodies: Returns full HTTP response body on 4xx/5xx errors (like axios/Postman)
- Features
- Installation
- Quick Start
- Project Usage Examples
- API Reference
- Configuration
- Supported Browsers
- Response Format
- Binary Management
- Troubleshooting
- Requirements
- Contributing
- License
- Contributors
npm install cuimpimport { get, post, createCuimpHttp } from 'cuimp'
// Simple GET request
const response = await get('https://httpbin.org/headers')
console.log(response.data)
// POST with data
const result = await post('https://httpbin.org/post', {
name: 'John Doe',
email: '[email protected]',
})
// Using HTTP client instance
const client = createCuimpHttp({
descriptor: { browser: 'chrome', version: '123' },
})
const data = await client.get('https://api.example.com/users')import { get, createCuimpHttp } from 'cuimp'
// Create a client that mimics Chrome 123
const scraper = createCuimpHttp({
descriptor: { browser: 'chrome', version: '123' },
})
// Scrape a website that blocks regular requests
const response = await scraper.get('https://example.com/protected-content', {
headers: {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36',
Accept: 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
},
})
console.log('Scraped content:', response.data)import { createCuimpHttp } from 'cuimp'
// Test your API with different browser signatures
const browsers = ['chrome', 'firefox', 'safari', 'edge']
for (const browser of browsers) {
const client = createCuimpHttp({
descriptor: { browser, version: 'latest' },
})
const response = await client.get('https://your-api.com/test')
console.log(`${browser}: ${response.status}`)
}import { createCuimpHttp } from 'cuimp'
// Enable automatic cookie management
const client = createCuimpHttp({
descriptor: { browser: 'chrome', version: '123' },
cookieJar: true, // Cookies are automatically stored and sent
})
// First request - server sets cookies
await client.get('https://httpbin.org/cookies/set/session_id/abc123')
// Second request - cookies are automatically included
const response = await client.get('https://httpbin.org/cookies')
console.log(response.data.cookies) // { session_id: 'abc123' }
// Access cookies programmatically
const cookieJar = client.getCookieJar()
const cookies = cookieJar.getCookies()
// Clear cookies
client.clearCookies()
// Clean up when done (removes temp cookie file)
client.destroy()import { request } from 'cuimp'
// HTTP proxy
const response1 = await request({
url: 'https://httpbin.org/ip',
proxy: 'http://proxy.example.com:8080',
})
// SOCKS5 proxy with authentication
const response2 = await request({
url: 'https://httpbin.org/ip',
proxy: 'socks5://user:[email protected]:1080',
})
// Automatic proxy detection from environment variables
// HTTP_PROXY, HTTPS_PROXY, ALL_PROXY
const response3 = await request({
url: 'https://httpbin.org/ip',
// Will automatically use HTTP_PROXY if set
})import { Cuimp, downloadBinary } from 'cuimp'
// Method 1: Using Cuimp class
const cuimp = new Cuimp({ descriptor: { browser: 'chrome' } })
const binaryInfo = await cuimp.download()
console.log('Downloaded:', binaryInfo.binaryPath)
// Method 2: Using convenience function
const info = await downloadBinary({
descriptor: { browser: 'firefox', version: '133' },
})
// Pre-download multiple browsers for offline use
const browsers = ['chrome', 'firefox', 'safari', 'edge']
for (const browser of browsers) {
await downloadBinary({ descriptor: { browser } })
console.log(`${browser} binary ready`)
}import { createCuimpHttp } from 'cuimp'
// Suppress all logs
const silentLogger = {
info: () => {},
warn: () => {},
error: () => {},
debug: () => {},
}
const client = createCuimpHttp({
descriptor: { browser: 'chrome' },
logger: silentLogger,
})
// Or use a structured logger (Winston, Pino, etc.)
const client = createCuimpHttp({
descriptor: { browser: 'chrome' },
logger: myStructuredLogger,
})Make a GET request.
const response = await get('https://api.example.com/users')Make a POST request.
const response = await post('https://api.example.com/users', {
name: 'John Doe',
email: '[email protected]',
})Make a PUT request.
Make a PATCH request.
Make a DELETE request.
Make a HEAD request.
Make an OPTIONS request.
Download curl-impersonate binary without making HTTP requests.
// Download default binary
const info = await downloadBinary()
// Download specific browser binary
const chromeInfo = await downloadBinary({
descriptor: { browser: 'chrome', version: '123' },
})Create an HTTP client instance.
const client = createCuimpHttp({
descriptor: { browser: 'chrome', version: '123' },
path: '/custom/path/to/binary',
})
// Use the client
const response = await client.get('https://api.example.com/data')Make a request with full configuration.
const response = await request({
url: 'https://api.example.com/users',
method: 'POST',
headers: {
Authorization: 'Bearer token',
'Content-Type': 'application/json',
},
data: { name: 'John Doe' },
timeout: 5000,
})The core class for managing curl-impersonate binaries and descriptors.
import { Cuimp } from 'cuimp'
const cuimp = new Cuimp({
descriptor: { browser: 'chrome', version: '123' },
path: '/custom/path',
})
// Verify binary
const info = await cuimp.verifyBinary()
// Build command preview
const command = cuimp.buildCommandPreview('https://example.com', 'GET')
// Download binary without verification
const binaryInfo = await cuimp.download()HTTP client class that wraps the Cuimp core.
import { CuimpHttp, Cuimp } from 'cuimp'
const core = new Cuimp()
const client = new CuimpHttp(core, {
baseURL: 'https://api.example.com',
timeout: 10000,
})Configure which browser to impersonate:
interface CuimpDescriptor {
browser?: 'chrome' | 'firefox' | 'edge' | 'safari'
version?: string // e.g., '123', '124', or 'latest' (default)
architecture?: 'x64' | 'arm64'
platform?: 'linux' | 'windows' | 'macos'
forceDownload?: boolean // Force re-download even if binary exists
}Request configuration options:
interface CuimpRequestConfig {
url: string
method?: 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE' | 'HEAD' | 'OPTIONS'
headers?: Record<string, string>
data?: any
timeout?: number
maxRedirects?: number
proxy?: string // HTTP, HTTPS, or SOCKS proxy URL
insecureTLS?: boolean // Skip TLS certificate verification
signal?: AbortSignal // Request cancellation
}Core options:
interface CuimpOptions {
descriptor?: CuimpDescriptor
path?: string // Custom path to curl-impersonate binary
extraCurlArgs?: string[] // Global curl arguments applied to all requests
logger?: Logger // Custom logger for binary download/verification messages
cookieJar?: boolean | string // Enable automatic cookie management
}The cookieJar option enables automatic cookie management:
// Option 1: Automatic temp file (cleaned up on destroy)
const client = createCuimpHttp({
cookieJar: true,
})
// Option 2: Custom file path (persists between runs)
// Recommended: Use user home directory for security
import os from 'os'
import path from 'path'
const cookiePath = path.join(os.homedir(), '.cuimp', 'cookies', 'my-cookies.txt')
const client = createCuimpHttp({
cookieJar: cookiePath, // User-specific, secure location
})
// Option 3: Disabled (default)
const client = createCuimpHttp({
cookieJar: false, // or omit entirely
})Best Practices for Cookie File Paths:
- ✅ Use
~/.cuimp/cookies/(user home directory) - secure, user-specific, consistent with binary storage - ✅ Use temp directory for temporary cookies - auto-cleaned
- ❌ Avoid project root (
./cookies.txt) - risk of committing sensitive data to git
Cookie Jar Methods:
// Get the cookie jar instance
const jar = client.getCookieJar()
// Get all cookies
const cookies = jar.getCookies()
// Get cookies for a specific domain
const domainCookies = jar.getCookiesForDomain('example.com')
// Manually set a cookie
jar.setCookie({
domain: 'example.com',
name: 'my_cookie',
value: 'my_value',
path: '/',
secure: true,
expires: new Date('2025-12-31'),
})
// Delete a cookie
jar.deleteCookie('my_cookie', 'example.com')
// Clear all cookies
client.clearCookies()
// Clean up (removes temp file if using cookieJar: true)
client.destroy()You can provide a custom logger to control how cuimp logs binary download and verification messages:
interface Logger {
info(...args: any[]): void
warn(...args: any[]): void
error(...args: any[]): void
debug(...args: any[]): void
}Example: Using a custom formatted logger
import { createCuimpHttp } from 'cuimp'
// Custom logger with formatted output
const customLogger = {
info: (...args) => console.log('[INFO]', new Date().toISOString(), ...args),
warn: (...args) => console.warn('[WARN]', new Date().toISOString(), ...args),
error: (...args) => console.error('[ERROR]', new Date().toISOString(), ...args),
debug: (...args) => console.debug('[DEBUG]', new Date().toISOString(), ...args),
}
const client = createCuimpHttp({
descriptor: { browser: 'chrome', version: '123' },
logger: customLogger,
})
// Now all binary download/verification messages will use your custom format
await client.get('https://api.example.com/data')
// Output: [INFO] 2024-01-15T10:30:00.000Z Verifying binary...
// Output: [INFO] 2024-01-15T10:30:01.000Z Binary verified successfullyExample: Collecting logs for analysis
const logEntries = []
const collectingLogger = {
info: (...args) =>
logEntries.push({ level: 'info', timestamp: Date.now(), message: args.join(' ') }),
warn: (...args) =>
logEntries.push({ level: 'warn', timestamp: Date.now(), message: args.join(' ') }),
error: (...args) =>
logEntries.push({ level: 'error', timestamp: Date.now(), message: args.join(' ') }),
debug: (...args) =>
logEntries.push({ level: 'debug', timestamp: Date.now(), message: args.join(' ') }),
}
const client = createCuimpHttp({
descriptor: { browser: 'firefox' },
logger: collectingLogger,
})
await client.get('https://api.example.com/data')
// Analyze collected logs
console.log('Collected logs:', logEntries)
// Can send to external logging service, save to file, etc.By default, cuimp uses console for logging.
| Browser | Versions | Platforms |
|---|---|---|
| Chrome | 99, 100, 101, 104, 107, 110, 116, 119, 120, 123, 124, 131, 133a, 136, 142 | Linux, Windows, macOS, Android |
| Firefox | 133, 135, 145 | Linux, Windows, macOS |
| Edge | 99, 101 | Linux, Windows, macOS |
| Safari | 153, 155, 170, 172, 180, 184, 260, 2601 | macOS, iOS |
| Tor | 145 | Linux, Windows, macOS |
All HTTP methods return a standardized response. Important: Unlike traditional curl behavior, cuimp returns response objects for all HTTP status codes (including 4xx/5xx), allowing you to access error response bodies, headers, and status information. Only network errors (connection failures, DNS errors, etc.) throw exceptions.
interface CuimpResponse<T = any> {
status: number
statusText: string
headers: Record<string, string>
data: T
rawBody: Buffer
request: {
url: string
method: string
headers: Record<string, string>
command: string
}
}📁 Runnable Examples: Check out the
examples/folder for complete, runnable examples demonstrating all features of cuimp.
import { get, post } from 'cuimp'
// GET request
const users = await get('https://jsonplaceholder.typicode.com/users')
console.log(users.data)
// POST request
const newUser = await post('https://jsonplaceholder.typicode.com/users', {
name: 'John Doe',
email: '[email protected]',
})import { createCuimpHttp } from 'cuimp'
const client = createCuimpHttp({
descriptor: { browser: 'chrome', version: '123' },
})
// Set default headers
client.defaults.headers['Authorization'] = 'Bearer your-token'
// Make requests
const response = await client.get('/api/users')import { Cuimp } from 'cuimp'
const cuimp = new Cuimp({
path: '/usr/local/bin/curl-impersonate',
})
const info = await cuimp.verifyBinary()import { get, post } from 'cuimp'
// 4xx/5xx responses return response objects (not thrown)
const response = await get('https://httpbin.org/status/404')
if (response.status === 404) {
console.log('Resource not found:', response.data)
} else if (response.status >= 500) {
console.error('Server error:', response.statusText)
console.error('Error details:', response.data)
} else if (response.status >= 400) {
console.warn('Client error:', response.status, response.statusText)
}
// Handle JSON error responses
const errorResponse = await post('https://api.example.com/users', {
email: 'invalid-email',
})
if (errorResponse.status === 400) {
// Access the error body (parsed JSON if Content-Type is application/json)
const errorData = errorResponse.data
console.log('Validation errors:', errorData.errors)
console.log('Error message:', errorData.message)
}4xx/5xx Responses: Unlike traditional curl behavior, cuimp returns full response objects for HTTP error status codes (4xx/5xx), similar to axios and Postman. This allows you to access error response bodies, headers, and status information.
import { get } from 'cuimp'
// 4xx/5xx responses return response objects (not thrown)
const response = await get('https://httpbin.org/status/404')
console.log('Status:', response.status) // 404
console.log('Status Text:', response.statusText) // 'Not Found'
console.log('Body:', response.data) // Response body (if any)
console.log('Headers:', response.headers) // Response headers
// Check status and handle accordingly
if (response.status >= 400) {
console.error(`Error ${response.status}:`, response.data)
} else {
console.log('Success:', response.data)
}Network Errors: Network errors (connection failures, DNS errors, etc.) are still thrown as exceptions:
import { get } from 'cuimp'
try {
const response = await get('https://api.example.com/data')
console.log(response.data)
} catch (error) {
if (error.code === 'ENOTFOUND') {
console.log('Network error: DNS resolution failed')
} else if (error.code === 'ECONNREFUSED') {
console.log('Network error: Connection refused')
} else {
console.log('Unknown error:', error.message)
}
}Cuimp automatically manages curl-impersonate binaries:
- Automatic Download: Downloads the appropriate binary for your platform on first use
- Version Matching: Reuses cached binaries only if they match the requested version
- Force Download: Use
forceDownload: trueto bypass cache and always download fresh binaries - Verification: Checks binary integrity and permissions
- Clean Storage: Binaries are stored in
~/.cuimp/binaries/(user home directory) - Cross-Platform: Automatically detects your platform and architecture
- Specific version (e.g.,
'133'): Uses cached binary if version matches, otherwise downloads - 'latest' (default): Uses any cached binary, or downloads if none exists
- forceDownload: Always downloads, ignoring cache (useful for always getting the actual latest version)
- Download location:
~/.cuimp/binaries/(user home directory) - Search locations: Also checks
node_modules/cuimp/binaries/and system paths as fallback - Shared across projects: Downloaded binaries are reused between projects
- No Project Pollution: Your project directory stays clean
// HTTP proxy
proxy: 'http://proxy.example.com:8080'
// HTTPS proxy
proxy: 'https://proxy.example.com:8080'
// SOCKS4 proxy
proxy: 'socks4://proxy.example.com:1080'
// SOCKS5 proxy
proxy: 'socks5://proxy.example.com:1080'
// Proxy with authentication
proxy: 'http://username:[email protected]:8080'
proxy: 'socks5://username:[email protected]:1080'
// Automatic from environment variables
// HTTP_PROXY, HTTPS_PROXY, ALL_PROXY, http_proxy, https_proxy, all_proxyCuimp always downloads fresh binaries on first use, regardless of what's already installed on your system. This ensures:
- ✅ Consistency: All users get the same binary versions
- ✅ Reliability: No dependency on system-installed binaries
- ✅ Security: Fresh downloads with verified checksums
- ✅ Simplicity: No need to manage system dependencies
Cuimp automatically detects and uses these proxy environment variables:
# Set proxy for all requests
export HTTP_PROXY=http://proxy.example.com:8080
export HTTPS_PROXY=https://proxy.example.com:8080
export ALL_PROXY=socks5://proxy.example.com:1080
# Or use lowercase variants
export http_proxy=http://proxy.example.com:8080
export https_proxy=https://proxy.example.com:8080
export all_proxy=socks5://proxy.example.com:1080- Node.js >= 18.17
- Internet connection (for binary download)
Q: Binary download fails
# Check your internet connection and try again
# The binary will be downloaded to node_modules/cuimp/binaries/Q: Proxy not working
// Make sure your proxy URL is correct
const response = await request({
url: 'https://httpbin.org/ip',
proxy: 'http://username:[email protected]:8080',
})
// Or set environment variables
process.env.HTTP_PROXY = 'http://proxy.example.com:8080'Q: Permission denied errors
# On Unix systems, make sure the binary has execute permissions
chmod +x node_modules/cuimp/binaries/curl-impersonateQ: Binary not found
// Force re-download by clearing the binaries directory
rm -rf node_modules/cuimp/binaries/
// Then run your code again - it will re-downloadEnable debug logging to see what's happening:
// Set debug environment variable
process.env.DEBUG = 'cuimp:*'
// Or check the binary path
import { Cuimp } from 'cuimp'
const cuimp = new Cuimp()
const binaryPath = await cuimp.verifyBinary()
console.log('Binary path:', binaryPath)When running cuimp inside a Docker container, you may encounter the following error:
error setting certificate verify locations: CAfile: /etc/ssl/certs/ca-certificates.crt CApath: /etc/ssl/certs
This occurs because the container does not have access to the required CA certificates.
To fix this, mount your host machine’s certificate directory into the container, for example:
volumes:
- /etc/ssl/certs:/etc/ssl/certs:roThis ensures cuimp can properly verify TLS certificates inside the container.
This project is licensed under the MIT License - see the LICENSE file for details.
Contributions, issues, and feature requests are welcome!
Feel free to check the issues page if you want to contribute.
- Fork the project
- Create your feature branch (
git checkout -b feature/AmazingFeature) - Commit your changes (
git commit -m 'Add some AmazingFeature') - Push to the branch (
git push origin feature/AmazingFeature) - Open a Pull Request
- NPM Package: npmjs.com/package/cuimp
- GitHub Repository: github.com/F4RAN/cuimp-ts
- curl-impersonate: github.com/lexiforest/curl-impersonate
- Issues & Bug Reports: github.com/F4RAN/cuimp-ts/issues
Thanks to these awesome people who have contributed to this project:
|
F4RAN Original Author & Maintainer |
ma-joel CI, Encoding & Redirects |
parigi-n Bug Fixes |
nvitaterna Bug Fixes |
tony13tv macOS Support |
|
reyzzz HTTP/2 Headers Fix |
kenmadev Windows Repro & Testing |
If you find this project useful, please consider giving it a ⭐️!
Made with ❤️ by the Cuimp community