Ruby SDK

The official Ruby SDK returns immutable result objects, raises typed errors and retries the answers that are never charged. It needs Ruby 3.2 or later and has no runtime dependencies: only the standard library (net/http, json, openssl).

Not published yet. The gem email-validation-sdk is not on RubyGems yet, so the install step below does not work today. The SDK is complete and tested; it will be published once the registry account and the licence are in place. Until then, use the code samples, which call the API directly.

Install

gem install email-validation-sdk

or in your Gemfile:

gem "email-validation-sdk"

The gem is required as email_validation.

Validate an address

require "email_validation"

client = EmailValidation::Client.new(api_key: ENV.fetch("EV_API_KEY"))

result = client.validate("accepted@sandbox.email-validation.org")
puts result.status, result.reason, result.action # valid accepted send

Build the client once and share it: it is safe to use from several threads and keeps connections alive. Keyword arguments: ip_address:, timeout: (seconds the check may take, 5–15) and fresh:. fresh: true checks the address again even when it was checked in the last 12 months (see repeat checks). Results are Data objects with the API's field names; to_h and to_json give back the API's fields.

Async

The Ruby client is synchronous and thread-safe: share one client between threads, within your rate limit, or use a bulk job for lists.

Handle errors

begin
  client.validate(address)
rescue EmailValidation::InsufficientCreditsError
  # buy credits; nothing was charged
rescue EmailValidation::RateLimitError => e
  puts "retry in #{e.retry_after} seconds"
rescue EmailValidation::Error => e
  puts "#{e.code} #{e.status} #{e.request_id}"
end

Every error is an EmailValidation::Error (a StandardError) with status (0 when no answer arrived), code, request_id and retry_after. Subclasses: InvalidRequestError, AuthenticationError, InsufficientCreditsError, PermissionDeniedError, NotFoundError, ConflictError, RateLimitError, ServiceUnavailableError, ServerError and ConnectionError. Wrong arguments raise ArgumentError before any request is sent.

Retries and timeouts

client = EmailValidation::Client.new(
  api_key: ENV.fetch("EV_API_KEY"),
  timeout: 30,        # seconds per attempt
  max_retries: 2,     # after 429 / 503 answers
  max_retry_wait: 10  # longer Retry-After values are raised to you instead of waited out
)

429 and 503 answers are retried with backoff, honouring Retry-After. A daily key cap is never retried, and a validation is never retried after a network failure, because it may already have been charged.

Credits, usage and status

credits = client.credits # balance, debt, pending, lots
usage = client.usage(from: "2026-10-19", to: "2026-11-30", group_by: "day")
status = client.status

Bulk jobs

job = client.bulk.create(emails: ["accepted@sandbox.email-validation.org", "catch_all@sandbox.email-validation.org"])

# or from a CSV file (up to 100 MB): a String, an IO or a Pathname
job = client.bulk.create(file: Pathname.new("customers.csv"), column: "email", has_header: true)

job = client.bulk.wait(job.id) { |j| puts "#{j.status} #{j.processed}" }

if job.status == EmailValidation::BulkJobStatus::COMPLETED
  csv = client.bulk.results(job.id, filter: %w[valid risky])
elsif job.stuck?
  puts "could not start: #{job.error_code}" # e.g. insufficient_credits: buy credits, then client.bulk.start(job.id)
end

wait returns when the job is completed, failed, cancelled or expired, or when it is still ready with an error_code: it could not start and does not move on by itself. Pass timeout: (seconds) to give up earlier. Also: bulk.get, bulk.start for jobs created with start: false (call it before wait), bulk.cancel, and bulk.results with a block, which streams large files in chunks.

Verify webhooks

# Rack, Sinatra or Rails
payload = request.body.read # the raw body, before any JSON parsing
begin
  event = EmailValidation.verify_webhook(payload, request.env, ENV.fetch("EV_WEBHOOK_SECRET"))
rescue EmailValidation::WebhookVerificationError
  halt 400, "invalid signature"
end

puts "job done #{event.data.job_id}" if event.type == "bulk.completed"
status 204

Headers can be any Hash with names in any case, or a Rack env. event.type can also be webhook.test, sent when you press Send test in the dashboard: answer it with a 2xx like any other event. EmailValidation.sign_webhook creates a valid signature for local tests.

Constants

EmailValidation::STATUSES, REASONS, REASONS_BY_STATUS, REASON_INFO, ACTIONS, ERROR_CODES, WEBHOOK_EVENTS, the named values in Status, Reason, Action, BulkJobStatus and ErrorCode, and EmailValidation.sandbox_address(reason) mirror the API's vocabulary. See statuses and reasons and test addresses.

Bring your own HTTP client

Pass transport: with any object that has call(request) and returns an EmailValidation::HttpResponse (or raises EmailValidation::TransportError when no answer arrived), for example to log requests or in tests. The default EmailValidation::NetHttpTransport keeps connections alive and honours the standard proxy environment variables.

Updated · Maintained by the email-validation.org team