Integrating Fuego Email Verification API with Ruby

This guide will walk you through integrating the Fuego Email Verification API into your Ruby applications. Whether you’re using Ruby on Rails, Sinatra, or a standalone Ruby script, this guide provides comprehensive implementation examples.

Prerequisites

  • A Fuego account with an API key
  • Ruby 2.5 or newer
  • Basic knowledge of Ruby and HTTP requests

API Endpoint

The Fuego API endpoint for email verification is:

https://app.fuegoverify.com/api/v1/verify

Authentication

Authentication is done via an API token passed as a query parameter:

api_token = "YOUR_API_TOKEN"
# Token will be added to the URL as a query parameter

Method 1: Using Ruby’s Standard Library (Net::HTTP)

Ruby’s built-in Net::HTTP library can be used to make API requests without any additional gems:

require 'net/http'
require 'uri'
require 'json'

def verify_email(email)
  api_token = "YOUR_API_TOKEN"
  
  # Prepare the URL with query parameters
  uri = URI("https://app.fuegoverify.com/api/v1/verify")
  params = { email: email, token: api_token }
  uri.query = URI.encode_www_form(params)
  
  # Prepare the request
  http = Net::HTTP.new(uri.host, uri.port)
  http.use_ssl = true  # Enable HTTPS
  
  request = Net::HTTP::Get.new(uri)
  request["Content-Type"] = "application/json"
  
  begin
    # Send the request
    response = http.request(request)
    
    # Parse and return the response
    case response
    when Net::HTTPSuccess
      return JSON.parse(response.body)
    when Net::HTTPTooManyRequests
      raise "Rate limit exceeded. Please try again later."
    when Net::HTTPUnauthorized
      raise "Invalid API token. Please check your credentials."
    else
      # Try to get error message from response
      begin
        error_data = JSON.parse(response.body)
        error_message = error_data["error"] || "Unknown error"
      rescue
        error_message = "HTTP Error: #{response.code} #{response.message}"
      end
      
      raise error_message
    end
  rescue => e
    if e.is_a?(JSON::ParserError)
      raise "Failed to parse API response"
    else
      raise "API request failed: #{e.message}"
    end
  end
end

# Usage example
begin
  result = verify_email("[email protected]")
  puts "Verification result: #{result['result']}"
  
  if result['result'] == "valid"
    puts "Email is valid!"
  else
    puts "Email is #{result['result']}: #{result['reason']}"
  end
  
  # Access domain intelligence
  if result['domain_insight']
    puts "Company: #{result['domain_insight']['name']}"
    puts "Industry: #{result['domain_insight']['industry']}"
  end
rescue => e
  puts "Error: #{e.message}"
end

Method 2: Using HTTParty

HTTParty is a popular gem that simplifies HTTP requests in Ruby:

# First install the gem: gem install httparty
require 'httparty'

class EmailVerifier
  include HTTParty
  base_uri 'https://app.fuegoverify.com/api/v1'
  
  def initialize(api_token)
    @api_token = api_token
    @headers = {
      "Content-Type" => "application/json"
    }
  end
  
  def verify(email)
    options = {
      headers: @headers,
      query: { 
        email: email,
        token: @api_token
      }
    }
    
    begin
      response = self.class.get('/verify', options)
      
      if response.success?
        return response.parsed_response
      elsif response.code == 429
        raise "Rate limit exceeded. Please try again later."
      elsif response.code == 401
        raise "Invalid API token. Please check your credentials."
      else
        error_message = response.parsed_response&.dig("error") || "HTTP Error: #{response.code}"
        raise error_message
      end
    rescue => e
      raise "Verification failed: #{e.message}"
    end
  end
end

# Usage example
verifier = EmailVerifier.new("YOUR_API_KEY")

begin
  result = verifier.verify("[email protected]")
  puts "Verification result: #{result['result']}"
  
  # Process result similar to previous example
rescue => e
  puts "Error: #{e.message}"
end

Method 3: Using Faraday

Faraday is another popular HTTP client with middleware support:

# First install the gem: gem install faraday
require 'faraday'
require 'json'

class FuegoClient
  def initialize(api_token)
    @api_token = api_token
    @connection = Faraday.new(url: 'https://app.fuegoverify.com/api/v1') do |faraday|
      faraday.request :url_encoded
      faraday.adapter Faraday.default_adapter
    end
  end
  
  def verify_email(email)
    response = @connection.get('verify') do |req|
      req.headers['Content-Type'] = 'application/json'
      req.params['email'] = email
      req.params['token'] = @api_token
    end
    
    handle_response(response)
  end
  
  private
  
  def handle_response(response)
    case response.status
    when 200
      JSON.parse(response.body)
    when 429
      raise "Rate limit exceeded. Please try again later."
    when 401
      raise "Invalid API token. Please check your credentials."
    else
      begin
        error_data = JSON.parse(response.body)
        error_message = error_data["error"] || "Unknown error"
      rescue
        error_message = "HTTP Error: #{response.status}"
      end
      
      raise error_message
    end
  rescue JSON::ParserError
    raise "Failed to parse API response"
  end
