Command Palette
Search for a command to run...
Go
Go SDK with goroutines and efficient concurrent processing
Installation & Setup
Required setup and dependencies for Go development
Project Setup
go mod init leakzero-example
go get github.com/google/uuid
export LEAKZERO_API_KEY=your_api_key_here
go run main.gogo.mod
module leakzero-example
go 1.21
require (
github.com/google/uuid v1.4.0
)Dockerfile
FROM golang:1.21-alpine AS builder
WORKDIR /app
COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN go build -o leakzero-client main.go
FROM alpine:latest
RUN apk --no-cache add ca-certificates
WORKDIR /root/
COPY --from=builder /app/leakzero-client .
CMD ["./leakzero-client"]Go Version: This example requires Go 1.18+ for generics support and improved error handling. The UUID package is the only external dependency.
Quick Start Example
Simple example to get you started with Go
basic_usage.go
package main
import (
"context"
"fmt"
"log"
"os"
)
func main() {
client := NewLeakZeroClient(os.Getenv("LEAKZERO_API_KEY"))
ctx := context.Background()
result, err := client.RegularSearch(ctx, SearchOptions{
Field: "email",
Value: "[email protected]",
RowLimit: 100,
})
if err != nil {
log.Fatal(err)
}
fmt.Printf("Found %d records\n", result.TotalResults)
fmt.Printf("Cost: $%.6f\n", result.Cost)
for i, record := range result.Data {
if i >= 5 {
break
}
breach := record["breach_name"]
date := record["breach_date"]
fmt.Printf("Breach: %v, Date: %v\n", breach, date)
}
}Production ReadyComplete Go Implementation
Full-featured client with concurrency, error handling, and enterprise features
main.go
package main
import (
"bytes"
"context"
"encoding/json"
"fmt"
"io"
"log"
"net/http"
"os"
"strconv"
"strings"
"sync"
"time"
"github.com/google/uuid"
)
type SearchOptions struct {
Field string `json:"field"`
Value string `json:"value"`
Option string `json:"option,omitempty"`
RowLimit int `json:"rowLimit,omitempty"`
AutoExport map[string]interface{} `json:"autoExport,omitempty"`
}
type SearchResult struct {
Data []map[string]interface{} `json:"data"`
TotalResults int `json:"totalResults"`
Cost float64 `json:"cost"`
RequestID string `json:"requestId"`
ProcessingTime int `json:"processingTime"`
Analysis map[string]interface{} `json:"analysis,omitempty"`
}
type BalanceResponse struct {
Balance float64 `json:"balance"`
Currency string `json:"currency"`
LastUsage string `json:"lastUsage"`
}
type UsageStats struct {
TotalRequests int `json:"totalRequests"`
TotalCost float64 `json:"totalCost"`
RequestsToday int `json:"requestsToday"`
CostToday float64 `json:"costToday"`
RequestsThisWeek int `json:"requestsThisWeek"`
CostThisWeek float64 `json:"costThisWeek"`
}
type APIError struct {
Error string `json:"error"`
Message string `json:"message"`
StatusCode int `json:"statusCode"`
RetryAfter int `json:"retryAfter,omitempty"`
}
func (e *APIError) Error() string {
return fmt.Sprintf("API Error [%d]: %s - %s", e.StatusCode, e.Error, e.Message)
}
type LeakZeroClient struct {
APIKey string
BaseURL string
HTTPClient *http.Client
Logger *log.Logger
MaxRetries int
}
func NewLeakZeroClient(apiKey string, options ...func(*LeakZeroClient)) *LeakZeroClient {
if apiKey == "" {
panic("API key is required")
}
client := &LeakZeroClient{
APIKey: apiKey,
BaseURL: "https://api.leakzero.io/api/v1",
HTTPClient: &http.Client{
Timeout: 120 * time.Second,
},
Logger: log.New(os.Stdout, "[LeakZero] ", log.LstdFlags),
MaxRetries: 3,
}
for _, option := range options {
option(client)
}
return client
}
func WithBaseURL(baseURL string) func(*LeakZeroClient) {
return func(c *LeakZeroClient) {
c.BaseURL = strings.TrimSuffix(baseURL, "/")
}
}
func WithTimeout(timeout time.Duration) func(*LeakZeroClient) {
return func(c *LeakZeroClient) {
c.HTTPClient.Timeout = timeout
}
}
func WithMaxRetries(maxRetries int) func(*LeakZeroClient) {
return func(c *LeakZeroClient) {
c.MaxRetries = maxRetries
}
}
func WithLogger(logger *log.Logger) func(*LeakZeroClient) {
return func(c *LeakZeroClient) {
c.Logger = logger
}
}
func (c *LeakZeroClient) generateHeaders() map[string]string {
return map[string]string{
"x-api-key": c.APIKey,
"x-timestamp": time.Now().UTC().Format(time.RFC3339),
"x-request-id": c.generateRequestID(),
"Content-Type": "application/json",
"User-Agent": "LeakZero-Go-SDK/1.0.0",
}
}
func (c *LeakZeroClient) generateRequestID() string {
return fmt.Sprintf("req_%d_%s", time.Now().Unix(), uuid.New().String()[:8])
}
func (c *LeakZeroClient) makeRequest(ctx context.Context, method, endpoint string, payload interface{}) (*http.Response, error) {
var body io.Reader
if payload != nil {
jsonData, err := json.Marshal(payload)
if err != nil {
return nil, fmt.Errorf("failed to marshal payload: %w", err)
}
body = bytes.NewBuffer(jsonData)
}
url := c.BaseURL + endpoint
for attempt := 0; attempt <= c.MaxRetries; attempt++ {
req, err := http.NewRequestWithContext(ctx, method, url, body)
if err != nil {
return nil, fmt.Errorf("failed to create request: %w", err)
}
for key, value := range c.generateHeaders() {
req.Header.Set(key, value)
}
resp, err := c.HTTPClient.Do(req)
if err != nil {
if attempt == c.MaxRetries {
return nil, fmt.Errorf("request failed after %d attempts: %w", c.MaxRetries+1, err)
}
c.Logger.Printf("Request attempt %d failed: %v", attempt+1, err)
time.Sleep(time.Duration(attempt+1) * time.Second)
continue
}
if resp.StatusCode == 429 {
retryAfterStr := resp.Header.Get("Retry-After")
retryAfter := 5 // default
if retryAfterStr != "" {
if parsed, err := strconv.Atoi(retryAfterStr); err == nil {
retryAfter = parsed
}
}
if retryAfter > 30 {
retryAfter = 30
}
c.Logger.Printf("Rate limited. Waiting %ds", retryAfter)
resp.Body.Close()
select {
case <-time.After(time.Duration(retryAfter) * time.Second):
continue
case <-ctx.Done():
return nil, ctx.Err()
}
}
if resp.StatusCode >= 400 {
defer resp.Body.Close()
var apiError APIError
if err := json.NewDecoder(resp.Body).Decode(&apiError); err != nil {
return nil, fmt.Errorf("HTTP %d: %s", resp.StatusCode, resp.Status)
}
apiError.StatusCode = resp.StatusCode
return nil, &apiError
}
c.Logger.Printf("API Request successful: %s %s", method, endpoint)
return resp, nil
}
return nil, fmt.Errorf("max retries exceeded")
}
func (c *LeakZeroClient) GetBalance(ctx context.Context) (*BalanceResponse, error) {
resp, err := c.makeRequest(ctx, "GET", "/public/balance", nil)
if err != nil {
c.Logger.Printf("Failed to get balance: %v", err)
return nil, err
}
defer resp.Body.Close()
var balance BalanceResponse
if err := json.NewDecoder(resp.Body).Decode(&balance); err != nil {
return nil, fmt.Errorf("failed to decode balance response: %w", err)
}
return &balance, nil
}
func (c *LeakZeroClient) RegularSearch(ctx context.Context, options SearchOptions) (*SearchResult, error) {
if options.Option == "" {
options.Option = "exact"
}
if options.RowLimit == 0 {
options.RowLimit = 1000
}
if options.RowLimit > 10000 {
options.RowLimit = 10000
}
c.Logger.Printf("Searching for %s: %s", options.Field, options.Value)
resp, err := c.makeRequest(ctx, "POST", "/public/search/regular", options)
if err != nil {
c.Logger.Printf("Regular search failed: %v", err)
return nil, err
}
defer resp.Body.Close()
var data map[string]interface{}
if err := json.NewDecoder(resp.Body).Decode(&data); err != nil {
return nil, fmt.Errorf("failed to decode search response: %w", err)
}
totalResults := 0
if val := resp.Header.Get("x-rows-returned"); val != "" {
totalResults, _ = strconv.Atoi(val)
}
cost := 0.0
if val := resp.Header.Get("x-charge"); val != "" {
if charge, err := strconv.Atoi(val); err == nil {
cost = float64(charge) / 1_000_000_000
}
}
processingTime := 0
if val := resp.Header.Get("x-processing-time"); val != "" {
processingTime, _ = strconv.Atoi(val)
}
result := &SearchResult{
Data: []map[string]interface{}{},
TotalResults: totalResults,
Cost: cost,
RequestID: resp.Header.Get("x-request-id"),
ProcessingTime: processingTime,
}
if dataArray, ok := data["data"].([]interface{}); ok {
for _, item := range dataArray {
if record, ok := item.(map[string]interface{}); ok {
result.Data = append(result.Data, record)
}
}
}
c.Logger.Printf("Found %d records (Cost: $%.6f)", result.TotalResults, result.Cost)
return result, nil
}
func (c *LeakZeroClient) AdvancedSearch(ctx context.Context, queries []SearchOptions, rowLimit int) (*SearchResult, error) {
if len(queries) == 0 {
return nil, fmt.Errorf("at least one query is required")
}
if rowLimit == 0 {
rowLimit = 1000
}
if rowLimit > 10000 {
rowLimit = 10000
}
payload := map[string]interface{}{
"queries": queries,
"rowLimit": rowLimit,
}
c.Logger.Printf("Advanced search with %d queries", len(queries))
resp, err := c.makeRequest(ctx, "POST", "/public/search/advanced", payload)
if err != nil {
c.Logger.Printf("Advanced search failed: %v", err)
return nil, err
}
defer resp.Body.Close()
var data map[string]interface{}
if err := json.NewDecoder(resp.Body).Decode(&data); err != nil {
return nil, fmt.Errorf("failed to decode advanced search response: %w", err)
}
// Parse response similar to RegularSearch
result := &SearchResult{
Data: []map[string]interface{}{},
}
if dataArray, ok := data["data"].([]interface{}); ok {
for _, item := range dataArray {
if record, ok := item.(map[string]interface{}); ok {
result.Data = append(result.Data, record)
}
}
}
c.Logger.Printf("Advanced search completed")
return result, nil
}
func (c *LeakZeroClient) IdentityLinkingSearch(ctx context.Context, field, value, contextDomain string, maxResults int, includePossible bool) (*SearchResult, error) {
if maxResults == 0 {
maxResults = 50
}
if maxResults > 100 {
maxResults = 100
}
payload := map[string]interface{}{
"field": field,
"value": value,
"maxDepth": 2,
"minConfidence": 0.35,
"maxResults": maxResults,
"includePossible": includePossible,
}
if contextDomain != "" {
payload["contextDomain"] = contextDomain
}
c.Logger.Printf("identity linking search for: %s:%s", field, value)
resp, err := c.makeRequest(ctx, "POST", "/public/search/identity-linking", payload)
if err != nil {
c.Logger.Printf("Identity linking search failed: %v", err)
return nil, err
}
defer resp.Body.Close()
var data map[string]interface{}
if err := json.NewDecoder(resp.Body).Decode(&data); err != nil {
return nil, fmt.Errorf("failed to decode identity linking response: %w", err)
}
result := &SearchResult{
Data: []map[string]interface{}{},
Analysis: make(map[string]interface{}),
}
if correlation, ok := data["data"].(map[string]interface{}); ok {
result.Analysis["summary"] = correlation["summary"]
result.Analysis["metadata"] = correlation["metadata"]
if candidates, ok := correlation["results"].([]interface{}); ok {
for _, item := range candidates {
if candidate, ok := item.(map[string]interface{}); ok {
result.Data = append(result.Data, candidate)
}
}
}
}
c.Logger.Printf("Identity linking completed")
return result, nil
}
func (c *LeakZeroClient) GetUsageStats(ctx context.Context) (*UsageStats, error) {
resp, err := c.makeRequest(ctx, "GET", "/public/usage/stats", nil)
if err != nil {
c.Logger.Printf("Failed to get usage stats: %v", err)
return nil, err
}
defer resp.Body.Close()
var stats UsageStats
if err := json.NewDecoder(resp.Body).Decode(&stats); err != nil {
return nil, fmt.Errorf("failed to decode usage stats: %w", err)
}
return &stats, nil
}
func (c *LeakZeroClient) BulkSearch(ctx context.Context, searchTerms []string, field string, batchSize int, delay time.Duration) ([]map[string]interface{}, error) {
if batchSize == 0 {
batchSize = 5
}
if delay == 0 {
delay = 200 * time.Millisecond
}
var results []map[string]interface{}
var mu sync.Mutex
var wg sync.WaitGroup
c.Logger.Printf("Starting bulk search for %d terms", len(searchTerms))
for i := 0; i < len(searchTerms); i += batchSize {
end := i + batchSize
if end > len(searchTerms) {
end = len(searchTerms)
}
batch := searchTerms[i:end]
for j, term := range batch {
wg.Add(1)
go func(term string, index int) {
defer wg.Done()
if index > 0 {
time.Sleep(delay)
}
result, err := c.RegularSearch(ctx, SearchOptions{
Field: field,
Value: term,
RowLimit: 100,
})
resultData := map[string]interface{}{
"term": term,
"success": err == nil,
}
if err != nil {
resultData["error"] = err.Error()
c.Logger.Printf("Failed to search for %s: %v", term, err)
} else {
resultData["result"] = result
}
mu.Lock()
results = append(results, resultData)
mu.Unlock()
}(term, j)
}
wg.Wait()
processed := min(i+batchSize, len(searchTerms))
c.Logger.Printf("Processed %d/%d terms", processed, len(searchTerms))
if i+batchSize < len(searchTerms) {
time.Sleep(delay * 2)
}
}
successful := 0
for _, result := range results {
if result["success"].(bool) {
successful++
}
}
c.Logger.Printf("Bulk search completed: %d successful, %d failed", successful, len(results)-successful)
return results, nil
}
func min(a, b int) int {
if a < b {
return a
}
return b
}
func demonstrateBasicUsage() {
apiKey := os.Getenv("LEAKZERO_API_KEY")
if apiKey == "" {
log.Fatal("LEAKZERO_API_KEY environment variable is required")
}
client := NewLeakZeroClient(apiKey, WithMaxRetries(5))
ctx := context.Background()
fmt.Println("Checking API balance...")
balance, err := client.GetBalance(ctx)
if err != nil {
log.Printf("Error getting balance: %v", err)
return
}
fmt.Printf("Current balance: $%.6f
", balance.Balance)
if balance.Balance < 0.001 {
log.Fatal("Insufficient balance. Please top up your account.")
}
fmt.Println("
Performing regular search...")
result, err := client.RegularSearch(ctx, SearchOptions{
Field: "email",
Value: "[email protected]",
Option: "exact",
RowLimit: 100,
})
if err != nil {
log.Printf("Search error: %v", err)
return
}
fmt.Printf("Found %d records
", result.TotalResults)
fmt.Printf("Cost: $%.6f
", result.Cost)
for i, record := range result.Data {
if i >= 5 { // Show first 5
break
}
breachName := "Unknown"
breachDate := "Unknown"
if val, ok := record["breach_name"].(string); ok {
breachName = val
}
if val, ok := record["breach_date"].(string); ok {
breachDate = val
}
fmt.Printf("Breach: %s, Date: %s
", breachName, breachDate)
}
fmt.Println("
Getting usage statistics...")
stats, err := client.GetUsageStats(ctx)
if err != nil {
log.Printf("Stats error: %v", err)
return
}
fmt.Printf("Total requests: %d
", stats.TotalRequests)
fmt.Printf("Total cost: $%.6f
", stats.TotalCost)
}
func demonstrateConcurrentUsage() {
apiKey := os.Getenv("LEAKZERO_API_KEY")
if apiKey == "" {
log.Fatal("LEAKZERO_API_KEY environment variable is required")
}
client := NewLeakZeroClient(apiKey)
ctx := context.Background()
searchTerms := []string{"[email protected]", "[email protected]", "[email protected]"}
var wg sync.WaitGroup
results := make([]*SearchResult, len(searchTerms))
for i, term := range searchTerms {
wg.Add(1)
go func(index int, searchTerm string) {
defer wg.Done()
result, err := client.RegularSearch(ctx, SearchOptions{
Field: "email",
Value: searchTerm,
RowLimit: 50,
})
if err != nil {
log.Printf("Search failed for %s: %v", searchTerm, err)
return
}
results[index] = result
}(i, term)
}
wg.Wait()
successful := 0
for _, result := range results {
if result != nil {
successful++
}
}
fmt.Printf("Completed %d concurrent searches
", successful)
}
func main() {
demonstrateBasicUsage()
fmt.Println("
" + strings.Repeat("=", 50))
demonstrateConcurrentUsage()
}Type Safety: Strong typing with structs and interfaces for better compile-time checks
Concurrency: Goroutines and channels for efficient parallel processing
Context Support: Proper context handling for timeouts and cancellation
Key Features
Core Features
- • Strong type safety with structs and interfaces
- • Context-aware requests with timeout support
- • Automatic retry with exponential backoff
- • Rate limit handling with intelligent delays
- • Comprehensive error handling and logging
Performance Features
- • Goroutine-based concurrent operations
- • Connection pooling and HTTP keep-alive
- • Bulk search operations with batching
- • Memory-efficient JSON streaming
- • Configurable timeout and retry settings
Best Practices:
- • Always use environment variables for API keys
- • Implement proper context handling for production use
- • Use goroutines for concurrent searches to improve performance
- • Monitor your API usage and balance regularly
- • Use structured logging for better debugging