Real-time email verification using only the Go standard library —
net/http and encoding/json.
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.
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.")
}
}
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.
RetryAfterSeconds seconds, then repeat the same request to get the final verdict.Unknown as "not yet decided" — never as a delivery failure.The balance endpoint is free to call and never consumes a credit — perfect for a pre-flight check before verifying a large list.
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 | 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. |
Explore every response field and status code in the full API reference.