1. Home
  2. API docs
  3. C# / .NET

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_KEY as an environment variable or with dotnet user-secrets in development. ASP.NET Core reads both through builder.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
// 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
// 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.

ResultWhat it meansWhat to do
validThe mail server confirmed the mailbox exists.Accept.
riskyThe 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.
invalidBad 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 503The 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 402The 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.

Ready to clean your email and phone lists?

Start verifying emails and phone numbers today and boost your deliverability.

Get Started

Cancel anytime • No setup required