Command Palette

Search for a command to run...

C++

Modern C++ SDK with RAII and smart pointer management

Installation & Setup
Required dependencies and build setup for C++ development

System Dependencies

sudo apt-get update
sudo apt-get install -y build-essential cmake pkg-config
sudo apt-get install -y libcurl4-openssl-dev libjsoncpp-dev

brew install cmake curl jsoncpp

vcpkg install curl jsoncpp

mkdir build && cd build
cmake ..
make

export LEAKZERO_API_KEY=your_api_key_here

./leakzero_client

CMakeLists.txt

cmake_minimum_required(VERSION 3.15)
project(LeakZeroClient)

set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)

# Find required packages
find_package(PkgConfig REQUIRED)
find_package(CURL REQUIRED)

# Find JsonCpp
pkg_check_modules(JSONCPP jsoncpp)
if(NOT JSONCPP_FOUND)
    find_package(PkgConfig QUIET)
    pkg_check_modules(JSONCPP REQUIRED jsoncpp)
endif()

# Add executable
add_executable(leakzero_client main.cpp)

# Link libraries
target_link_libraries(leakzero_client
    PRIVATE
    CURL::libcurl
    ${JSONCPP_LIBRARIES}
)

target_include_directories(leakzero_client
    PRIVATE
    ${JSONCPP_INCLUDE_DIRS}
)
target_compile_options(leakzero_client
    PRIVATE
    ${JSONCPP_CFLAGS_OTHER}
)

Dockerfile

FROM ubuntu:22.04 AS builder