end

# Usage example
client = FuegoClient.new("YOUR_API_KEY")

begin
  result = client.verify_email("[email protected]")
  puts "Verification result: #{result['result']}"
  
  # Process result similar to previous examples
rescue => e
  puts "Error: #{e.message}"
end

Working with the Response

The API returns a JSON response with detailed information about the email:

{
  "email" => "[email protected]",
  "result" => "valid",    # or "invalid", "risky", "unknown"
  "reason" => nil,        # reason for invalid or risky result
  "role" => false,        # true for role-based emails like "admin@" or "support@"
  "free" => false,        # true for free email providers like Gmail or Yahoo
  "disposable" => false,  # true for temporary/disposable email addresses
  "accept_all" => false,  # true if the domain accepts all emails
  "did_you_mean" => nil,  # suggestion for misspelled emails
  "domain" => "techcorp.com",
  "user" => "alex",
  "success" => true,
  "domain_insight" => {
    "category" => "organization",
    "name" => "TechCorp Inc.",
    "industry" => "Software Development",
    "country" => "US",
    "ticker" => "TCH",
    "employees" => 3500,
    "description" => "TechCorp Inc. is a leading software development company..."
  }
}

Handling Common Results

Here’s a utility method to process verification results:

def handle_verification_result(result)
  case result["result"]
  when "valid"
    {
      valid: true,
      message: "Email address is valid and deliverable",
      details: result
    }
  when "invalid"
    {
      valid: false,
      message: "Email address is invalid: #{result['reason'] || 'Unknown reason'}",
      details: result
    }
  when "risky"
    {
      valid: false,
      risky: true,
      message: "Email address is risky: #{result['reason'] || 'Unknown reason'}",
      details: result
    }
  when "unknown"
    {
      valid: nil,
      message: "Verification result is inconclusive",
      details: result
    }
  else
    {
      valid: nil,
      message: "Unknown verification result",
      details: result
    }
  end
end

# Example usage
begin
  result = verify_email("[email protected]")
  status = handle_verification_result(result)
  
  puts status[:message]
  
  # You can also access domain intelligence
  if status[:details]["domain_insight"]
    insight = status[:details]["domain_insight"]
    puts "Company: #{insight['name']}" if insight['name']
    puts "Industry: #{insight['industry']}" if insight['industry']
  end
rescue => e
  puts "Error: #{e.message}"
end

Ruby on Rails Integration

Here’s how you can integrate email verification into a Rails application:

1. Create a Service Object

First, create a service object to encapsulate the API interaction:

# app/services/email_verification_service.rb
require 'httparty'

class EmailVerificationService
  include HTTParty
  base_uri 'https://app.fuegoverify.com/api/v1'
  
  def initialize
    @api_token = Rails.application.credentials.fuego[:api_token]
    @headers = {
      "Content-Type" => "application/json"
    }
  end
  
  def verify(email)
    options = {
      headers: @headers,
      query: { 
        email: email,
        token: @api_token
      }
    }
    
    begin
      response = self.class.get('/verify', options)
      
      if response.success?
        return response.parsed_response
      elsif response.code == 429
        Rails.logger.error("Fuego API rate limit exceeded")
        raise "Rate limit exceeded. Please try again later."
      elsif response.code == 401
        Rails.logger.error("Fuego API authentication failed")
        raise "Invalid API token. Please check your credentials."
      else
        error_message = response.parsed_response&.dig("error") || "HTTP Error: #{response.code}"
        Rails.logger.error("Fuego API error: #{error_message}")
        raise error_message
      end
    rescue => e
      Rails.logger.error("Email verification failed: #{e.message}")
      raise "Verification failed: #{e.message}"
    end
  end
  
  def valid?(email)
    result = verify(email)
    result["result"] == "valid"
  rescue
    false
  end
  
  def handle_result(result)
    case result["result"]
    when "valid"
      { valid: true, message: "Email is valid" }
    when "invalid"
      { valid: false, message: "Email is invalid: #{result['reason']}" }
    when "risky"
      { valid: false, risky: true, message: "Email is risky: #{result['reason']}" }
    else
      { valid: false, message: "Email verification failed" }
    end
  end
end

2. Store the API Key Securely

Add your API key to Rails credentials:

rails credentials:edit

