Limited-time August offer: Save 80% on email verification credits. View pricing
Tutorial · Go

Verify Email Addresses with Go

Real-time email verification using only the Go standard library — net/http and encoding/json.

GET https://api.validemail.net/ 1 credit per verification Go 1.18+
1

Verify an email address

Send a GET request with the email and your API token. The helper below also handles interim Unknown results by waiting RetryAfterSeconds and retrying automatically.

Go
package main

import (
	"encoding/json"
	"fmt"
	"io"
	"net/http"
	"net/url"
	"time"
)

const (
	apiURL = "https://api.validemail.net/"
	apiKey = "YOUR_API_KEY" // find it in your ValidEmail dashboard
)

type AdditionalInfo struct {
	Key   string `json:"Key"`
	Value string `json:"Value"`
}

type VerificationResult struct {
	IsValid             bool             `json:"IsValid"`
	Score               int              `json:"Score"`
	Email               string           `json:"Email"`
	State               string           `json:"State"`
	Reason              string           `json:"Reason"`
	Domain              string           `json:"Domain"`
	Free                bool             `json:"Free"`
	Role                bool             `json:"Role"`
	Disposable          bool             `json:"Disposable"`
	AcceptAll           bool             `json:"AcceptAll"`
	Tag                 bool             `json:"Tag"`
	MXRecord            string           `json:"MXRecord"`
	RetryAfterSeconds   *int             `json:"RetryAfterSeconds"`
	EmailAdditionalInfo []AdditionalInfo `json:"EmailAdditionalInfo"`
}

var client = &http.Client{Timeout: 90 * time.Second}

func verifyEmail(email string, maxRetries int) (*VerificationResult, error) {
	var result VerificationResult

	for attempt := 0; attempt < maxRetries; attempt++ {
		query := url.Values{"email": {email}, "token": {apiKey}}
		resp, err := client.Get(apiURL + "?" + query.Encode())
		if err != nil {
			return nil, err
		}

		body, err := io.ReadAll(resp.Body)
		resp.Body.Close()
		if err != nil {
			return nil, err
		}

		if resp.StatusCode != http.StatusOK {
			return nil, fmt.Errorf("request failed with HTTP %d: %s", resp.StatusCode, body)
		}

		if err := json.Unmarshal(body, &result); err != nil {
			return nil, err
		}

		// "Unknown" means the verification is still in progress (e.g. greylisting).
		if result.State == "Unknown" && result.RetryAfterSeconds != nil {
			time.Sleep(time.Duration(*result.RetryAfterSeconds) * time.Second)
			continue
		}

		return &result, nil
	}

	return &result, nil // still pending after maxRetries — treat as risky
}

func main() {
	result, err := verifyEmail("someone@example.com", 3)
	if err != nil {
		panic(err)
	}

	fmt.Println("Valid: ", result.IsValid)
	fmt.Println("Score: ", result.Score)
	fmt.Println("State: ", result.State)
	fmt.Println("Reason:", result.Reason)

	if result.IsValid && result.Score >= 80 {
		fmt.Println("Safe to send.")
	}
}
Handling interim results (State = "Unknown")

Some mail servers greylist first-time senders or respond slowly. Instead of failing, the API returns an interim 200 OK result with State = "Unknown", a Reason of PENDING or GREYLISTED, and a RetryAfterSeconds hint while the verification finishes in the background.

  • Wait RetryAfterSeconds seconds, then repeat the same request to get the final verdict.
  • Definitive results are cached for about 10 minutes, so the retry is answered instantly once the verification completes.
  • Treat Unknown as "not yet decided" — never as a delivery failure.
2

Check your credits balance

The balance endpoint is free to call and never consumes a credit — perfect for a pre-flight check before verifying a large list.

Go
resp, err := client.Get("https://api.validemail.net/balance?token=" + apiKey)
if err != nil {
	panic(err)
}
defer resp.Body.Close()

var balance struct {
	Balance int `json:"balance"`
}
if err := json.NewDecoder(resp.Body).Decode(&balance); err != nil {
	panic(err)
}

fmt.Println("Credits remaining:", balance.Balance)

HTTP status codes

HTTP Status Meaning What to do
200 OK The request was processed. Check State — a result with State = "Unknown" and a RetryAfterSeconds value is an interim answer, not a final verdict. Use the result. If State is Unknown, retry the same request after RetryAfterSeconds.
400 Bad Request Missing email/token parameter, no credits remaining, or a transient processing failure. Check the response text. If you are out of credits, top up your balance before retrying.
401 Unauthorized The API key is invalid or unknown. Verify the token value against the API key shown in your dashboard.
403 Forbidden The account attached to this API key is inactive. Contact info@validemail.net to reactivate the account.
429 Too Many Requests You exceeded your per-second request limit. The response body includes the configured limit. Slow down and retry with backoff, or contact us to raise your rate limit.
Next steps

Explore every response field and status code in the full API reference.