Ruby SDK

ip-api.io Ruby SDK
gem install ip-api-io

ip-api-io is the official, pure-Ruby client — built on net/http from the standard library. One client object wraps IP geolocation, VPN/proxy/Tor detection, email validation, and fraud risk scoring, with a typed error hierarchy and batch helpers, so you write the call, not the plumbing.

Location
Mountain View, US
Threat signals
No VPN or proxy
Footprint
Pure Ruby — net/http
client.lookup_ip("8.8.8.8")
{
"ip": "8.8.8.8",
"isp": "Google LLC",
"location": {
"country": "United States",
"city": "Mountain View",
"timezone": "America/Los_Angeles"
},
"suspicious_factors": {
"is_vpn": false,
"is_datacenter": true
}
}

What you'll build

  • Install the gem and construct one reusable client
  • Look up any IP — or the caller's IP — with one call
  • Detect VPN, proxy, Tor, and threats; batch up to 100 IPs
  • Rescue rate limits and errors the production-ready way
Trusted by thousands of businesses
Fast JSON API responses
Real-time validation
Simple integration, SDKs & examples

Prerequisites

  • Ruby 3.0 or higher
  • An ip-api.io API key — get one free
  • No other dependencies — the gem is built on net/http
1

Install the gem

Add the official ip-api-io gem to your project. It is pure Ruby — nothing to pull in beyond the standard library.

gem install ip-api-io

# ...or in a Gemfile:
# gem "ip-api-io"
2

Construct the client

Require the gem and build one client with your API key. Read the key from an environment variable — never hardcode it. The API rejects keyless requests with 401.

require "ipapi"

client = Ipapi::Client.new(api_key: ENV.fetch("IP_API_IO_KEY"))

The client is thread-safe — build one and reuse it across requests, Sidekiq jobs, or threads. In Rails, a memoized client in an initializer reading from Rails.application.credentials or ENV is a good pattern.

3

Look up an IP address

Call lookup_ip(ip) for any IPv4 or IPv6 address, or lookup to resolve the caller's own IP. Responses are typed objects; absent fields return nil, so guard with &..

info = client.lookup_ip("8.8.8.8")

puts info.ip                            # "8.8.8.8"
puts info.isp                           # "Google LLC"
puts info.location.country              # "United States"
puts info.suspicious_factors.datacenter? # true

# Resolve the caller's own IP:
me = client.lookup
puts me.ip
nil for nullable fields: location fields like country and city return nil for private ranges or unrecognized addresses — reach for the safe-navigation operator, e.g. info.location&.city. Boolean signals such as info.suspicious_factors.vpn? are predicate methods.
4

Detect VPN, proxy, Tor & batch lookups

The suspicious_factors object carries seven boolean predicates on every lookup — this is what sets ip-api.io apart from plain geolocation APIs. You get security intelligence in the same call, at no extra cost.

info = client.lookup_ip("185.220.101.45") # example Tor exit node
f = info.suspicious_factors

if f.vpn? || f.proxy? || f.tor_node?
  puts "Anonymized traffic — consider a CAPTCHA or block"
end
puts "Cloud/datacenter IP — likely automated" if f.datacenter?
puts "Active threat signal — block this request" if f.threat?

risky = f.vpn? || f.proxy? || f.tor_node? || f.threat?

Need to check many IPs at once? lookup_batch takes up to 100 addresses in a single request (it raises ArgumentError if the array is empty or longer than 100):

batch = client.lookup_batch(["8.8.8.8", "1.1.1.1", "9.9.9.9"])

puts batch.total_processed
puts batch.successful_lookups

batch.results.each do |ip, info|
  puts "#{ip} #{info.suspicious_factors.vpn?}"
end
Tip: want a single block / review / allow decision instead of raw flags? Use client.risk_score_ip(ip) for a combined fraud risk score from 0 to 100.
5

Production-ready error handling

Errors are raised as exceptions under Ipapi::Error. Rescue the specific subclass you care about — the client never retries on its own, so on a rate limit RateLimitError#reset tells you exactly when to try again.

begin
  info = client.lookup_ip("8.8.8.8")
  puts info.ip
rescue Ipapi::RateLimitError => e
  puts "Rate limited. Remaining: #{e.remaining}. Resets at #{e.reset}"
rescue Ipapi::AuthenticationError
  puts "Invalid or missing API key — get one free at https://ip-api.io"
rescue Ipapi::InvalidRequestError => e
  puts "Bad request: #{e.message}"
rescue Ipapi::ServerError
  puts "ip-api.io is having issues — try again later"
rescue Ipapi::Error => e
  puts "Transport / decode error: #{e.message}"
end
No automatic retries: wrap calls in your own retry/backoff logic and use RateLimitError#reset (a Unix timestamp) to schedule the next attempt. Transport failures (DNS, connect, timeout, parse) are raised as Ipapi::Error and caught by the final rescue.

In a Rails controller, run the lookup server-side and pass the real client IP from the X-Forwarded-For header (request.remote_ip already resolves it):

class CheckoutController < ApplicationController
  def create
    info = IPAPI_CLIENT.lookup_ip(request.remote_ip)
    render json: {
      ip:        info.ip,
      country:   info.location&.country,
      is_vpn:    info.suspicious_factors.vpn?,
      is_threat: info.suspicious_factors.threat?
    }
  rescue Ipapi::Error
    # Treat ip-api.io as a non-critical dependency
    render json: { ip: request.remote_ip, country: nil }
  end
end
Reference

Response & method reference

lookup_ip returns a typed Ipapi::IPInfo with three parts: the top-level ip/isp/asn, a suspicious_factors object, and a location object. Absent fields return nil for private ranges or unrecognized addresses.

