Web Data LabsBlog › IP Geolocation Scraper

How to Geolocate IP Addresses in Bulk Programmatically (ip-api.com Guide)

IP geolocation is one of those things that looks trivial until you actually have a million log lines to enrich. One IP at a time? Easy. A hundred thousand IPs from last month's nginx logs? Suddenly you're staring at rate limits, paid tiers, and a queue that would take three days to drain.

I've had to do this enough times - for analytics dashboards, fraud signals, log analysis - that I just built a small wrapper and stopped reinventing the loop every time. Here's what actually works in 2026, and how to skip the plumbing.

Why this keeps coming up

You'd be surprised how many real projects need an IP → country/city/ISP lookup at scale. A few I've seen lately:

The pattern is always the same: you have a list of IPs, you want a row of structured fields per IP, you want it cheap and you don't want to set up your own MaxMind database update pipeline.

The free option that actually works

The simplest free path is ip-api.com. No API key, no signup. They have a batch endpoint that takes up to 100 IPs per call and returns a structured array. The free tier is HTTP-only and limited to 45 requests per minute, which is fine for most workloads if you respect it.

You get the obvious fields: country, country code, region, city, zip, lat/lon, timezone. And the less obvious but more useful ones: ISP name, organization name, and ASN (autonomous system number). The ASN is what tells you "this IP belongs to AWS" or "this is a residential Comcast subscriber" - which matters a lot if you're doing fraud or bot detection.

There are paid tiers if you need HTTPS, higher throughput, or commercial usage at scale. For most projects under a million lookups a day, the free tier batched at 100 IPs is more than enough.

The annoying parts

A few things that bite people the first time they wire this up:

Rate limit is per IP that's calling, not per IP being looked up. 45 req/min sounds generous until you realize one batch = one request, so you're looking at ~4,500 IPs/min if you batch properly, or 45 IPs/min if you call them one at a time like a tutorial taught you. Always batch.

Don't request fields you don't need. The default response is bloated. Pass a fields filter so the response is small and the rate-limit accounting is happier.

Some IPs come back as fail. Reserved ranges, bogons, malformed addresses. Always check the status field per row before you trust the data. Don't assume a successful HTTP response means every row succeeded.

The data is good, not perfect. No IP geolocation database is. City-level accuracy is around 70-80% for residential IPs and worse for mobile networks (where you might land in the carrier's home city instead of the actual user). Country-level is essentially perfect. Don't build your billing around "this user is in San Francisco" - build it around country.

The easy path - our Apify actor

If you don't want to deal with batching, retries, rate limit pacing, and dataset storage, we built an Apify actor that wraps the ip-api.com batch endpoint: cryptosignals/ip-geolocation-scraper.

You hand it an array of IPs, it batches them 100 at a time, paces itself under the rate limit, and gives you a flat dataset. No code to write.

Input:

{
  "ips": [
    "8.8.8.8",
    "1.1.1.1",
    "208.67.222.222",
    "9.9.9.9",
    "185.228.168.9"
  ]
}

Output (one row per IP):

{
  "ip": "8.8.8.8",
  "status": "success",
  "country": "United States",
  "country_code": "US",
  "region": "VA",
  "region_name": "Virginia",
  "city": "Ashburn",
  "zip": "20149",
  "lat": 39.03,
  "lon": -77.5,
  "timezone": "America/New_York",
  "isp": "Google LLC",
  "org": "Google Public DNS",
  "as_number": "AS15169 Google LLC",
  "scraped_at": "2026-05-03T18:08:00.000Z"
}

Pricing is $0.01 per IP geolocated. Ten thousand IPs = $100. A million IPs = a real conversation, but cheaper than building and hosting your own pipeline. No subscription, no minimum, no batching code on your end.

When you need something heavier

Once you start doing IP geolocation as part of a larger scraping operation - say, scraping a website that serves different content based on the visitor's IP, or testing a geo-restricted product across multiple regions - free public lookup APIs aren't enough. You need to actually be in those countries, not just know about them.

For that kind of work you want residential proxies. We use Oxylabs for the heavy stuff - their residential pool covers basically every country, the success rate on geo-fenced sites is solid, and you can pin a session to a specific city. Overkill for plain IP lookup, exactly right for testing localized SERPs, ad creatives, or pricing pages from inside the target country.

Where to go from here

If you just need to enrich a list of IPs once, run the actor with your list and download the dataset as CSV. Done in under a minute.

If you need this on an ongoing basis - say, enriching new signups in real time - call the actor on a schedule, or fold it into your ETL pipeline. The Apify API gives you signed run URLs you can hit from any backend.

And if you find yourself reaching for fraud signals beyond plain geolocation - hosting/datacenter detection, proxy/VPN detection, abuse history - that's a different layer of data. Geolocation is the cheap first pass. Fraud scoring is the expensive second pass. Don't conflate them.