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_clientCMakeLists.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