Go · Email breach checks
Check email breaches in Go with net/http
Go decodes the email breach response into structs with very little ceremony. The breach records use PascalCase keys, so fields named Title, BreachDate, DataClasses and IsVerified line up with the JSON without any struct tags. The envelope fields breached and breaches also match exported Go fields because encoding/json compares names case-insensitively when no tag is present.
The program below builds one http.Client with a 10 second timeout and reuses it across attempts, which is the idiomatic way to keep connections pooled. A switch on the status code separates validation errors, authentication problems and rate limiting. On a rate_limited 429 it sleeps for the Retry-After seconds and tries again, while quota_exceeded falls through to an error so a worker can stop cleanly.
Steps
- 1
Use Go 1.21 or newer, since the example relies on the built-in max function.
- 2
Export FINOWL_API_KEY and save the program as main.go in a new module.
- 3
Run go run main.go to print each breach's title, date and exposed data classes.
- 4
Replace the hard-coded address with input from your handler or job queue.
package main
import (
"bytes"
"encoding/json"
"fmt"
"log"
"net/http"
"os"
"strconv"
"strings"
"time"
)
type Breach struct {
Title string
BreachDate string
DataClasses []string
IsVerified bool
}
type EmailResult struct {
Email string
Breached bool
Breaches []Breach
}
func checkEmail(client *http.Client, email string) (*EmailResult, error) {
body, _ := json.Marshal(map[string]string{"email": email})
for attempt := 0; attempt < 3; attempt++ {
req, _ := http.NewRequest(http.MethodPost, "https://api.finowl.co/v1/breaches/email", bytes.NewReader(body))
req.Header.Set("Authorization", "Bearer "+os.Getenv("FINOWL_API_KEY"))
req.Header.Set("Content-Type", "application/json")
resp, err := client.Do(req)
if err != nil {
return nil, err
}
var out struct {
EmailResult
Error struct{ Code string }
}
err = json.NewDecoder(resp.Body).Decode(&out)
resp.Body.Close()
switch code := out.Error.Code; {
case resp.StatusCode == http.StatusOK:
return &out.EmailResult, err
case resp.StatusCode == http.StatusBadRequest, resp.StatusCode == http.StatusUnauthorized:
return nil, fmt.Errorf("request rejected (%d): %s", resp.StatusCode, code)
case resp.StatusCode == http.StatusTooManyRequests && code == "rate_limited":
secs, _ := strconv.Atoi(resp.Header.Get("Retry-After"))
time.Sleep(time.Duration(max(secs, 1)) * time.Second)
default:
return nil, fmt.Errorf("finowl error %d: %s", resp.StatusCode, code)
}
}
return nil, fmt.Errorf("still rate limited after 3 attempts")
}
func main() {
result, err := checkEmail(&http.Client{Timeout: 10 * time.Second}, "alex@example.com")
if err != nil {
log.Fatal(err)
}
fmt.Printf("%s breached=%t\n", result.Email, result.Breached)
for _, b := range result.Breaches {
fmt.Printf("%s (%s) verified=%t\n", b.Title, b.BreachDate, b.IsVerified)
fmt.Println(" exposed:", strings.Join(b.DataClasses, ", "))
}
}IN PRODUCTION
Production tips
Share one http.Client across goroutines rather than creating one per request. It is safe for concurrent use and reuses TCP connections to api.finowl.co.
Use a buffered channel or golang.org/x/time/rate limiter in front of checkEmail when fanning out over many users, so all goroutines together respect 60 requests per minute.
Pass a context from your HTTP handler with http.NewRequestWithContext in production code, so a cancelled client request also cancels the outbound breach lookup.
Each lookup costs 1 credit, with 100 free credits every month. See pricing and error codes.
A FEW GOOD QUESTIONS
Common questions
Related guides
- Check breached passwords in Go before storing them
- Predict gender from names in Go with net/http
- Check email breaches in Python with requests
- Check email breaches in Node.js with fetch
- Check email breaches in TypeScript with typed results
- Check email breaches in Java with HttpClient
- Email breach checks for fintech signups and logins
- Email breach checks for online stores and shopper logins
- Email breach checks for SaaS logins and admins
Check email breaches in Go with net/http
Grab an API key, paste the example and run it. 100 free credits every month.