Command Palette
Search for a command to run...
JavaScript
JavaScript SDK for Node.js and browser applications
Installation & Setup
Required dependencies and project setup for Node.js JavaScript
Package Installation
npm install axios
npm install --save-dev nodemon
npm init -ypackage.json Example
{
"name": "leakzero-client",
"version": "1.0.0",
"description": "LeakZero API client for Node.js",
"main": "leakzero-client.js",
"scripts": {
"start": "node leakzero-client.js",
"dev": "nodemon leakzero-client.js"
},
"dependencies": {
"axios": "^1.6.0"
},
"devDependencies": {
"nodemon": "^3.0.0"
},
"keywords": ["leakzero", "security", "breach-intelligence"],
"author": "Your Name",
"license": "MIT"
}Environment Variables: Create a
.env file with LEAKZERO_API_KEY=your_key_here and use require('dotenv').config() at the top of your file.Quick Start Example
Simple example to get you started with JavaScript
Basic Usage
const { LeakZeroClient } = require('./leakzero-client');
const client = new LeakZeroClient(process.env.LEAKZERO_API_KEY);
async function basicSearch() {
try {
const results = await client.regularSearch({
field: 'email',
value: '[email protected]',
rowLimit: 100
});
console.log(`Found ${results.totalResults} records`);
console.log(`Cost: $${results.cost.toFixed(6)}`);
results.data.forEach(record => {
console.log(`Breach: ${record.breach_name}, Date: ${record.breach_date}`);
});
} catch (error) {
console.error('Search failed:', error.message);
}
}
basicSearch();Production ReadyComplete JavaScript Implementation
Full-featured client with error handling, retry logic, and bulk operations
leakzero-client.js
const axios = require('axios');
class LeakZeroClient {
constructor(apiKey, options = {}) {
if (!apiKey) {
throw new Error('API key is required');
}
this.apiKey = apiKey;
this.baseURL = options.baseURL || 'https://api.leakzero.io/api/v1';
this.maxRetries = options.maxRetries || 3;
this.defaultTimeout = options.timeout || 120000;
this.client = axios.create({
baseURL: this.baseURL,
timeout: this.defaultTimeout,
headers: {
'Content-Type': 'application/json',
'User-Agent': 'LeakZero-JavaScript-SDK/1.0.0',
},
});
this.setupInterceptors();
}
setupInterceptors() {
this.client.interceptors.request.use(
(config) => {
const timestamp = new Date().toISOString();
config.headers['x-api-key'] = this.apiKey;
config.headers['x-timestamp'] = timestamp;
config.headers['x-request-id'] = this.generateRequestId();
return config;
},
(error) => Promise.reject(error)
);
this.client.interceptors.response.use(
(response) => {
console.log(`API Request successful: ${response.config.method?.toUpperCase()} ${response.config.url}`);
return response;
},
async (error) => {
const config = error.config;
if (!config || (config.__retryCount || 0) >= this.maxRetries) {
return Promise.reject(this.formatError(error));
}
config.__retryCount = (config.__retryCount || 0) + 1;
if (error.response?.status === 429) {
const retryAfter = parseInt(error.response.headers['retry-after']) || 5;
const delay = Math.min(retryAfter * 1000, 30000);
console.warn(`Rate limited. Retrying after ${delay}ms (attempt ${config.__retryCount}/${this.maxRetries})`);
await this.delay(delay);
return this.client(config);
}
if (error.response?.status >= 500) {
const delay = Math.min(1000 * Math.pow(2, config.__retryCount - 1), 10000);
console.warn(`Server error. Retrying after ${delay}ms (attempt ${config.__retryCount}/${this.maxRetries})`);
await this.delay(delay);
return this.client(config);
}
return Promise.reject(this.formatError(error));
}
);
}
generateRequestId() {
return `req_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
}
delay(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
formatError(error) {
const response = error.response;
const data = response?.data || {};
return {
error: data.error || 'API_ERROR',
message: data.message || error.message || 'An unknown error occurred',
statusCode: response?.status || 0,
retryAfter: response?.headers['retry-after']
? parseInt(response.headers['retry-after'])
: undefined,
};
}
async getBalance() {
try {
const response = await this.client.get('/public/balance');
return response.data;
} catch (error) {
console.error('Failed to get balance:', error.message);
throw error;
}
}
async regularSearch(options) {
try {
if (!options || !options.field || !options.value) {
throw new Error('Field and value are required for search');
}
const payload = {
field: options.field,
value: options.value,
option: options.option || 'exact',
rowLimit: Math.min(options.rowLimit || 1000, 10000),
};
if (options.autoExport) {
payload.autoExport = JSON.stringify(options.autoExport);
}
console.log(`Searching for ${options.field}: ${options.value}`);
const response = await this.client.post('/public/search/regular', payload);
const result = {
data: response.data.data || [],
totalResults: parseInt(response.headers['x-rows-returned'] || '0'),
cost: parseInt(response.headers['x-charge'] || '0') / 1_000_000_000,
requestId: response.headers['x-request-id'] || 'unknown',
processingTime: parseInt(response.headers['x-processing-time'] || '0'),
};
console.log(`Found ${result.totalResults} records (Cost: $${result.cost.toFixed(6)})`);
return result;
} catch (error) {
console.error('Regular search failed:', error.message);
throw error;
}
}
async advancedSearch(queries, options = {}) {
try {
if (!Array.isArray(queries) || queries.length === 0) {
throw new Error('At least one query is required for advanced search');
}
const payload = {
queries: queries.map(q => ({
field: q.field,
value: q.value,
option: q.option || 'exact',
})),
rowLimit: Math.min(options.rowLimit || 1000, 10000),
};
console.log(`Advanced search with ${queries.length} queries`);
const response = await this.client.post('/public/search/advanced', payload);
const result = {
data: response.data.data || [],
totalResults: parseInt(response.headers['x-rows-returned'] || '0'),
cost: parseInt(response.headers['x-charge'] || '0') / 1_000_000_000,
requestId: response.headers['x-request-id'] || 'unknown',
processingTime: parseInt(response.headers['x-processing-time'] || '0'),
};
console.log(`Advanced search completed (Cost: $${result.cost.toFixed(6)})`);
return result;
} catch (error) {
console.error('Advanced search failed:', error.message);
throw error;
}
}
async identityLinkingSearch(field, value, options = {}) {
try {
const identifier = field + ':' + value;
const payload = {
field,
value,
maxDepth: Math.min(options.maxDepth || 2, 4),
minConfidence: options.minConfidence || 0.35,
maxResults: Math.min(options.maxResults || 50, 100),
includePossible: options.includePossible ?? true,
...(options.contextDomain ? { contextDomain: options.contextDomain } : {}),
};
console.log(`Identity linking search for: ${identifier}`);
const response = await this.client.post('/public/search/identity-linking', payload);
const result = {
data: response.data.data?.results || [],
linkedIdentities: response.data.data?.results || [],
summary: response.data.data?.summary || {},
metadata: response.data.data?.metadata || {},
disclaimer: response.data.data?.disclaimer,
totalResults: response.data.rowsReturned || 0,
cost: parseInt(response.headers['x-charge'] || '0') / 1_000_000_000,
requestId: response.data.requestId || 'unknown',
processingTime: response.data.data?.metadata?.processingTimeMs || 0,
};
console.log(`Identity linking found ${result.totalResults} records with ${result.linkedIdentities.length} linked identities`);
return result;
} catch (error) {
console.error('Identity linking search failed:', error.message);
throw error;
}
}
async getUsageStats() {
try {
const response = await this.client.get('/public/usage/stats');
return response.data;
} catch (error) {
console.error('Failed to get usage stats:', error.message);
throw error;
}
}
async getSearchHistory(options = {}) {
try {
const params = {
limit: Math.min(options.limit || 50, 100),
offset: options.offset || 0,
};
const response = await this.client.get('/public/search-history', { params });
return response.data;
} catch (error) {
console.error('Failed to get search history:', error.message);
throw error;
}
}
analyzeBreaches(records) {
const breachDistribution = {};
const dateDistribution = {};
const fieldDistribution = {};
records.forEach(record => {
const breachName = record.breach_name || 'Unknown';
breachDistribution[breachName] = (breachDistribution[breachName] || 0) + 1;
const breachDate = record.breach_date;
if (breachDate) {
try {
const year = new Date(breachDate).getFullYear().toString();
if (!isNaN(year)) {
dateDistribution[year] = (dateDistribution[year] || 0) + 1;
}
} catch (e) {
}
}
Object.keys(record).forEach(field => {
if (record[field] && field !== 'breach_name' && field !== 'breach_date') {
fieldDistribution[field] = (fieldDistribution[field] || 0) + 1;
}
});
});
return {
breachDistribution,
dateDistribution,
fieldDistribution,
totalRecords: records.length,
uniqueBreaches: Object.keys(breachDistribution).length,
dateRange: {
earliest: Math.min(...Object.keys(dateDistribution).map(Number)),
latest: Math.max(...Object.keys(dateDistribution).map(Number)),
},
};
}
async bulkSearch(searchTerms, options = {}) {
const results = [];
const delay = options.delay || 200;
const batchSize = options.batchSize || 5;
console.log(`Starting bulk search for ${searchTerms.length} terms`);
for (let i = 0; i < searchTerms.length; i += batchSize) {
const batch = searchTerms.slice(i, i + batchSize);
const batchPromises = batch.map(async (term, index) => {
try {
if (index > 0) {
await this.delay(delay);
}
const result = await this.regularSearch({
field: options.field || 'email',
value: term,
option: options.option || 'exact',
rowLimit: options.rowLimit || 100,
});
return { term, result, success: true };
} catch (error) {
console.error(`Failed to search for ${term}:`, error.message);
return { term, error: error.message, success: false };
}
});
const batchResults = await Promise.all(batchPromises);
results.push(...batchResults);
console.log(`Processed ${Math.min(i + batchSize, searchTerms.length)}/${searchTerms.length} terms`);
if (i + batchSize < searchTerms.length) {
await this.delay(delay * 2);
}
}
const successful = results.filter(r => r.success);
const failed = results.filter(r => !r.success);
console.log(`Bulk search completed: ${successful.length} successful, ${failed.length} failed`);
return {
successful,
failed,
totalProcessed: results.length,
successRate: (successful.length / results.length * 100).toFixed(2) + '%',
};
}
}
async function demonstrateUsage() {
const client = new LeakZeroClient(process.env.LEAKZERO_API_KEY, {
maxRetries: 5,
timeout: 60000,
});
try {
console.log('Checking API balance...');
const balance = await client.getBalance();
console.log(`Current balance: $${balance.balance.toFixed(6)}`);
if (balance.balance < 0.001) {
throw new Error('Insufficient balance. Please top up your account.');
}
console.log('\nPerforming regular search...');
const emailSearch = await client.regularSearch({
field: 'email',
value: '[email protected]',
option: 'exact',
rowLimit: 100,
autoExport: {
format: 'json',
enableAutoExport: true,
},
});
console.log(`Found ${emailSearch.totalResults} records for email search`);
if (emailSearch.data.length > 0) {
const analysis = client.analyzeBreaches(emailSearch.data);
console.log('\nBreach Analysis:');
console.log('Total unique breaches:', analysis.uniqueBreaches);
console.log('Date range:', `${analysis.dateRange.earliest} - ${analysis.dateRange.latest}`);
console.log('Top 5 breaches:');
Object.entries(analysis.breachDistribution)
.sort(([,a], [,b]) => b - a)
.slice(0, 5)
.forEach(([breach, count]) => {
console.log(` - ${breach}: ${count} records`);
});
}
console.log('\nPerforming advanced search...');
const advancedSearch = await client.advancedSearch([
{ field: 'email', value: '[email protected]' },
{ field: 'username', value: 'johndoe' },
]);
console.log(`Advanced search found ${advancedSearch.totalResults} records`);
console.log('\nPerforming identity linking search...');
const identitySearch = await client.identityLinkingSearch('email', '[email protected]', {
includePossible: true,
maxDepth: 2,
maxResults: 50,
});
console.log(`Identity linking found ${identitySearch.linkedIdentities.length} linked identities`);
console.log('\nGetting usage statistics...');
const stats = await client.getUsageStats();
console.log('Usage stats:', {
totalRequests: stats.totalRequests,
totalCost: `$${stats.totalCost.toFixed(6)}`,
averageResponseTime: `${stats.averageResponseTime}ms`,
});
} catch (error) {
console.error('Error:', error.message);
if (error.statusCode === 401) {
console.error('Authentication failed. Please check your API key.');
} else if (error.statusCode === 403) {
console.error('Insufficient balance or rate limit exceeded.');
} else if (error.statusCode >= 500) {
console.error('Server error. Please try again later.');
}
}
}
module.exports = { LeakZeroClient };
if (require.main === module) {
demonstrateUsage();
}ES6 Features: Uses modern JavaScript with async/await and ES6 classes
Bulk Operations: Built-in support for bulk searches with rate limiting
Data Analysis: Comprehensive breach data analysis utilities
Key Features
JavaScript Benefits
- No compilation step required
- Fast development iteration
- Extensive NPM ecosystem
- Easy deployment and scaling
Advanced Features
- Smart bulk search operations
- Comprehensive data analysis
- Identity linking searches
- Usage statistics and monitoring