Kotlin email verification
Verify emails in Kotlin with Verifly
Add a live email verification step to any Kotlin app with the Ktor client or an OkHttp coroutine. Verifly is an API-first, pay-as-you-go service — call it from a suspend function, deserialize a typed verdict with kotlinx.serialization, and drop bad mailboxes before they reach your mailer.
import io.ktor.client.*
import io.ktor.client.call.*
import io.ktor.client.plugins.contentnegotiation.*
import io.ktor.client.request.*
import io.ktor.serialization.kotlinx.json.*
import kotlinx.serialization.Serializable
@Serializable
data class Verdict(val result: String, val is_valid: Boolean)
suspend fun verify(email: String): Verdict {
val client = HttpClient { install(ContentNegotiation) { json() } }
return client.use {
it.get("https://verifly.email/api/v1/verify") {
header("Authorization", "Bearer vf_YOUR_KEY")
parameter("email", email)
}.body()
}
}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 Kotlin 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.
Coroutine-friendly email verification
Email verification is a network round-trip, which in Kotlin means it belongs inside a suspend function, off the main thread. A regex or a syntactic check only tells you user@dead-domain.com is well-formed; it cannot tell you the mailbox bounces. That answer lives behind an SMTP handshake, and Verifly performs it for you over one HTTPS request you can await from any coroutine.
Two idiomatic clients fit. Ktor's HttpClient is coroutine-native — a get call is already a suspend function, and with the ContentNegotiation plugin and kotlinx.serialization it deserializes the JSON straight into a @Serializable data class. Alternatively, OkHttp with its coroutine-friendly await extension works well, especially on Android where OkHttp is already on the classpath. Either way the response carries a result field of deliverable, undeliverable, or risky plus boolean flags for disposable, role, catch_all, and smtp.
Do not embed the Bearer key in an Android APK — treat it as a server-side secret and proxy verification through your own backend, which is where Ktor server routes shine. 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.
The same call with OkHttp coroutines
If you already depend on OkHttp, you do not need to add Ktor. OkHttp's Call exposes an await() suspend extension, so a verification becomes a clean suspend function on Dispatchers.IO. Parse the JSON with kotlinx.serialization and branch on the result field. Reuse a single OkHttpClient across your app — it is designed to be shared and pools connections.
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import kotlinx.coroutines.suspendCancellableCoroutine
import kotlinx.serialization.json.Json
import kotlinx.serialization.json.jsonPrimitive
import kotlinx.serialization.json.jsonObject
import okhttp3.*
import java.io.IOException
import kotlin.coroutines.resume
import kotlin.coroutines.resumeWithException
val http = OkHttpClient()
suspend fun Call.await(): Response = suspendCancellableCoroutine { cont ->
enqueue(object : Callback {
override fun onFailure(call: Call, e: IOException) = cont.resumeWithException(e)
override fun onResponse(call: Call, response: Response) = cont.resume(response)
})
cont.invokeOnCancellation { cancel() }
}
suspend fun isDeliverable(email: String): Boolean = withContext(Dispatchers.IO) {
val req = Request.Builder()
.url("https://verifly.email/api/v1/verify?email=$email")
.header("Authorization", "Bearer vf_YOUR_KEY")
.build()
http.newCall(req).await().use { resp ->
val obj = Json.parseToJsonElement(resp.body!!.string()).jsonObject
obj["result"]?.jsonPrimitive?.content == "deliverable"
}
}Batch-verify a list with Ktor
For cleaning many addresses, do not fire 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 deserializes into a list of @Serializable items. Filter the list down to deliverable, non-disposable addresses with a standard Kotlin filter/map.
import io.ktor.client.request.*
import io.ktor.http.*
import kotlinx.serialization.Serializable
@Serializable
data class Item(val email: String, val result: String, val disposable: Boolean = false)
@Serializable
data class Batch(val results: List<Item>)
@Serializable
data class BatchRequest(val emails: List<String>)
suspend fun cleanList(client: HttpClient, emails: List<String>): List<String> {
val batch: Batch = client.post("https://verifly.email/api/v1/verify/batch") {
header("Authorization", "Bearer vf_YOUR_KEY")
contentType(ContentType.Application.Json)
setBody(BatchRequest(emails))
}.body()
return batch.results
.filter { it.result == "deliverable" && !it.disposable }
.map { it.email }
}FAQ
Frequently asked questions
Ktor client or OkHttp for Verifly in Kotlin?
Both are idiomatic. Ktor's HttpClient is coroutine-native and pairs with kotlinx.serialization for typed deserialization out of the box. OkHttp is a great fit if it is already on your classpath — add a small await() suspend extension and it works cleanly from coroutines. Verifly is a plain REST API, so either suits.
Should I put my key in an Android app?
No. The Bearer key is a server-side secret. If an Android app needs verification, proxy it through your own backend — a Ktor server route, for example — so the key never ships inside the APK where it can be extracted.
How do I deserialize the response?
Declare a @Serializable data class with the fields you need — result: String, is_valid: Boolean, and optional disposable/role/catch_all/smtp with default false — and let kotlinx.serialization map the JSON. With Ktor's ContentNegotiation you just call .body<Verdict>().
How do I batch-verify a list of addresses?
POST them to /api/v1/verify/batch with a JSON emails array and deserialize the results array into a List of items, then filter on the result field. Batching is faster and cheaper than looping the single endpoint in a coroutine.
What fields come back in the JSON?
Each result has result (deliverable, undeliverable, or risky), is_valid, and boolean flags disposable, role, catch_all, and smtp. Give the flags a default of false in your data class so a missing field deserializes safely.
What does Kotlin 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.