RUN apt-get update && apt-get install -y \
    build-essential \
    cmake \
    pkg-config \
    libcurl4-openssl-dev \
    libjsoncpp-dev \
    && rm -rf /var/lib/apt/lists/*

WORKDIR /app
COPY . .

RUN mkdir build && cd build && \
    cmake .. && \
    make

FROM ubuntu:22.04
RUN apt-get update && apt-get install -y \
    libcurl4 \
    libjsoncpp25 \
    && rm -rf /var/lib/apt/lists/*

WORKDIR /root/
COPY --from=builder /app/build/leakzero_client .

CMD ["./leakzero_client"]
C++ Standard: This example requires C++17 for modern features like std::optional and structured bindings. It depends on libcurl for HTTP and JsonCpp for JSON parsing.
Quick Start Example
Simple example to get you started with C++

basic_usage.cpp

#include <iostream>
#include <cstdlib>
#include "leakzero_client.hpp"

int main() {
    try {
        LeakZero::Client client(std::getenv("LEAKZERO_API_KEY"));

        // Simple search
        LeakZero::SearchOptions options("email", "[email protected]");
        options.rowLimit = 100;

        auto result = client.regularSearch(options);

        std::cout << "Found " << result.totalResults << " records\n";
        std::cout << "Cost: $" << std::fixed << std::setprecision(6) << result.cost << std::endl;
        for (size_t i = 0; i < std::min(size_t(5), result.data.size()); ++i) {
            const auto& record = result.data[i];
            auto breach = record.get("breach_name", "Unknown").asString();
            auto date = record.get("breach_date", "Unknown").asString();
            std::cout << "Breach: " << breach << ", Date: " << date << std::endl;
        }

        return 0;
    } catch (const std::exception& e) {
        std::cerr << "Error: " << e.what() << std::endl;
        return 1;
    }
}
Production ReadyComplete C++ Implementation
Full-featured client with RAII, exception handling, and modern C++ features

leakzero_client.hpp / main.cpp

#include <iostream>
#include <string>
#include <vector>
#include <map>
#include <memory>
#include <future>
#include <chrono>
#include <thread>
#include <random>
#include <algorithm>
#include <sstream>
#include <iomanip>
#include <exception>
#include <curl/curl.h>
#include <json/json.h>

namespace LeakZero {
class ApiError;
class SearchOptions;
class SearchResult;
class UsageStats;
class BalanceResponse;
class HttpResponse {
public:
    int statusCode;
    std::string body;
    std::map<std::string, std::string> headers;

    HttpResponse(int code = 0) : statusCode(code) {}
};

class ApiError : public std::exception {
private:
    std::string error_;
    std::string message_;
    int statusCode_;

public:
    ApiError(const std::string& error, const std::string& message, int statusCode)
        : error_(error), message_(message), statusCode_(statusCode) {}

    const char* what() const noexcept override {
        static std::string result = "API Error [" + std::to_string(statusCode_) +
                                   "]: " + error_ + " - " + message_;
        return result.c_str();
    }

    const std::string& getError() const { return error_; }
    const std::string& getMessage() const { return message_; }
    int getStatusCode() const { return statusCode_; }
};

class SearchOptions {
public:
    std::string field;
    std::string value;
    std::string option = "exact";
    int rowLimit = 1000;
    Json::Value autoExport;

    SearchOptions(const std::string& f, const std::string& v)
        : field(f), value(v) {}

    Json::Value toJson() const {
        Json::Value json;
        json["field"] = field;
        json["value"] = value;
        json["option"] = option;
        json["rowLimit"] = rowLimit;
        if (!autoExport.isNull()) {
            json["autoExport"] = autoExport;
        }
        return json;
    }
};

class SearchResult {
public:
    std::vector<Json::Value> data;
    int totalResults = 0;
    double cost = 0.0;
    std::string requestId;
    int processingTime = 0;
    Json::Value analysis;

    void addRecord(const Json::Value& record) {
        data.push_back(record);
    }

    size_t size() const { return data.size(); }
    bool empty() const { return data.empty(); }
};

class BalanceResponse {
public:
    double balance = 0.0;
    std::string currency;
    std::string lastUsage;

    static BalanceResponse fromJson(const Json::Value& json) {
        BalanceResponse response;
        response.balance = json.get("balance", 0.0).asDouble();
        response.currency = json.get("currency", "USD").asString();
        response.lastUsage = json.get("lastUsage", "").asString();
        return response;
    }
};

class UsageStats {
public:
    int totalRequests = 0;
    double totalCost = 0.0;
    int requestsToday = 0;
    double costToday = 0.0;
    int requestsThisWeek = 0;
    double costThisWeek = 0.0;

    static UsageStats fromJson(const Json::Value& json) {
        UsageStats stats;
        stats.totalRequests = json.get("totalRequests", 0).asInt();
        stats.totalCost = json.get("totalCost", 0.0).asDouble();
        stats.requestsToday = json.get("requestsToday", 0).asInt();
        stats.costToday = json.get("costToday", 0.0).asDouble();
        stats.requestsThisWeek = json.get("requestsThisWeek", 0).asInt();
        stats.costThisWeek = json.get("costThisWeek", 0.0).asDouble();
        return stats;
    }
};

class Logger {
private:
    bool enabled_;

public:
    Logger(bool enabled = true) : enabled_(enabled) {}

    template<typename... Args>
    void info(const std::string& format, Args... args) {
        if (enabled_) {
            printf(("[INFO] " + format + "\n").c_str(), args...);
        }
    }

    template<typename... Args>
    void warning(const std::string& format, Args... args) {
        if (enabled_) {
            printf(("[WARN] " + format + "\n").c_str(), args...);
        }
    }

    template<typename... Args>
    void error(const std::string& format, Args... args) {
        if (enabled_) {
            printf(("[ERROR] " + format + "\n").c_str(), args...);
        }
    }
};

// HTTP Client implementation
class HttpClient {
private:
    CURL* curl_;
    struct curl_slist* headers_;
    Logger logger_;

    static size_t WriteCallback(void* contents, size_t size, size_t nmemb, std::string* response) {
        size_t totalSize = size * nmemb;
        response->append((char*)contents, totalSize);
        return totalSize;
    }

    static size_t HeaderCallback(char* buffer, size_t size, size_t nitems, std::map<std::string, std::string>* headers) {
        size_t totalSize = size * nitems;
        std::string header(buffer, totalSize);

        size_t colonPos = header.find(':');
        if (colonPos != std::string::npos) {
            std::string key = header.substr(0, colonPos);
            std::string value = header.substr(colonPos + 1);

            // Trim whitespace
            key.erase(key.find_last_not_of(" \t\r\n") + 1);
            value.erase(0, value.find_first_not_of(" \t\r\n"));
            value.erase(value.find_last_not_of(" \t\r\n") + 1);

            (*headers)[key] = value;
        }

        return totalSize;
    }

public:
    HttpClient() : logger_(true) {
        curl_global_init(CURL_GLOBAL_DEFAULT);
        curl_ = curl_easy_init();
        headers_ = nullptr;

        if (curl_) {
            curl_easy_setopt(curl_, CURLOPT_TIMEOUT, 120L);
            curl_easy_setopt(curl_, CURLOPT_CONNECTTIMEOUT, 30L);
            curl_easy_setopt(curl_, CURLOPT_FOLLOWLOCATION, 1L);
            curl_easy_setopt(curl_, CURLOPT_SSL_VERIFYPEER, 1L);
            curl_easy_setopt(curl_, CURLOPT_SSL_VERIFYHOST, 2L);
            curl_easy_setopt(curl_, CURLOPT_WRITEFUNCTION, WriteCallback);
            curl_easy_setopt(curl_, CURLOPT_HEADERFUNCTION, HeaderCallback);
        }
    }

    ~HttpClient() {
        if (headers_) {
            curl_slist_free_all(headers_);
        }
        if (curl_) {
            curl_easy_cleanup(curl_);
        }
        curl_global_cleanup();
    }

    void setHeaders(const std::map<std::string, std::string>& headerMap) {
        if (headers_) {
            curl_slist_free_all(headers_);
            headers_ = nullptr;
        }

        for (const auto& pair : headerMap) {
            std::string header = pair.first + ": " + pair.second;
            headers_ = curl_slist_append(headers_, header.c_str());
        }

        if (headers_) {
            curl_easy_setopt(curl_, CURLOPT_HTTPHEADER, headers_);
        }
    }

    HttpResponse get(const std::string& url) {
        return makeRequest("GET", url, "");
    }

    HttpResponse post(const std::string& url, const std::string& data) {
        return makeRequest("POST", url, data);
    }

private:
    HttpResponse makeRequest(const std::string& method, const std::string& url, const std::string& data) {
        if (!curl_) {
            throw std::runtime_error("CURL not initialized");
        }

        HttpResponse response;
        std::string responseBody;

        curl_easy_setopt(curl_, CURLOPT_URL, url.c_str());
        curl_easy_setopt(curl_, CURLOPT_WRITEDATA, &responseBody);
        curl_easy_setopt(curl_, CURLOPT_HEADERDATA, &response.headers);

        if (method == "POST") {
            curl_easy_setopt(curl_, CURLOPT_POSTFIELDS, data.c_str());
            curl_easy_setopt(curl_, CURLOPT_POSTFIELDSIZE, data.length());
        } else {
            curl_easy_setopt(curl_, CURLOPT_HTTPGET, 1L);
        }

        CURLcode res = curl_easy_perform(curl_);
        if (res != CURLE_OK) {
            throw std::runtime_error("CURL error: " + std::string(curl_easy_strerror(res)));
        }

        long httpCode = 0;
        curl_easy_getinfo(curl_, CURLINFO_RESPONSE_CODE, &httpCode);

        response.statusCode = static_cast<int>(httpCode);
        response.body = responseBody;

        return response;
    }
class Client {
private:
    std::string apiKey_;
    std::string baseUrl_;
    std::unique_ptr<HttpClient> httpClient_;
    Logger logger_;
    int maxRetries_;

    std::string generateRequestId() {
        std::random_device rd;
        std::mt19937 gen(rd());
        std::uniform_int_distribution<> dis(0, 15);

        auto now = std::chrono::system_clock::now();
        auto timestamp = std::chrono::duration_cast<std::chrono::seconds>(now.time_since_epoch()).count();

        std::stringstream ss;
        ss << "req_" << timestamp << "_";
        for (int i = 0; i < 8; ++i) {
            ss << std::hex << dis(gen);
        }

        return ss.str();
    }

    std::string getCurrentTimestamp() {
        auto now = std::chrono::system_clock::now();
        auto time_t = std::chrono::system_clock::to_time_t(now);

        std::stringstream ss;
        ss << std::put_time(std::gmtime(&time_t), "%Y-%m-%dT%H:%M:%SZ");
        return ss.str();
    }

    std::map<std::string, std::string> generateHeaders() {
        return {
            {"x-api-key", apiKey_},
            {"x-timestamp", getCurrentTimestamp()},
            {"x-request-id", generateRequestId()},
            {"Content-Type", "application/json"},
            {"User-Agent", "LeakZero-Cpp-SDK/1.0.0"}
        };
    }

    HttpResponse makeRequestWithRetry(const std::string& method, const std::string& endpoint, const Json::Value& payload = Json::Value()) {
        std::string url = baseUrl_ + endpoint;

        for (int attempt = 0; attempt <= maxRetries_; ++attempt) {
            try {
                httpClient_->setHeaders(generateHeaders());

                HttpResponse response;
                if (method == "POST" && !payload.isNull()) {
                    Json::StreamWriterBuilder builder;
                    std::string jsonString = Json::writeString(builder, payload);
                    response = httpClient_->post(url, jsonString);
                } else {
                    response = httpClient_->get(url);
                }
                if (response.statusCode == 429) {
                    int retryAfter = 5; // default
                    auto it = response.headers.find("Retry-After");
                    if (it != response.headers.end()) {
                        retryAfter = std::min(std::stoi(it->second), 30);
                    }

                    logger_.warning("Rate limited. Waiting %d seconds", retryAfter);
                    std::this_thread::sleep_for(std::chrono::seconds(retryAfter));
                    continue;
                }

                if (response.statusCode >= 400) {
                    Json::Reader reader;
                    Json::Value errorJson;

                    std::string error = "HTTP_ERROR";
                    std::string message = "HTTP " + std::to_string(response.statusCode);

                    if (reader.parse(response.body, errorJson)) {
                        error = errorJson.get("error", error).asString();
                        message = errorJson.get("message", message).asString();
                    }

                    throw ApiError(error, message, response.statusCode);
                }

                logger_.info("API Request successful: %s %s", method.c_str(), endpoint.c_str());
                return response;

            } catch (const ApiError& e) {
                if (attempt == maxRetries_) {
                    throw;
                }
                logger_.warning("Request attempt %d failed: %s", attempt + 1, e.what());
                std::this_thread::sleep_for(std::chrono::seconds(attempt + 1));
            }
        }

        throw std::runtime_error("Max retries exceeded");
    }

public:
    Client(const std::string& apiKey, const std::string& baseUrl = "https://api.leakzero.io/api/v1")
        : apiKey_(apiKey), baseUrl_(baseUrl), logger_(true), maxRetries_(3) {

        if (apiKey_.empty()) {
            throw std::invalid_argument("API key is required");
        }

        // Remove trailing slash from baseUrl
        if (!baseUrl_.empty() && baseUrl_.back() == '/') {
            baseUrl_.pop_back();
        }

        httpClient_ = std::make_unique<HttpClient>();
    }

    void setMaxRetries(int maxRetries) {
        maxRetries_ = maxRetries;
    }

    void setLogging(bool enabled) {
        logger_ = Logger(enabled);
    }
    BalanceResponse getBalance() {
        try {
            HttpResponse response = makeRequestWithRetry("GET", "/public/balance");

            Json::Reader reader;
            Json::Value json;
            if (!reader.parse(response.body, json)) {
                throw std::runtime_error("Failed to parse balance response");
            }

            return BalanceResponse::fromJson(json);

        } catch (const std::exception& e) {
            logger_.error("Failed to get balance: %s", e.what());
            throw;
        }
    }
    SearchResult regularSearch(const SearchOptions& options) {
        try {
            // Enforce limits
            SearchOptions limitedOptions = options;
            limitedOptions.rowLimit = std::min(limitedOptions.rowLimit, 10000);

            logger_.info("Searching for %s: %s", options.field.c_str(), options.value.c_str());

            HttpResponse response = makeRequestWithRetry("POST", "/public/search/regular", limitedOptions.toJson());

            Json::Reader reader;
            Json::Value json;
            if (!reader.parse(response.body, json)) {
                throw std::runtime_error("Failed to parse search response");
            }

            SearchResult result;

            auto it = response.headers.find("x-rows-returned");
            if (it != response.headers.end()) {
                result.totalResults = std::stoi(it->second);
            }

            it = response.headers.find("x-charge");
            if (it != response.headers.end()) {
                result.cost = std::stoi(it->second) / 1000000000.0;
            }

            it = response.headers.find("x-processing-time");
            if (it != response.headers.end()) {
                result.processingTime = std::stoi(it->second);
            }

            result.requestId = response.headers["x-request-id"];
            if (json.isMember("data") && json["data"].isArray()) {
                for (const auto& item : json["data"]) {
                    result.addRecord(item);
                }
            }

            logger_.info("Found %d records (Cost: $%.6f)", result.totalResults, result.cost);
            return result;

        } catch (const std::exception& e) {
            logger_.error("Regular search failed: %s", e.what());
            throw;
        }
    }

    SearchResult advancedSearch(const std::vector<SearchOptions>& queries, int rowLimit = 1000) {
        try {
            if (queries.empty()) {
                throw std::invalid_argument("At least one query is required");
            }

            rowLimit = std::min(rowLimit, 10000);

            Json::Value payload;
            Json::Value queryArray(Json::arrayValue);

            for (const auto& query : queries) {
                queryArray.append(query.toJson());
            }

            payload["queries"] = queryArray;
            payload["rowLimit"] = rowLimit;

            logger_.info("Advanced search with %zu queries", queries.size());

            HttpResponse response = makeRequestWithRetry("POST", "/public/search/advanced", payload);

            Json::Reader reader;
            Json::Value json;
            if (!reader.parse(response.body, json)) {
                throw std::runtime_error("Failed to parse advanced search response");
            }

            SearchResult result;

            logger_.info("Advanced search completed");
            return result;

        } catch (const std::exception& e) {
            logger_.error("Advanced search failed: %s", e.what());
            throw;
        }
    }

    SearchResult identityLinkingSearch(const std::string& field, const std::string& value, const std::string& contextDomain = "", int maxResults = 50, bool includePossible = true) {
        try {
            maxResults = std::min(maxResults, 100);

            Json::Value payload;
            payload["field"] = field;
            payload["value"] = value;
            payload["maxDepth"] = 2;
            payload["minConfidence"] = 0.35;
            payload["maxResults"] = maxResults;
            payload["includePossible"] = includePossible;
            if (!contextDomain.empty()) {
                payload["contextDomain"] = contextDomain;
            }

            logger_.info("identity linking search for: %s:%s", field.c_str(), value.c_str());

            HttpResponse response = makeRequestWithRetry("POST", "/public/search/identity-linking", payload);

            Json::Reader reader;
            Json::Value json;
            if (!reader.parse(response.body, json)) {
                throw std::runtime_error("Failed to parse identity linking response");
            }

            SearchResult result;
            const Json::Value& correlation = json["data"];
            result.analysis["summary"] = correlation["summary"];
            result.analysis["metadata"] = correlation["metadata"];

            if (correlation.isMember("results") && correlation["results"].isArray()) {
                for (const auto& item : correlation["results"]) {
                    result.addRecord(item);
                }
            }

            logger_.info("Identity linking completed");
            return result;

        } catch (const std::exception& e) {
            logger_.error("Identity linking search failed: %s", e.what());
            throw;
        }
    }
    UsageStats getUsageStats() {
        try {
            HttpResponse response = makeRequestWithRetry("GET", "/public/usage/stats");

            Json::Reader reader;
            Json::Value json;
            if (!reader.parse(response.body, json)) {
                throw std::runtime_error("Failed to parse usage stats response");
            }

            return UsageStats::fromJson(json);

        } catch (const std::exception& e) {
            logger_.error("Failed to get usage stats: %s", e.what());
            throw;
        }
    }
    void analyzeBreaches(const SearchResult& result) {
        if (result.empty()) {
            std::cout << "No records to analyze\n";
            return;
        }

        std::map<std::string, int> breachDistribution;
        std::map<std::string, int> dateDistribution;
        std::map<std::string, int> fieldDistribution;

        for (const auto& record : result.data) {
            std::string breachName = record.get("breach_name", "Unknown").asString();
            breachDistribution[breachName]++;

            std::string breachDate = record.get("breach_date", "").asString();
            if (!breachDate.empty() && breachDate.length() >= 4) {
                std::string year = breachDate.substr(0, 4);
                dateDistribution[year]++;
            }
            for (const auto& memberName : record.getMemberNames()) {
                if (!record[memberName].isNull() && memberName != "breach_name" && memberName != "breach_date") {
                    fieldDistribution[memberName]++;
                }
            }
        }

        std::cout << "\nBreach Analysis:\n";
        std::cout << "Total records: " << result.size() << "\n";
        std::cout << "Unique breaches: " << breachDistribution.size() << "\n";

        std::cout << "\nTop breaches:\n";
        std::vector<std::pair<std::string, int>> sortedBreaches(breachDistribution.begin(), breachDistribution.end());
        std::sort(sortedBreaches.begin(), sortedBreaches.end(),
                  [](const auto& a, const auto& b) { return a.second > b.second; });

        for (int i = 0; i < std::min(5, (int)sortedBreaches.size()); ++i) {
            std::cout << "  - " << sortedBreaches[i].first << ": " << sortedBreaches[i].second << " records\n";
        }
    }
    std::vector<std::future<SearchResult>> bulkSearchAsync(const std::vector<std::string>& searchTerms,
                                                           const std::string& field = "email",
                                                           int batchSize = 5) {
        std::vector<std::future<SearchResult>> futures;

        logger_.info("Starting bulk search for %zu terms", searchTerms.size());

        for (const auto& term : searchTerms) {
            auto future = std::async(std::launch::async, [this, field, term]() {
                std::this_thread::sleep_for(std::chrono::milliseconds(200));
                return this->regularSearch(SearchOptions(field, term));
            });
            futures.push_back(std::move(future));
        }

        return futures;
    }
};

} // namespace LeakZero
int main() {
    try {
        const char* apiKey = std::getenv("LEAKZERO_API_KEY");
        if (!apiKey) {
            std::cerr << "LEAKZERO_API_KEY environment variable is required\n";
            return 1;
        }

        LeakZero::Client client(apiKey);
        client.setMaxRetries(5);

        std::cout << "Checking API balance...\n";
        auto balance = client.getBalance();
        std::cout << "Current balance: $" << std::fixed << std::setprecision(6) << balance.balance << std::endl;

        if (balance.balance < 0.001) {
            std::cerr << "Insufficient balance. Please top up your account.\n";
            return 1;
        }

        std::cout << "\nPerforming regular search...\n";
        LeakZero::SearchOptions options("email", "[email protected]");
        options.rowLimit = 100;

        auto result = client.regularSearch(options);
        std::cout << "Found " << result.totalResults << " records\n";
        std::cout << "Cost: $" << std::fixed << std::setprecision(6) << result.cost << std::endl;
        for (size_t i = 0; i < std::min(size_t(5), result.data.size()); ++i) {
            const auto& record = result.data[i];
            std::string breachName = record.get("breach_name", "Unknown").asString();
            std::string breachDate = record.get("breach_date", "Unknown").asString();
            std::cout << "Breach: " << breachName << ", Date: " << breachDate << std::endl;
        }

        client.analyzeBreaches(result);

        std::cout << "\nGetting usage statistics...\n";
        auto stats = client.getUsageStats();
        std::cout << "Total requests: " << stats.totalRequests << std::endl;
        std::cout << "Total cost: $" << std::fixed << std::setprecision(6) << stats.totalCost << std::endl;

        return 0;

    } catch (const std::exception& e) {
        std::cerr << "Error: " << e.what() << std::endl;
        return 1;
    }
}
RAII: Resource Acquisition Is Initialization with automatic cleanup and exception safety
Modern C++: C++17 features with smart pointers, futures, and STL containers
Performance: Efficient memory management with zero-copy operations where possible
Key Features

Core Features

  • RAII-based resource management
  • Exception-safe error handling
  • Automatic retry with exponential backoff
  • Rate limit handling with intelligent delays
  • Template-based type safety

Performance Features

  • Future-based asynchronous operations
  • Connection pooling with libcurl
  • Move semantics for efficient data transfer
  • Zero-copy JSON parsing where possible
  • Configurable timeout and retry settings
Best Practices:
  • Always use environment variables for API keys
  • Implement proper exception handling in production
  • Use std::future for concurrent operations
  • Monitor your API usage and balance regularly
  • Leverage RAII for automatic resource cleanup
Documentation - LeakZero | LeakZero