cuimp
v1.10.0
Published
Node wrapper for curl-impersonate (lexiforest) via CLI - Enhanced with raw buffer support and extra curl args
Maintainers
Readme
Cuimp
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
✨ Features
- 🚀 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)
📑 Table of Contents
- Features
- Installation
- Quick Start
- Project Usage Examples
- API Reference
- Configuration
- Supported Browsers
- Response Format
- Binary Management
- Troubleshooting
- Requirements
- Contributing
- License
- Contributors
📦 Installation
npm install cuimp🚀 Quick Start
import { 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')Project Usage Examples
Web Scraping with Browser Impersonation
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)API Testing with Different Browsers
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}`)
}Automatic Cookie Management
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()Using with Proxies
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
})Streaming Responses
import { requestStream } from 'cuimp'
await requestStream(
{ url: 'https://httpbin.org/stream/3', extraCurlArgs: ['--no-buffer'] },
{
onHeaders: ({ status, headers }) => {
console.log('status', status)
console.log('content-type', headers['Content-Type'])
},
onData: chunk => {
process.stdout.write(chunk)
},
onEnd: info => {
console.log('done', info.status)
},
onError: err => {
console.error('stream error', err)
},
}
)Note: For streaming (SSE or chunked responses), include --no-buffer in extraCurlArgs so curl flushes output immediately. You can also set it on the client defaults:
const client = createCuimpHttp({ extraCurlArgs: ['--no-buffer'] })
await client.requestStream({ url: 'https://httpbin.org/stream/3' }, { onData: chunk => {} })If you want the full body, enable buffering:
const res = await requestStream({ url: 'https://httpbin.org/stream/3' }, { collectBody: true })
console.log(res.rawBody?.toString('utf8'))Pre-downloading Binaries
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`)
}Custom Logging
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,
})API Reference
Convenience Functions
get(url, config?)
Make a GET request.
const response = await get('https://api.example.com/users')post(url, data?, config?)
Make a POST request.
const response = await post('https://api.example.com/users', {
name: 'John Doe',
email: '[email protected]',
})put(url, data?, config?)
Make a PUT request.
patch(url, data?, config?)
Make a PATCH request.
del(url, config?)
Make a DELETE request.
head(url, config?)
Make a HEAD request.
options(url, config?)
Make an OPTIONS request.
downloadBinary(options?)
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' },
})HTTP Client
createCuimpHttp(options?)
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')request(config)
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,
})requestStream(config, handlers?)
Stream a response body incrementally with callbacks.
const res = await requestStream(
{ url: 'https://httpbin.org/stream/3' },
{
onHeaders: ({ status }) => console.log('status', status),
onData: chunk => process.stdout.write(chunk),
onEnd: info => console.log('done', info.status),
onError: err => console.error(err),
}
)handlers supports:
interface CuimpStreamHandlers {
onHeaders?: (info: {
status: number
statusText: string
headers: Record<string, string>
}) => void
onData?: (chunk: Buffer) => void
onEnd?: (info: CuimpStreamResponse) => void
onError?: (error: Error) => void
collectBody?: boolean
}Core Classes
Cuimp
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()CuimpHttp
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,
})CuimpHttp also supports streaming via client.requestStream(config, handlers).
Configuration
CuimpDescriptor
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
}CuimpRequestConfig
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
}CuimpOptions
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
proxy?: string // Default proxy for all requests (HTTP, HTTPS, or SOCKS URL)
cookieJar?: boolean | string // Enable automatic cookie management
autoDownload?: boolean // If false, throw error instead of auto-downloading binaries (default: true)
}Proxy Configuration
Set a default proxy for all requests when creating the client. Per-request proxy in CuimpRequestConfig overrides this.
// Session-level proxy: all requests use this proxy by default
const client = createCuimpHttp({
proxy: 'http://proxy.example.com:8080',
})
await client.get('https://api.example.com/data') // Uses the proxy
// Override for a single request
await client.get('https://api.example.com/other', {
proxy: 'socks5://other-proxy:1080',
})Supported proxy URL formats: http://host:port, https://host:port, socks4://host:port, socks5://host:port, or host:port (defaults to HTTP). Authentication: http://user:pass@host:port.
Cookie Jar Configuration
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()Binary Auto-Download Control
By default, cuimp automatically downloads binaries if they're not found. You can disable this behavior:
// Disable auto-download (for advanced users)
const client = createCuimpHttp({
descriptor: { browser: 'chrome' },
autoDownload: false, // Will throw error if binary not found
})
try {
await client.get('https://api.example.com/data')
} catch (error) {
// Error: Binary not found for chrome on macos-arm64...
// Set autoDownload: true to enable automatic download
}
// Explicit download still works
const cuimp = new Cuimp({ descriptor: { browser: 'chrome' }, autoDownload: false })
await cuimp.download() // Always downloads, ignoring autoDownload settingUse cases for autoDownload: false:
- Control when binaries are downloaded (custom installation methods)
- Fail fast if binary is missing (production environments)
- Prevent unexpected downloads
Custom Logging
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.Example: Conditional debug logger (environment-based)
// Only show debug messages when DEBUG=true is set
const smartLogger = {
info: (...args) => console.log('[INFO]', ...args),
warn: (...args) => console.warn('[WARN]', ...args),
error: (...args) => console.error('[ERROR]', ...args),
debug: process.env.DEBUG === 'true' ? (...args) => console.debug('[DEBUG]', ...args) : () => {}, // Suppress debug messages by default
}
const client = createCuimpHttp({
descriptor: { browser: 'chrome' },
logger: smartLogger,
})
await client.get('https://api.example.com/data')
// With DEBUG=true: Shows all logs including debug messages
// Without DEBUG: Only shows info/warn/error, suppresses debugBy default, cuimp uses console for logging. Diagnostic messages (like "Found existing binary", "Binary verified") are logged at debug level, so they only appear if your logger implements debug().
Supported Browsers
| 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 |
Response Format
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
}
}Examples
📁 Runnable Examples: Check out the
examples/folder for complete, runnable examples demonstrating all features of cuimp.
Basic Usage
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]',
})Using HTTP Client
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')Custom Binary Path
import { Cuimp } from 'cuimp'
const cuimp = new Cuimp({
path: '/usr/local/bin/curl-impersonate',
})
const info = await cuimp.verifyBinary()Handling 4xx/5xx Error Responses
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)
}Error Handling
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)
}
}Binary Management
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
Version Behavior
- 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)
Binary Storage Location
- 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
Supported Proxy Formats
// 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_proxyImportant Notes
Force Download Behavior
Cuimp 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
Environment Variables
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:1080Requirements
- Node.js >= 18.17
- Internet connection (for binary download)
Troubleshooting
Common Issues
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-downloadDebug Mode
Enable 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)Docker
Error: “error setting certificate verify locations”
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.
📄 License
This project is licensed under the MIT License - see the LICENSE file for details.
🤝 Contributing
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
🔗 Links
- 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
👥 Contributors
Thanks to these awesome people who have contributed to this project:
If you find this project useful, please consider giving it a ⭐️!
Made with ❤️ by the Cuimp community