client .lookup_ip("78.55.53.58")
{
  "ip": "78.55.53.58",
  "isp": "Deutsche Telekom AG",
  "asn": "AS3320",
  "suspicious_factors": {
    "is_proxy":      false,
    "is_tor_node":   false,
    "is_spam":       false,
    "is_crawler":    false,
    "is_datacenter": false,
    "is_vpn":        false,
    "is_threat":     false
  },
  "location": {
    "country":           "Germany",
    "country_code":      "DE",
    "city":              "Berlin",
    "latitude":          52.5694,
    "longitude":         13.3753,
    "zip":               "13409",
    "timezone":          "Europe/Berlin",
    "local_time":        "2024-05-20T22:16:52+02:00",
    "local_time_unix":   1716236212,
    "is_daylight_savings": true
  }
}

suspicious_factors — security signals (all predicates)

MethodDescription
vpn?VPN service, corporate gateway, or self-hosted VPN detected
proxy?HTTP, HTTPS, or SOCKS proxy (~99.5% accuracy)
tor_node?Tor exit node, relay, or bridge (updated in real time)
datacenter?Cloud provider (AWS, GCP, Azure), VPS, or hosting facility
threat?Active security threat — malware C&C, botnet, or DDoS source
spam?Associated with spam, phishing, or malware email campaigns
crawler?Known web crawler, scraper, or bot

location — geographic and timezone data

MethodTypeDescription
countryString, nilFull country name in English (ISO 3166-1)
country_codeString, nilISO 3166-1 alpha-2 code (e.g. "DE", "US")
cityString, nilCity or municipality name (85–95% accuracy)
latitudeFloat, nilDecimal degrees, WGS84 — ~50 km median accuracy radius
longitudeFloat, nilDecimal degrees, WGS84 — ~50 km median accuracy radius
zipString, nilPostal or ZIP code in country-specific format
timezoneString, nilIANA timezone identifier (e.g. "America/Los_Angeles")
local_timeString, nilCurrent local time in ISO 8601 with UTC offset
local_time_unixInteger, nilUnix timestamp (seconds) in local timezone
daylight_savings?BooleanTrue if currently observing DST

Client methods

MethodReturnsWhat it does
lookup / lookup_ip(ip)IPInfoGeolocate the caller's IP, or a specific IP
lookup_batch(ips)BatchIPLookupGeolocate up to 100 IPs in one call
risk_score / risk_score_ip(ip)RiskScoreFraud risk score (0–100) for an IP
tor_check(ip)TorDetectionCheck whether an IP is a Tor node
email_info(email)EmailInfoBasic email validation (syntax, MX, disposable)
validate_email(email)AdvancedEmailValidationFull validation incl. SMTP deliverability
asn(ip)ASNLookupAutonomous System Number lookup
whois(domain)WhoisWHOIS domain registration info
domain_age(domain)DomainAgeDomain registration date and age in days
rate_limitRateLimitInfoRemaining quota and plan limits
Full surface: the client also exposes validate_email_batch, email_risk_score, ip_reputation, reverse_dns, forward_dns, mx_records, domain_age_batch, and usage_summary. See the README and per-feature guides.
FAQ

Frequently asked questions

Common questions about the ip-api.io Ruby SDK.

Is the ip-api-io gem official?

Yes. ip-api-io is the official Ruby client for ip-api.io, maintained by the ip-api.io team and published to RubyGems from github.com/ip-api-io/ipapi-ruby. It is MIT licensed and not affiliated with ip-api.com or ipapi.com.

How do I install the ip-api.io Ruby SDK?

Run gem install ip-api-io (or add gem "ip-api-io" to your Gemfile and bundle install), then require it and construct a client:

# gem install ip-api-io
require "ipapi"

client = Ipapi::Client.new(api_key: ENV["IP_API_IO_KEY"])

It is pure Ruby — built on net/http from the standard library — and works on Ruby 3.0+.

Does it work with Rails?

Yes. The gem is plain Ruby with no framework assumptions, so it drops into Rails, Sidekiq jobs, Sinatra, or a script. A common pattern is one memoized client in an initializer reading the key from Rails.application.credentials or ENV, reused across requests — the client is thread-safe.

How are nullable fields represented?

Responses are typed objects with reader methods. Absent fields return nil (e.g. info.location.country), so guard with &. or a nil check. Boolean signals are predicate methods ending in ?, such as info.suspicious_factors.vpn?.

How do I handle rate limits?

Errors are raised as exceptions. Rescue Ipapi::RateLimitError, which exposes #limit, #remaining, and #reset parsed from the response headers. The client never retries automatically — schedule your retry after #reset.

Pricing

Pricing

Start using IP-API.io to make your website safer and more user-friendly. Keep out unwanted bots, show visitors content that's relevant to where they are, and spot risky IP addresses quickly. It's perfect for making online shopping more personal and keeping your site secure. Get started today with one of the plans!

Small
€10 /mo
100,000 geo ip requests
10,000 advanced email validation requests
Location data
Email validation
Risk score calculation
Currency data
Time zone data
Threat data
Unlimited support
HTTPS encryption
Create Account to Subscribe
Medium
€20 /mo
300,000 geo ip requests
25,000 advanced email validation requests
Location data
Email validation
Risk score calculation
Currency data
Time zone data
Threat data
Unlimited support
HTTPS encryption
Create Account to Subscribe

Need support?

Explore how IP-API.io can enhance your security, provide robust bot protection, and improve IP geolocation accuracy for your applications.

Contact Support

Found a bug in the SDK?

The ip-api-io gem is open source. Open an issue or pull request on GitHub and we'll take a look.

Open on GitHub