Email Verification API - Go (Golang) 2026 | BounceZero
BlogDeveloper 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
result string "valid" | "invalid" | "unknown"
is_disposable bool true if the domain is a known disposable provider
is_role_address bool true for info@, admin@, support@, etc.
catch_all_score float64 0.0-1.0 - confidence the specific mailbox exists on catch-all domains
mx_found bool false means the domain has no mail server
free_provider bool true for gmail.com, outlook.com, yahoo.com, etc.
smtp_code int SMTP response code from live probe (250, 550, 421...)

1. Single address - net/http

No external dependencies. Uses Go’s standard library.

package main

import (
	"encoding/json"
	"fmt"
	"net/http"
	"net/url"
	"os"
)

type VerifyResult struct {
	Email          string  `json:"email"`
	Result         string  `json:"result"`
	IsDisposable   bool    `json:"is_disposable"`
	IsRoleAddress  bool    `json:"is_role_address"`
	CatchAllScore  float64 `json:"catch_all_score"`
	MxFound        bool    `json:"mx_found"`
	FreeProvider   bool    `json:"free_provider"`
}

func VerifyEmail(email, apiKey string) (*VerifyResult, error) {
	endpoint := "https://api.bouncezero.io/v1/verify"
	params := url.Values{}
	params.Set("email", email)

	req, err := http.NewRequest("GET", endpoint+"?"+params.Encode(), nil)
	if err != nil {
		return nil, err
	}
	req.Header.Set("X-API-Key", apiKey)

	client := &http.Client{}
	resp, err := client.Do(req)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()

	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("result: %s | disposable: %v | catch_all_score: %.2f\n",
		result.Result, result.IsDisposable, result.CatchAllScore)
}

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.Result}
	}
}

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
		}

		if result.Result == "invalid" || result.IsDisposable {
			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 call the BounceZero API with the email as a query parameter and your API key in the X-API-Key header. Unmarshal the JSON response into a struct and check the result field. 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