Email verification API in C# and .NET
To verify an email in C#, register a typed HttpClient with the SimpleVerifier base address, a Bearer token and a 30-second timeout, then call GET verify?email= and read status from the JSON: valid, risky or invalid. Reject only invalid, and return null on HttpRequestException or a timeout so the address goes through.
By SimpleVerifier Team
Before you start
- Create a key on the API keys page. Keys start with
sv_live_and are shown once. - Set
SIMPLEVERIFIER_KEYas an environment variable or withdotnet user-secretsin development. ASP.NET Core reads both throughbuilder.Configuration. - API access is included in the Pro plan ($29.99/month) and the Email + Phone plan ($49.99/month). See pricing.
1. Add the typed client
Uses a primary constructor and [GeneratedRegex], so it needs .NET 8 or later. The JsonPropertyName attributes map the snake_case fields.
// SimpleVerifierClient.cs — .NET 8+
using System.Net.Http.Json;
using System.Text.Json.Serialization;
using System.Text.RegularExpressions;
using Microsoft.Extensions.Logging;
public sealed record VerifyResult(
[property: JsonPropertyName("email")] string Email,
[property: JsonPropertyName("status")] string Status,
[property: JsonPropertyName("reason")] string Reason,
[property: JsonPropertyName("mailbox_checked")] bool MailboxChecked);
public sealed partial class SimpleVerifierClient(HttpClient http, ILogger<SimpleVerifierClient> logger)
{
[GeneratedRegex(@"^[^@\s]+@[^@\s]+\.[^@\s]+$")]
private static partial Regex EmailShape();
/// <summary>"valid", "risky" or "invalid"; null when the API could not answer.</summary>
public async Task<string?> EmailStatusAsync(string email, CancellationToken ct = default)
{
var address = email?.Trim() ?? "";
if (!EmailShape().IsMatch(address)) return "invalid"; // no API call made
try
{
using var response = await http.GetAsync($"verify?email={Uri.EscapeDataString(address)}", ct);
if (!response.IsSuccessStatusCode)
{
logger.LogWarning("SimpleVerifier returned HTTP {Status}", (int)response.StatusCode);
return null; // 401, 402, 429, 503: fail open
}
var result = await response.Content.ReadFromJsonAsync<VerifyResult>(ct);
return result?.Status;
}
catch (Exception ex) when (ex is HttpRequestException or TaskCanceledException)
{
logger.LogWarning(ex, "SimpleVerifier unavailable");
return null; // timeout or network error: fail open
}
}
}2. Register it and use it in an endpoint
AddHttpClient manages the underlying connections for you. An HttpClient timeout surfaces as TaskCanceledException, which the client catches.
// Program.cs — ASP.NET Core minimal API
using System.Net.Http.Headers;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddHttpClient<SimpleVerifierClient>(client =>
{
client.BaseAddress = new Uri("https://simpleverifier.com/api/v1/");
client.Timeout = TimeSpan.FromSeconds(30);
client.DefaultRequestHeaders.Authorization =
new AuthenticationHeaderValue("Bearer", builder.Configuration["SIMPLEVERIFIER_KEY"]);
});
var app = builder.Build();
app.MapPost("/signup", async (SignupRequest body, SimpleVerifierClient verifier, CancellationToken ct) =>
{
if (await verifier.EmailStatusAsync(body.Email, ct) == "invalid")
{
return Results.ValidationProblem(new Dictionary<string, string[]>
{
["email"] = ["This email address cannot receive mail. Please check it."],
});
}
// Save the signup here. The status was "valid", "risky" or null (API unavailable).
return Results.Ok();
});
app.Run();
public record SignupRequest(string Email);What the API returns
GET or POST https://simpleverifier.com/api/v1/verify with email runs the syntax, domain, disposable-domain and live mailbox checks in one call and returns:
{
"email": "jane@company.com",
"status": "valid",
"reason": "Mailbox exists",
"mailbox_checked": true
}Every snippet above follows the same rule: only a successful response with status set to invalid blocks a person.
| Result | What it means | What to do |
|---|---|---|
| valid | The mail server confirmed the mailbox exists. | Accept. |
| risky | The domain is real but the mailbox couldn't be confirmed: catch-all domains, role addresses like info@, providers that hide mailbox status. | Accept on signup forms. Store the status and keep these out of cold campaigns. |
| invalid | Bad format, no mail server, a disposable domain, or the mailbox doesn't exist. | Reject and ask the person to check the address. |
| timeout, 429 or 503 | The check didn't finish: a slow mail server, too many calls this minute, or a brief outage. | Accept (fail open) and log it. Re-check later if it matters. |
| 401 or 402 | The key is wrong or revoked, or the subscription isn't active. | Accept, and alert yourself: every check is being skipped until it's fixed. |
Errors return JSON with an error message. Each key can make 120 calls a minute; for thousands of addresses, upload a CSV in the dashboard instead.
Phone numbers and spreadsheets
The same key works with /api/v1/phone?phone=…&country=… to validate a phone number and normalise it to E.164, with live carrier lookups on the Email + Phone plan. The request and response are in the API reference. To check a column of addresses without code, use the Google Sheets add-on with the same key.
Common questions
- Can I use this as a validation attribute?
- A ValidationAttribute is synchronous and can't take constructor dependencies, so calling an HTTP API from one means blocking a thread. Check the address in the endpoint or controller action as shown, or with an async validator such as FluentValidation's MustAsync.
- Does EmailAddressAttribute check that the mailbox exists?
- No. [EmailAddress] only looks at the format. The API checks the domain, disposable domains and the mailbox.
- How much does the SimpleVerifier API cost?
- API access is included in the paid plans: Pro at $29.99/month and Email + Phone at $49.99/month. There are no per-call credits. Each key can make 120 calls a minute.
- Should I reject risky addresses?
- Not on a signup or contact form. Risky means the mailbox couldn't be confirmed, which includes every address at a catch-all company domain. Accept it, store the status, and leave risky addresses out of cold outreach.
- What if the API is slow or down?
- Let the address through. A mailbox check can take several seconds when a mail server is slow, so cap the wait at about 30 seconds (less on a busy form) and treat a timeout, a 429 or a 503 as unknown rather than invalid.