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 -y

package.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
Documentation - LeakZero | LeakZero