Email Verification API - Go (Golang) 2026 | BounceZero
Blog›Developer Guides

Email Verification API
Go (Golang) 2026

Production-ready Go integration examples for the BounceZero email verification API: single-address lookup with net/http, concurrent worker pool for bulk processing, Gin middleware for signup form validation, and CLI-style CSV batch verification.

By BounceZero Team |July 2026 |7 min read

API quick reference

Field Type Description
classification string "valid" | "invalid" (binary verdict)
score int 0-100 confidence score
is_deliverable bool true when the address is safe to send to
risk_level string "low" | "medium" | "high" (catch-all valids are medium)
checks.not_disposable bool false if the domain is a known disposable provider
metadata.internal_classification string detail behind the verdict: verified, likely_valid, catch_all, invalid, risky, unknown, disposable, spamtrap, error
metadata.refunded bool true when the address could not be verified; the credit is auto-refunded

1. Single address - net/http

No external dependencies. Uses Go’s standard library.

package main

import (
	"bytes"
	"encoding/json"
	"fmt"
	"net/http"
	"os"
	"time"
)

type VerifyResult struct {
	Email          string `json:"email"`
	Score          int    `json:"score"`
	Classification string `json:"classification"` // "valid" | "invalid"
	IsDeliverable  bool   `json:"is_deliverable"`
	RiskLevel      string `json:"risk_level"`
	Checks         struct {
		NotDisposable *bool `json:"not_disposable"`
	} `json:"checks"`
	Metadata struct {
		InternalClassification string `json:"internal_classification"`
		Refunded               bool   `json:"refunded"`
		CatchAllUnresolved     bool   `json:"catch_all_unresolved"`
	} `json:"metadata"`
}

var httpClient = &http.Client{Timeout: 30 * time.Second}

func VerifyEmail(email, apiKey string) (*VerifyResult, error) {
	endpoint := "https://api.bouncezero.io/api/v1/verify"
	payload, _ := json.Marshal(map[string]string{"email": email})

	req, err := http.NewRequest("POST", endpoint, bytes.NewReader(payload))
	if err != nil {
		return nil, err
	}
	req.Header.Set("X-API-Key", apiKey)
	req.Header.Set("Content-Type", "application/json")

	resp, err := httpClient.Do(req)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()
	if resp.StatusCode != http.StatusOK {
		return nil, fmt.Errorf("bouncezero: HTTP %d", resp.StatusCode)
	}

	var result VerifyResult
	if err := json.NewDecoder(resp.Body).Decode(&result); err != nil {
		return nil, err
	}
	return &result, nil
}

func main() {
	apiKey := os.Getenv("BOUNCEZERO_API_KEY")
	result, err := VerifyEmail("[email protected]", apiKey)
	if err != nil {
		fmt.Fprintf(os.Stderr, "error: %v\n", err)
		os.Exit(1)
	}
	fmt.Printf("classification: %s | score: %d | detail: %s | refunded: %v\n",
		result.Classification, result.Score,
		result.Metadata.InternalClassification, result.Metadata.Refunded)
}

2. Concurrent worker pool

Verify thousands of addresses in parallel with bounded concurrency - no unbounded goroutine spawning.

package main

import (
	"fmt"
	"sync"
)

const numWorkers = 20

type Job struct {
	Email string
}

type Result struct {
	Email  string
	Result string
	Error  error
}

func worker(apiKey string, jobs <-chan Job, results chan<- Result, wg *sync.WaitGroup) {
	defer wg.Done()
	for job := range jobs {
		v, err := VerifyEmail(job.Email, apiKey)
		if err != nil {
			results <- Result{Email: job.Email, Error: err}
			continue
		}
		results <- Result{Email: job.Email, Result: v.Classification}
	}
}

func VerifyBatch(emails []string, apiKey string) []Result {
	jobs    := make(chan Job, len(emails))
	results := make(chan Result, len(emails))

	var wg sync.WaitGroup
	for i := 0; i < numWorkers; i++ {
		wg.Add(1)
		go worker(apiKey, jobs, results, &wg)
	}

	for _, email := range emails {
		jobs <- Job{Email: email}
	}
	close(jobs)

	go func() {
		wg.Wait()
		close(results)
	}()

	var out []Result
	for r := range results {
		out = append(out, r)
	}
	return out
}

func main() {
	emails := []string{"[email protected]", "[email protected]", "invalid@"}
	apiKey := "YOUR_API_KEY"
	results := VerifyBatch(emails, apiKey)
	for _, r := range results {
		if r.Error != nil {
			fmt.Printf("ERROR %s: %v\n", r.Email, r.Error)
		} else {
			fmt.Printf("%-35s %s\n", r.Email, r.Result)
		}
	}
}

3. Gin middleware - block invalid at signup

Fail-open pattern: if the API is unavailable, the request proceeds rather than blocking legitimate users.

package main

import (
	"net/http"
	"os"

	"github.com/gin-gonic/gin"
)

func EmailVerificationMiddleware() gin.HandlerFunc {
	apiKey := os.Getenv("BOUNCEZERO_API_KEY")
	return func(c *gin.Context) {
		var body struct {
			Email string `json:"email" binding:"required,email"`
		}
		if err := c.ShouldBindJSON(&body); err != nil {
			c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"error": "invalid request"})
			return
		}

		result, err := VerifyEmail(body.Email, apiKey)
		if err != nil {
			// fail-open: API unreachable > allow the request
			c.Set("email_verified", false)
			c.Next()
			return
		}

		// refunded = could not be verified; fail open on those instead of blocking
		if result.Classification == "invalid" && !result.Metadata.Refunded {
			c.AbortWithStatusJSON(http.StatusUnprocessableEntity, gin.H{
				"error":   "invalid_email",
				"message": "Please use a valid work email address.",
			})
			return
		}

		c.Set("email", body.Email)
		c.Set("email_verified", true)
		c.Next()
	}
}

func main() {
	r := gin.Default()
	r.POST("/signup", EmailVerificationMiddleware(), func(c *gin.Context) {
		email, _ := c.Get("email")
		c.JSON(http.StatusOK, gin.H{"status": "ok", "email": email})
	})
	r.Run(":8080")
}

New to verification? Start with the complete email verification guide.

Frequently Asked Questions

How do I verify an email address in Go?

Use the standard net/http package to POST {"email": "..."} to https://api.bouncezero.io/api/v1/verify with your API key in the X-API-Key header. Unmarshal the JSON response into a struct and check the classification field (valid or invalid). No external libraries required.

How do I verify emails concurrently in Go?

Use a worker pool with goroutines and buffered channels. Create a jobs channel, launch N worker goroutines (10-20 is typical for I/O-bound API calls), and fan out addresses into the jobs channel. Each worker calls the API and sends results back. Use a WaitGroup to synchronise completion.

Get your Go integration running in minutes.

Sign up for a BounceZero account, grab your API key, and paste the code above. 100 free credits included - no credit card required.

Build with the BounceZero API

Endpoints, code samples, and language guides