Official Rust SDK for the IPGeolocation.io IP Geolocation API.
Look up IPv4, IPv6, and domains with /v3/ipgeo and /v3/ipgeo-bulk. Get geolocation, company, ASN, time zone, network, hostname, abuse, user-agent, and security data from one API.
- Blocking Rust client built on
reqwest - Typed responses plus raw JSON and XML methods
- Cargo package
ip-geolocation-api-rust-sdkwith library importipgeolocation
- Install
- Quick Start
- At a Glance
- Get Your API Key
- Authentication
- Plan Behavior
- Client Configuration
- Available Methods
- Request Options
- Examples
- Response Metadata
- Errors
- Troubleshooting
- Frequently Asked Questions
- Links
Requires Rust 1.88 or later.
cargo add ip-geolocation-api-rust-sdkuse ipgeolocation::{
IpGeolocationClient,
IpGeolocationClientConfig,
LookupIpGeolocationRequest,
};- Cargo package:
ip-geolocation-api-rust-sdk - Library import:
ipgeolocation - GitHub repository: https://github.com/IPGeolocation/ip-geolocation-api-rust-sdk
Note
The Cargo package name is ip-geolocation-api-rust-sdk, but the library import path is ipgeolocation.
Applications with an existing Cargo.lock should refresh their TLS and Tokio
dependencies and rebuild to receive the security fixes:
cargo update -p rustls -p rustls-webpki -p webpki-roots -p tokioUse rustls 0.23.46+, rustls-webpki 0.103.16+, webpki-roots 1.0.11+, and Tokio 1.51.6 on the 1.51 line or 1.53.3+. Updating this library's lockfile does not update an application's lockfile or an already compiled binary.
use ipgeolocation::{
IpGeolocationClient,
IpGeolocationClientConfig,
LookupIpGeolocationRequest,
};
fn main() -> Result<(), Box<dyn std::error::Error>> {
let api_key = std::env::var("IPGEO_API_KEY").expect("set IPGEO_API_KEY first");
let mut client = IpGeolocationClient::new(IpGeolocationClientConfig {
api_key: Some(api_key),
..IpGeolocationClientConfig::default()
})?;
let response = client.lookup_ip_geolocation(&LookupIpGeolocationRequest {
ip: Some("8.8.8.8".to_string()),
..LookupIpGeolocationRequest::default()
})?;
if let Some(ip) = response.data.ip.as_deref() {
println!("{ip}");
}
if let Some(location) = response.data.location.as_ref() {
if let Some(country_name) = location.country_name.as_deref() {
println!("{country_name}");
}
if let Some(city) = location.city.as_deref() {
println!("{city}");
}
}
if let Some(time_zone) = response.data.time_zone.as_ref() {
if let Some(name) = time_zone.name.as_deref() {
println!("{name}");
}
}
if let Some(credits_charged) = response.metadata.credits_charged {
println!("{credits_charged}");
}
client.close();
Ok(())
}| Item | Value |
|---|---|
| Cargo package | ip-geolocation-api-rust-sdk |
| Library import | ipgeolocation |
| Supported Endpoints | /v3/ipgeo, /v3/ipgeo-bulk |
| Supported Inputs | IPv4, IPv6, domain |
| Main Data Returned | Geolocation, company, ASN, time zone, network, hostname, abuse, user-agent, currency, security |
| Authentication | API key |
| Response Formats | Structured JSON, raw JSON, raw XML |
| Bulk Limit | Up to 50,000 IPs or domains per request |
| Transport | Blocking reqwest client |
Create an IPGeolocation.io account and copy an API key from your dashboard.
- Sign up: https://app.ipgeolocation.io/signup
- Verify your email if prompted
- Sign in: https://app.ipgeolocation.io/login
- Open your dashboard: https://app.ipgeolocation.io/dashboard
- Copy an API key from the
API Keyssection
Keep your IPGeolocation.io API key in an environment variable or secret manager.
use ipgeolocation::{IpGeolocationClient, IpGeolocationClientConfig};
let api_key = std::env::var("IPGEO_API_KEY").expect("set IPGEO_API_KEY first");
let client = IpGeolocationClient::new(IpGeolocationClientConfig {
api_key: Some(api_key),
..IpGeolocationClientConfig::default()
})?;The API key is sent as the apiKey query parameter, so avoid logging full request URLs.
Feature availability depends on your plan and request parameters.
| Capability | Free | Paid |
|---|---|---|
| Single IPv4 and IPv6 lookup | Supported | Supported |
| Domain lookup | Not supported | Supported |
| Bulk lookup | Not supported | Supported |
Non-English lang |
Not supported | Supported |
Optional modules via include |
Not supported | Supported |
include: ["*"] |
Base response only | All plan-available modules |
Paid plans still need the include parameter for optional modules. fields and excludes only trim the response. They do not turn modules on or unlock paid data.
| Field | Type | Default | Notes |
|---|---|---|---|
api_key |
Option<String> |
unset | Your IPGeolocation.io API key. |
base_url |
String |
https://api.ipgeolocation.io |
Use the final address of your API or proxy. The SDK follows redirects only when the protocol, host, and port stay the same. Must be an absolute http or https URL. |
connect_timeout_ms |
u64 |
10000 |
Time to open the connection. Must be greater than zero. |
request_timeout_ms |
u64 |
30000 |
Full-request timeout. Must be greater than zero. |
Create the client with IpGeolocationClient::new(...). Config values are validated when the client is created. Request values are validated before each request is sent.
| Method | Returns | Notes |
|---|---|---|
lookup_ip_geolocation(request) |
Result<ApiResponse<IpGeolocationResponse>, IpGeolocationError> |
Single lookup. Typed JSON response. |
lookup_ip_geolocation_raw(request) |
Result<ApiResponse<String>, IpGeolocationError> |
Single lookup. Raw JSON or XML string. |
bulk_lookup_ip_geolocation(request) |
Result<ApiResponse<Vec<BulkLookupResult>>, IpGeolocationError> |
Bulk lookup. Typed JSON response. |
bulk_lookup_ip_geolocation_raw(request) |
Result<ApiResponse<String>, IpGeolocationError> |
Bulk lookup. Raw JSON or XML string. |
lookup_ip_geolocation_detailed(request) |
Result<ApiResponse<DetailedIpGeolocationResponse>, IpGeolocationError> |
Single lookup with extra location, CDN and security fields. |
bulk_lookup_ip_geolocation_detailed(request) |
Result<ApiResponse<Vec<DetailedBulkLookupResult>>, IpGeolocationError> |
Bulk lookup with the same fields. Each row is Ok(data) or Err(error). |
close() |
() |
Marks the client closed. Closed clients cannot be reused. |
Note
Typed methods support JSON only. Use the raw methods when you need XML output.
| Field | Applies To | Notes |
|---|---|---|
ip |
Single lookup | IPv4, IPv6, or domain. Leave it empty for caller IP lookup. |
ips |
Bulk lookup | Collection of 1 to 50,000 IPs or domains. Order and duplicates are preserved. |
lang |
Single and bulk | One of en, de, ru, ja, fr, cn, es, cs, it, ko, fa, pt, ar (Arabic). |
include |
Single and bulk | Collection of module names such as security, abuse, user_agent, hostname, liveHostname, hostnameFallbackLive, geo_accuracy, dma_code, or *. |
fields |
Single and bulk | Collection of field paths to keep, for example location.country_name or security.threat_score. |
excludes |
Single and bulk | Collection of field paths to remove from the response. |
user_agent |
Single and bulk | Overrides the outbound User-Agent header. If you also pass a User-Agent header in headers, user_agent wins. |
headers |
Single and bulk | Extra request headers as BTreeMap<String, String>. |
output |
Single and bulk | ResponseFormat::Json or ResponseFormat::Xml. Typed methods require JSON. |
The examples below assume you already have a configured client in scope inside a function that returns Result<(), Box<dyn std::error::Error>>:
use ipgeolocation::{
BulkLookupIpGeolocationRequest,
IpGeolocationClient,
IpGeolocationClientConfig,
LookupIpGeolocationRequest,
ResponseFormat,
};
let mut client = IpGeolocationClient::new(IpGeolocationClientConfig {
api_key: Some(std::env::var("IPGEO_API_KEY").expect("set IPGEO_API_KEY first")),
..IpGeolocationClientConfig::default()
})?;Leave ip empty to look up the public IP of the machine making the request.
let response = client.lookup_ip_geolocation(&LookupIpGeolocationRequest::default())?;
if let Some(ip) = response.data.ip.as_deref() {
println!("{ip}");
}Domain lookup is a paid-plan feature.
let response = client.lookup_ip_geolocation(&LookupIpGeolocationRequest {
ip: Some("ipgeolocation.io".to_string()),
..LookupIpGeolocationRequest::default()
})?;
if let Some(ip) = response.data.ip.as_deref() {
println!("{ip}");
}
if let Some(domain) = response.data.domain.as_deref() {
println!("{domain}");
}let response = client.lookup_ip_geolocation(&LookupIpGeolocationRequest {
ip: Some("9.9.9.9".to_string()),
include: vec!["security".to_string(), "abuse".to_string()],
..LookupIpGeolocationRequest::default()
})?;
if let Some(security) = response.data.security.as_ref() {
println!("{:?}", security.threat_score);
println!("{:?}", security.is_vpn);
}
if let Some(abuse) = response.data.abuse.as_ref() {
println!("{:?}", abuse.emails);
}To parse a visitor user-agent string, request user_agent and send the visitor string in the request User-Agent header.
use std::collections::BTreeMap;
let mut headers = BTreeMap::new();
headers.insert(
"User-Agent".to_string(),
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_2) AppleWebKit/601.3.9 (KHTML, like Gecko) Version/9.0.2 Safari/601.3.9".to_string(),
);
let response = client.lookup_ip_geolocation(&LookupIpGeolocationRequest {
ip: Some("115.240.90.163".to_string()),
include: vec!["user_agent".to_string()],
headers,
..LookupIpGeolocationRequest::default()
})?;
if let Some(user_agent) = response.data.user_agent.as_ref() {
println!("{:?}", user_agent.name);
println!("{:?}", user_agent.operating_system.as_ref().and_then(|value| value.name.as_deref()));
}Note
The user_agent request field overrides the SDK's default outbound User-Agent header. response.data.user_agent is different. That field contains the parsed visitor user-agent data returned by the API.
let response = client.lookup_ip_geolocation(&LookupIpGeolocationRequest {
ip: Some("8.8.8.8".to_string()),
include: vec!["security".to_string()],
fields: vec![
"location.country_name".to_string(),
"security.threat_score".to_string(),
"security.is_vpn".to_string(),
],
excludes: vec!["currency".to_string()],
..LookupIpGeolocationRequest::default()
})?;
if let Some(location) = response.data.location.as_ref() {
println!("{:?}", location.country_name);
}
if let Some(security) = response.data.security.as_ref() {
println!("{:?}", security.threat_score);
println!("{:?}", security.is_vpn);
}
println!("{}", response.data.currency.is_none());let response = client.lookup_ip_geolocation_raw(&LookupIpGeolocationRequest {
ip: Some("8.8.8.8".to_string()),
output: ResponseFormat::Xml,
..LookupIpGeolocationRequest::default()
})?;
println!("{}", response.data);Each input has one result in the same position, including duplicate IPs. If a typed bulk lookup gets too few or too many results, or a row is not an object, it returns IpGeolocationError::Serialization. Raw lookups return the response as received.
let response = client.bulk_lookup_ip_geolocation(&BulkLookupIpGeolocationRequest {
ips: vec![
"8.8.8.8".to_string(),
"invalid-ip".to_string(),
"1.1.1.1".to_string(),
],
include: vec!["security".to_string()],
..BulkLookupIpGeolocationRequest::default()
})?;
for result in &response.data {
if result.is_success() {
if let Some(data) = result.data.as_ref() {
println!("{:?}", data.ip);
println!("{:?}", data.security.as_ref().and_then(|value| value.threat_score));
}
continue;
}
if let Some(error) = result.error.as_ref() {
println!("{:?}", error.message);
}
}let response = client.bulk_lookup_ip_geolocation_raw(&BulkLookupIpGeolocationRequest {
ips: vec!["8.8.8.8".to_string(), "1.1.1.1".to_string()],
..BulkLookupIpGeolocationRequest::default()
})?;
println!("{}", response.data);Every method returns ApiResponse<T>, where:
datacontains the typed object or raw response stringmetadatacontains response details such as:credits_chargedsuccessful_recordsstatus_codeduration_msraw_headers
Example:
println!("{}", response.metadata.status_code);
println!("{}", response.metadata.duration_ms);
println!("{:?}", response.metadata.raw_headers.get("content-type"));The SDK returns IpGeolocationError. It hides your API key in request errors, including errors for individual IPs in a typed bulk lookup. If an API error arrives with a broken or oversized response body, you still get its HTTP status.
| Error Variant | When it happens |
|---|---|
IpGeolocationError::Validation |
Invalid config, invalid request values, or typed XML request |
IpGeolocationError::Api |
API returned a non-2xx response |
IpGeolocationError::RequestTimeout |
Connect timeout or full-request timeout |
IpGeolocationError::Transport |
Network or transport failure |
IpGeolocationError::Serialization |
Request or response serialization failure |
IpGeolocationError::ClientClosed |
Request attempted after close() |
ApiError exposes:
status_code()message()
Example:
use ipgeolocation::{
IpGeolocationError,
LookupIpGeolocationRequest,
ResponseFormat,
};
match client.lookup_ip_geolocation(&LookupIpGeolocationRequest {
output: ResponseFormat::Xml,
..LookupIpGeolocationRequest::default()
}) {
Ok(response) => println!("{:?}", response.data.ip),
Err(IpGeolocationError::Validation(error)) => {
println!("{error}");
}
Err(IpGeolocationError::Api(error)) => {
println!("{}", error.status_code());
println!("{}", error.message());
}
Err(error) => {
println!("{error}");
}
}cargo add ip-geolocation-api-rust-sdk, butuse ipgeolocation::...in code.- Set
api_keybefore making a lookup. - Typed methods only support JSON. Use the raw methods for XML.
- If you need security, abuse, user-agent, or hostname data, include those modules explicitly.
fieldsandexcludesfilter the response. They do not unlock paid data.request_timeout_msis a full-request timeout, not a socket-only body-read timeout.- This client is blocking. If your application is async-first, run it on a blocking thread or add an async client later.
Can I request XML and still get typed models?
No. Typed methods only support JSON. Use `lookup_ip_geolocation_raw` or `bulk_lookup_ip_geolocation_raw` for XML.Why is the Cargo package name different from the library import?
The published package name is `ip-geolocation-api-rust-sdk`, but the crate you import in Rust code is `ipgeolocation`.Why are so many response fields Option values?
Optional fields let the SDK preserve omitted API fields instead of inventing empty values for data the API did not send.Does domain lookup work on the free plan?
No. Domain lookup is a paid-plan feature.Is this client async?
Not yet. The current client is blocking and uses `reqwest::blocking`.- Homepage: https://ipgeolocation.io
- IP Geolocation API product page: https://ipgeolocation.io/ip-location-api.html
- Documentation: https://ipgeolocation.io/documentation/ip-location-api.html
- Crates package: https://crates.io/crates/ip-geolocation-api-rust-sdk
- GitHub repository: https://github.com/IPGeolocation/ip-geolocation-api-rust-sdk