Find and verify professional emails and find phone numbers with the Enrow API.
There is no tagged release yet: depend on the main branch. In your Package.swift:
dependencies: [
.package(url: "https://github.com/EnrowAPI/enrow-swift.git", branch: "main"),
],
targets: [
.target(name: "YourApp", dependencies: [
.product(name: "Enrow", package: "enrow-swift"),
]),
]Or in Xcode: File → Add Package Dependencies, enter the repository URL and pick the main branch.
Requires Swift 5.9+, macOS 12+ or iOS 15+.
Every search and verification is asynchronous. The launch call returns an id, and the result comes from the matching get call: poll it while qualification is ongoing (general.status for a bulk), or pass a webhook in the settings to be notified when it's done. See How webhooks work.
import Enrow
let enrow = Enrow(apiKey: "your_api_key")
let search = try await enrow.email.find(EmailFindParams(
companyDomain: "dundermifflin.com",
fullname: "Dwight Schrute"
))
var result = try await enrow.email.get(search.id)
while result.qualification == "ongoing" {
try await Task.sleep(nanoseconds: 2_000_000_000)
result = try await enrow.email.get(search.id)
}
print(result.email ?? "not found") // dwight.schrute@dundermifflin.comSearch from a full name and a company (companyDomain or companyName), or from a LinkedIn profile URL:
let search = try await enrow.email.find(EmailFindParams(
companyName: "Dunder Mifflin",
fullname: "Dwight Schrute",
custom: ["crm_id": "lead_001"],
settings: SearchSettings(
countryCode: "US",
retrieveGender: true,
webhook: "https://your-app.com/webhooks/enrow"
)
))
let fromProfile = try await enrow.email.find(EmailFindParams(
linkedinUrl: "https://www.linkedin.com/in/dwight-schrute"
))
let result = try await enrow.email.get(search.id)
print(result.qualification) // "valid", "invalid" or "ongoing"
print(result.email ?? "-") // dwight.schrute@dundermifflin.com
print(result.info?.firstname ?? "-") // Dwight
print(result.custom?["crm_id"]?.stringValue ?? "-") // lead_001
let profileResult = try await enrow.email.get(fromProfile.id)
print(profileResult.linkedinStatus ?? "-") // "found", "no_current_company"…custom takes any JSON value (usually an object, built from a dictionary literal) and comes back as-is in the result and the webhook. retrieveCompanyInfo: true adds company data to info.companyInfo when countryCode is FR.
Up to 5,000 searches per batch (name and company only):
let batch = try await enrow.email.findBulk(EmailFindBulkParams(
searches: [
EmailFindParams(companyDomain: "dundermifflin.com", fullname: "Dwight Schrute", custom: "lead_001"),
EmailFindParams(companyDomain: "dundermifflin.com", fullname: "Jim Halpert", custom: "lead_002"),
],
settings: SearchSettings(webhook: "https://your-app.com/webhooks/enrow")
))
let bulk = try await enrow.email.getBulk(batch.id)
if bulk.general.status == "completed" {
for item in bulk.results ?? [] {
print(item.index, item.qualification, item.email ?? "-")
}
}let verification = try await enrow.verify.single(VerifySingleParams(email: "pam.beesly@dundermifflin.com"))
let result = try await enrow.verify.get(verification.id)
print(result.qualification) // "valid", "invalid" or "ongoing"
// Bulk: up to 5,000 emails
let batch = try await enrow.verify.bulk(VerifyBulkParams(verifications: [
"pam.beesly@dundermifflin.com",
"angela.martin@dundermifflin.com",
]))
let bulk = try await enrow.verify.getBulk(batch.id)
for item in bulk.results ?? [] {
print(item.email, item.qualification)
}Search from a LinkedIn profile URL (recommended, it prevails), or from firstname + lastname + a company:
let search = try await enrow.phone.find(PhoneFindParams(
linkedinUrl: "https://www.linkedin.com/in/michael-scott/"
))
let byName = try await enrow.phone.find(PhoneFindParams(
firstname: "Michael",
lastname: "Scott",
companyDomain: "dundermifflin.com"
))
let result = try await enrow.phone.get(search.id)
print(result.qualification) // "found", "not_found" or "ongoing"
print(result.number ?? "-") // +15705551234
// Bulk: up to 3,000 searches
let batch = try await enrow.phone.findBulk(PhoneFindBulkParams(searches: [
PhoneFindParams(linkedinUrl: "https://www.linkedin.com/in/michael-scott"),
PhoneFindParams(linkedinUrl: "https://www.linkedin.com/in/dwight-schrute"),
]))
let bulk = try await enrow.phone.getBulk(batch.id)
for item in bulk.results ?? [] {
print(item.index, item.number ?? "not found")
}let info = try await enrow.account.info()
print(info.credits) // 36000do {
_ = try await enrow.email.find(EmailFindParams(companyDomain: "dundermifflin.com", fullname: "Dwight Schrute"))
} catch EnrowError.insufficientBalance(let message) { // 402
print("Not enough credits: \(message)")
} catch EnrowError.rateLimit(let message) { // 429
print("Rate limited: \(message)")
} catch EnrowError.authentication(let message) { // 401
print("Auth failed: \(message)")
} catch EnrowError.api(let status, let message) { // 400, 500…
print("API error \(status): \(message)")
}POST endpoints accept 10 requests per second per API key; GET endpoints are not rate limited. Retry rateLimit and 5xx errors with a backoff. See Error handling.
| Endpoint | Cost |
|---|---|
| Email Finder | 1 credit per email found |
| Email Verifier | 0.25 credit per verification |
| Phone Finder | 40 credits per phone found (50 on legacy plans) |
See Credits & billing.
MIT