Prerequisites
- A stable Rust toolchain with
ip-api-ioadded - A free ip-api.io API key
Match the Error enum
Each API-status variant carries a status and message. Match the specific variant you care about.
use ip_api_io::Error;
match client.lookup_ip("8.8.8.8").await {
Ok(info) => println!("{}", info.ip),
Err(Error::RateLimit { reset, .. }) => {
println!("quota hit — resets at {reset}");
}
Err(Error::Authentication { .. }) => println!("check your API key"),
Err(Error::InvalidRequest { message, .. }) => println!("bad request: {message}"),
Err(Error::Server { .. }) => println!("ip-api.io is having trouble, try later"),
Err(Error::Transport(e)) => println!("transport / decode error: {e}"),
Err(e) => println!("other error: {e}"),
} Error::Transport, which wraps the underlying reqwest error —
they are not an API status error.
Handle rate limits with the RateLimit variant
On HTTP 429 you get Error::RateLimit, carrying the x-ratelimit-* header values (-1 when a header is absent). Because the client never retries, reset tells you when to.
Err(Error::RateLimit { limit, remaining, reset }) => {
println!("{limit}"); // your quota for the window
println!("{remaining}"); // requests left (0 here)
println!("{reset}"); // unix timestamp when quota renews
// schedule a retry at `reset` instead of hammering the API
} Check quota proactively with rate_limit
Read your current limits without triggering a 429, so you can throttle in advance.
let rl = client.rate_limit().await?;
if let Some(plan) = &rl.plan_name {
println!("{plan}");
}
println!("{} / {}", rl.ip_api.remaining, rl.ip_api.limit);
println!("{:.1}% used", rl.email_api.usage_percent); Account usage with usage_summary
Aggregate usage for the current period — handy for dashboards and internal alerts.
let usage = client.usage_summary().await?;
println!("{} {}", usage.total_requests, usage.successful_requests);
println!("{} {}", usage.rate_limited_requests, usage.quota_consumed);
println!("{} {}", usage.period_start, usage.period_end);