Add the following to your credentials file:

fuego:
  api_token: YOUR_API_TOKEN

3. Create a Custom Validator

# app/validators/email_validator.rb
class EmailValidator < ActiveModel::EachValidator
  def validate_each(record, attribute, value)
    return if value.blank?
    
    # Basic format validation
    unless value =~ /\A([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})\z/i
      record.errors.add(attribute, "is not a valid email format")
      return
    end
    
    # Skip API validation in test environment
    return if Rails.env.test?
    
    # Skip if we've already validated this email recently
    cache_key = "email_validation:#{value}"
    cached = Rails.cache.read(cache_key)
    
    if cached
      record.errors.add(attribute, cached[:message]) unless cached[:valid]
      return
    end
    
    # Perform API validation
    begin
      verification_service = EmailVerificationService.new
      result = verification_service.verify(value)
      status = verification_service.handle_result(result)
      
      # Cache the result (for 1 day)
      Rails.cache.write(cache_key, status, expires_in: 1.day)
      
      # Add error if not valid
      record.errors.add(attribute, status[:message]) unless status[:valid]
      
      # Store domain intelligence if available
      if result["domain_insight"] && record.respond_to?(:domain_insight=)
        record.domain_insight = result["domain_insight"]
      end
      
    rescue => e
      # Log the error but don't fail validation on API errors
      Rails.logger.error("Email validation API error: #{e.message}")
    end
  end
end

4. Use the Validator in Your Models

# app/models/user.rb
class User < ApplicationRecord
  attr_accessor :domain_insight
  
  validates :email, presence: true, uniqueness: true, email: true
  
  # Optional: Store domain intelligence
  after_validation :store_domain_intelligence, if: -> { @domain_insight.present? }
  
  private
  
  def store_domain_intelligence
    # Store relevant company information
    self.company_name = @domain_insight["name"] if @domain_insight["name"].present?
    self.industry = @domain_insight["industry"] if @domain_insight["industry"].present?
    self.employee_count = @domain_insight["employees"] if @domain_insight["employees"].present?
  end
end

5. Add Controller Logic

# app/controllers/users_controller.rb
class UsersController < ApplicationController
  def new
    @user = User.new
  end
  
  def create
    @user = User.new(user_params)
    
    if @user.save
      redirect_to @user, notice: 'User was successfully created.'
    else
      render :new
    end
  end
  
  def verify_email
    verifier = EmailVerificationService.new
    
    begin
      result = verifier.verify(params[:email])
      render json: result
    rescue => e
      render json: { error: e.message }, status: :unprocessable_entity
    end
  end
  
  private
  
  def user_params
    params.require(:user).permit(:email, :name, :password, :password_confirmation)
  end
end

6. Add JavaScript for Real-time Validation

// app/javascript/controllers/email_validator_controller.js
import { Controller } from "@hotwired/stimulus"

export default class extends Controller {
  static targets = [ "input", "status" ]
  
  connect() {
    this.timeout = null
    this.inputTarget.addEventListener("blur", this.validateEmail.bind(this))
  }
  
  validateEmail() {
    const email = this.inputTarget.value
    
    if (!email || !email.includes('@')) {
      return
    }
    
    this.statusTarget.textContent = "Verifying email..."
    this.statusTarget.className = "text-info"
    
    clearTimeout(this.timeout)
    
    this.timeout = setTimeout(() => {
      fetch(`/verify_email?email=${encodeURIComponent(email)}`)
        .then(response => response.json())
        .then(data => {
          if (data.result === "valid") {
            this.statusTarget.textContent = "✓ Email is valid"
            this.statusTarget.className = "text-success"
          } else if (data.result === "risky") {
            this.statusTarget.textContent = `⚠️ ${data.reason || "Email is risky"}`
            this.statusTarget.className = "text-warning"
          } else {
            this.statusTarget.textContent = `✗ ${data.reason || "Email is invalid"}`
            this.statusTarget.className = "text-danger"
          }
          
          if (data.did_you_mean) {
            this.statusTarget.textContent += ` - Did you mean ${data.did_you_mean}?`
          }
        })
        .catch(error => {
          this.statusTarget.textContent = "Verification failed"
          this.statusTarget.className = "text-danger"
        })
    }, 500)
  }
}

7. Add Routes

# config/routes.rb
Rails.application.routes.draw do
  # ...
  get '/verify_email', to: 'users#verify_email'
  # ...
end

Batch Processing with Sidekiq

For bulk email verification, Sidekiq can be used to process emails asynchronously:

