Swift email verification

Verify emails in Swift with Verifly

Add a live email verification step to any Swift service with URLSession async/await and Codable. Verifly is an API-first, pay-as-you-go service — await a typed verdict, reject disposable or dead mailboxes, and keep the check server-side so your key never ships in an app.

Verify one email with URLSessionAPI
import Foundation

struct Verdict: Codable {
    let result: String
    let is_valid: Bool
}

func verify(_ email: String) async throws -> Verdict {
    var components = URLComponents(string: "https://verifly.email/api/v1/verify")!
    components.queryItems = [URLQueryItem(name: "email", value: email)]

    var request = URLRequest(url: components.url!)
    request.setValue("Bearer vf_YOUR_KEY", forHTTPHeaderField: "Authorization")

    let (data, _) = try await URLSession.shared.data(for: request)
    return try JSONDecoder().decode(Verdict.self, from: data)
}

Real-time SMTP mailbox checks

Single, batch, and async bulk verification

Disposable, role account, and catch-all detection

Pay-as-you-go credits with no subscription lock-in

Search fit

Built for Swift email verification

Use Verifly when you need a simple API, predictable pricing, and clean JSON results before emails hit your product, CRM, or campaign tool.

Verify signups in a Vapor route before saving a user
Screen addresses from an iOS app via your own backend
Decode a typed verdict with Codable and async/await
Clean an imported list in a server-side Swift task

Email verification in Swift

Swift's modern concurrency makes an email check a one-liner to await, but the language cannot tell you whether a mailbox is real. A regex or NSDataDetector only confirms that user@dead-domain.com is shaped like an address; whether it accepts mail is a runtime fact behind an SMTP handshake. Verifly performs that handshake for you over one HTTPS request, and URLSession's async/await API turns the round-trip into a clean try await.

The idiomatic approach is URLSession.shared.data(for:) decoding into a Codable struct with JSONDecoder. Build the URL with URLComponents so the email is properly percent-encoded, set the Bearer header on a URLRequest, and await the response. The JSON carries a result field of deliverable, undeliverable, or risky plus boolean flags for disposable, role, catch_all, and smtp — all of which map onto Codable properties.

One important caution: keep the Bearer key server-side. If you call Verifly directly from an iOS or macOS app, that key can be extracted from the binary. Instead run verification in a server-side Swift context — a Vapor backend the app talks to — so the secret stays on your server. Pricing is pay-as-you-go from $2 per 1,000 checks down to $0.60 per 1,000 at volume, and you can self-register for 100 free credits with no card at the autonomous register endpoint.

Screen a signup in a Vapor route

The right home for the key is a server-side Swift app. In Vapor, your iOS client posts the address to your own route, and the route calls Verifly with the secret. Await the verdict, reject disposable and undeliverable addresses, and create the user only when the address passes. Wrap the call so a network error fails open — abort the signup only when Verifly positively reports a bad mailbox, not when it is briefly unreachable.

A Vapor signup route (key stays server-side)
import Vapor

struct SignupInput: Content { let email: String }
struct Verdict: Content { let result: String; let disposable: Bool }

func routes(_ app: Application) throws {
    app.post("signup") { req async throws -> HTTPStatus in
        let input = try req.content.decode(SignupInput.self)
        var headers = HTTPHeaders()
        headers.bearerAuthorization = .init(token: Environment.get("VERIFLY_KEY")!)

        let uri = URI(string: "https://verifly.email/api/v1/verify?email=\(input.email)")
        let res = try await req.client.get(uri, headers: headers)
        let v = try res.content.decode(Verdict.self)

        guard v.result != "undeliverable", !v.disposable else {
            throw Abort(.badRequest, reason: "Please use a valid, permanent email")
        }
        // try await User(email: input.email).save(on: req.db)
        return .created
    }
}

Batch-verify a list in Swift

For cleaning many addresses, do not await the single endpoint in a loop — POST them together to /api/v1/verify/batch. It accepts a JSON body with an emails array and returns a results array in the same order, which decodes into an array of Codable items. Encode the request body with JSONEncoder, await one call, and filter the results down to deliverable addresses.

Batch verification with URLSession
import Foundation

struct Item: Codable { let email: String; let result: String; let disposable: Bool? }
struct Batch: Codable { let results: [Item] }

func cleanList(_ emails: [String]) async throws -> [String] {
    var request = URLRequest(url: URL(string: "https://verifly.email/api/v1/verify/batch")!)
    request.httpMethod = "POST"
    request.setValue("Bearer vf_YOUR_KEY", forHTTPHeaderField: "Authorization")
    request.setValue("application/json", forHTTPHeaderField: "Content-Type")
    request.httpBody = try JSONEncoder().encode(["emails": emails])

    let (data, _) = try await URLSession.shared.data(for: request)
    let batch = try JSONDecoder().decode(Batch.self, from: data)

    return batch.results
        .filter { $0.result == "deliverable" && !($0.disposable ?? false) }
        .map { $0.email }
}

FAQ

Frequently asked questions

Can I call Verifly directly from an iOS app?

You should not. The Bearer key can be extracted from a shipped binary. Keep verification server-side — run it in a Vapor backend that your app talks to — so the key stays a secret on your server rather than in the App Store bundle.

How do I decode the response in Swift?

Define a Codable struct with the fields you need — result: String, is_valid: Bool, and optional disposable/role/catch_all/smtp — and decode with JSONDecoder. URLSession's data(for:) async method gives you the Data to decode in one try await.

How do I percent-encode the email in the query?

Build the URL with URLComponents and set queryItems to [URLQueryItem(name: "email", value: email)]. URLComponents encodes the value for you, so a plus sign or unusual character in the address does not break the request.

How do I batch-verify a list of addresses?

POST them to /api/v1/verify/batch with a JSON emails array — encode ["emails": emails] with JSONEncoder — and decode the results array into an array of Codable items, then filter on result. Batching beats awaiting the single endpoint per address.

How do I stop a network error from blocking signup?

Fail open: only reject when Verifly positively returns undeliverable or disposable. Wrap the call so a thrown URLError or timeout lets the registration proceed rather than turning a real user away on a transient blip.

What does Swift email verification cost?

Pricing is pay-as-you-go from $2 per 1,000 checks down to $0.60 per 1,000 at volume, with 100 free credits when you self-register. There is no subscription. For AI agents, Verifly also exposes an MCP server at verifly.email/mcp.