# app/workers/email_verification_worker.rb
class EmailVerificationWorker
  include Sidekiq::Worker
  sidekiq_options retry: 3
  
  def perform(email_id)
    email_record = EmailAddress.find(email_id)
    verifier = EmailVerificationService.new
    
    begin
      result = verifier.verify(email_record.address)
      
      # Update the record with verification results
      email_record.update(
        verification_status: result["result"],
        verification_reason: result["reason"],
        domain_intelligence: result["domain_insight"],
        verified_at: Time.current
      )
      
    rescue => e
      email_record.update(
        verification_status: "error",
        verification_reason: e.message,
        verified_at: Time.current
      )
    end
  end
end

# Batch processing example
class EmailList < ApplicationRecord
  has_many :email_addresses
  
  def verify_all
    email_addresses.find_each do |email|
      EmailVerificationWorker.perform_async(email.id)
    end
  end
end

Rate Limiting and Backoff Strategy

For handling rate limits, implement an exponential backoff strategy:

require 'httparty'

class ResilientEmailVerifier
  include HTTParty
  base_uri 'https://app.fuegoverify.com/api/v1'
  
  def initialize(api_key)
    @api_key = api_key
    @headers = {
      "Authorization" => "Bearer #{api_key}",
      "Content-Type" => "application/json"
    }
  end
  
  def verify_with_retry(email, max_retries: 3)
    retries = 0
    
    begin
      options = {
        headers: @headers,
        query: { email: email }
      }
      
      response = self.class.get('/verify', options)
      
      if response.success?
        return response.parsed_response
      elsif response.code == 429 && retries < max_retries
        retries += 1
        # Exponential backoff with jitter
        sleep_time = (2 ** retries) + rand(0.1..0.5)
        puts "Rate limited. Retrying in #{sleep_time} seconds (Attempt #{retries}/#{max_retries})"
        sleep(sleep_time)
        retry
      elsif response.code == 401
        raise "Invalid API key. Please check your credentials."
      else
        error_message = response.parsed_response&.dig("error") || "HTTP Error: #{response.code}"
        raise error_message
      end
    rescue => e
      if e.message.include?("Rate limit") && retries < max_retries
        retries += 1
        sleep_time = (2 ** retries) + rand(0.1..0.5)
        puts "Rate limited. Retrying in #{sleep_time} seconds (Attempt #{retries}/#{max_retries})"
        sleep(sleep_time)
        retry
      else
        raise "Verification failed: #{e.message}"
      end
    end
  end
end

Processing CSV Files

A common use case is verifying emails from a CSV file:

require 'csv'
require 'httparty'

class CsvEmailProcessor
  def initialize(api_key, input_file, output_file)
    @verifier = ResilientEmailVerifier.new(api_key)
    @input_file = input_file
    @output_file = output_file
  end
  
  def process
    emails = read_csv
    puts "Processing #{emails.count} emails..."
    
    results = emails.map.with_index do |email, index|
      puts "Verifying email #{index + 1}/#{emails.count}: #{email}"
      
      begin
        result = @verifier.verify_with_retry(email)
        sleep(0.2) # Rate limiting safeguard
        [email, result]
      rescue => e
        puts "  Error: #{e.message}"
        [email, { "result" => "error", "reason" => e.message }]
      end
    end
    
    write_csv(results)
    puts "Completed! Results written to #{@output_file}"
  end
  
  private
  
  def read_csv
    emails = []
    
    CSV.foreach(@input_file, headers: true) do |row|
      email_column = row['email'] || row['Email'] || row[0]
      emails << email_column.strip if email_column
    end
    
    emails
  end
  
  def write_csv(results)
    CSV.open(@output_file, 'w') do |csv|
      # Write headers
      csv << [
        'Email', 
        'Status', 
        'Reason', 
        'Company', 
        'Industry', 
        'Employee Count', 
        'Country'
      ]
      
      # Write data
      results.each do |email, result|
        company = ''
        industry = ''
        employees = ''
        country = ''
        
        if result["domain_insight"]
          company = result["domain_insight"]["name"]
          industry = result["domain_insight"]["industry"]
          employees = result["domain_insight"]["employees"]
          country = result["domain_insight"]["country"]
        end
        
        csv << [
          email,
          result["result"],
          result["reason"],
          company,
          industry,
          employees,
          country
        ]
      end
    end
  end
end

# Usage
processor = CsvEmailProcessor.new(
  "YOUR_API_KEY",
  "input_emails.csv",
  "verification_results.csv"
)
processor.process

Conclusion

You now have all the tools you need to integrate the Fuego Email Verification API into your Ruby applications. This integration will help ensure that your email data is valid and deliverable, improving your email campaigns and user data quality.

For more information and advanced usage, refer to the complete API documentation.

Pricing

For information about pricing, see our pricing page where you can calculate the exact cost for your email verification needs.