ScreenshotNeo

BlogHow-to

Explore IP Geolocation on an Interactive Map

Map an IP address to its approximate country, region or city, then build a safe multi-IP visualization with code, accuracy guidance and troubleshooting.

By the ScreenshotNeo team1 October 20268 min read

Direct answer: An IP geolocation map plots the approximate area that a data provider associates with an IP address. It can show country, region, city, latitude/longitude and sometimes postal code. It is not a GPS fix, a street-address lookup or a way to track a person or device. For multiple addresses, fetch the location records, validate the coordinates, then render one marker per record on a map.

IPinfo describes the result as a general area associated with an IP, not the physical location of a specific device or person. Its API documentation lists continent, country, region, city, coordinates and postal code fields. Read the IPinfo geolocation field reference.

What an IP map actually shows

A marker represents a database association. Internet providers, mobile carriers, VPN operators, cloud hosts and privacy networks can make that association broad or misleading. Treat every pin as approximate IP location or location associated with this IP.

  • Country: usually the most stable level of detail.
  • Region and city: available when the provider’s plan and database support them.
  • Latitude and longitude: useful for visualization, but the coordinate can represent a city center, network office or other estimate.
  • Postal code: may be present, but does not establish a street address.
  • ASN and organization: helps explain why a result points to a cloud region, carrier or business network.

IPinfo’s support guidance is direct: “IP geolocation identifies the general area associated with an IP address, not the physical location of a specific device or person.” Source and limitations.

Fastest way to explore one IP

  1. Obtain a public IPv4 or IPv6 address. Do not submit private addresses such as 10.0.0.0/8, 172.16.0.0/12 or 192.168.0.0/16; they are not globally routable.
  2. Look up the address with a provider that returns coordinates.
  3. Read the country, region and city fields before looking at the map.
  4. Plot the returned latitude and longitude and label the marker as approximate.
  5. Record the provider, lookup time and response fields if the map will support an operational decision.

Command-line lookup with cURL

curl -sS "https://ipinfo.io/8.8.8.8/json" \
  -H "Accept: application/json" \
  -H "Authorization: Bearer $IPINFO_TOKEN"

If your account uses a token query parameter instead, follow that provider’s current authentication documentation. Never put a private token in browser JavaScript or a public map URL.

Python lookup

import os
import requests

ip = "8.8.8.8"
headers = {"Accept": "application/json"}
if os.getenv("IPINFO_TOKEN"):
    headers["Authorization"] = f"Bearer {os.environ['IPINFO_TOKEN']}"

response = requests.get(f"https://ipinfo.io/{ip}/json", headers=headers, timeout=15)
response.raise_for_status()
data = response.json()
print({key: data.get(key) for key in ("ip", "country", "region", "city", "loc", "postal", "org")})

Node.js lookup

const ip = '8.8.8.8';
const headers = { Accept: 'application/json' };
if (process.env.IPINFO_TOKEN) {
  headers.Authorization = `Bearer ${process.env.IPINFO_TOKEN}`;
}

const res = await fetch(`https://ipinfo.io/${ip}/json`, { headers });
if (!res.ok) throw new Error(`Lookup failed: ${res.status}`);
const data = await res.json();
console.log({
  ip: data.ip,
  country: data.country,
  region: data.region,
  city: data.city,
  loc: data.loc,
  postal: data.postal,
  org: data.org
});

Build a simple interactive map

The example below expects a JSON file containing already-looked-up records. Keeping lookup credentials on a server prevents accidental exposure in the browser. The map uses Leaflet and OpenStreetMap tiles; follow the tile provider’s usage policy for production traffic.

1. Prepare map data

[
  {"ip":"8.8.8.8","city":"Mountain View","region":"California","country":"US","lat":37.4056,"lon":-122.0775},
  {"ip":"1.1.1.1","city":"Brisbane","region":"Queensland","country":"AU","lat":-27.4679,"lon":153.0281}
]

Normalize the provider’s loc field (often "latitude,longitude") into numeric lat and lon values before sending it to the browser.

2. Render markers with Leaflet

<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <link rel="stylesheet" href="https://unpkg.com/leaflet@1.9.4/dist/leaflet.css">
  <style>#map { height: 70vh; }</style>
</head>
<body>
  <h1>Approximate IP locations</h1>
  <div id="map" aria-label="Map of approximate IP locations"></div>
  <script src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js"></script>
  <script>
    const points = [
      {ip:'8.8.8.8', city:'Mountain View', region:'California', country:'US', lat:37.4056, lon:-122.0775},
      {ip:'1.1.1.1', city:'Brisbane', region:'Queensland', country:'AU', lat:-27.4679, lon:153.0281}
    ];

    const map = L.map('map');
    L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
      maxZoom: 19,
      attribution: '&copy; OpenStreetMap contributors'
    }).addTo(map);

    const bounds = [];
    for (const point of points) {
      if (!Number.isFinite(point.lat) || !Number.isFinite(point.lon)) continue;
      const marker = L.marker([point.lat, point.lon]).addTo(map);
      marker.bindPopup(
        `<strong>Approximate IP location</strong><br>` +
        `${point.ip}<br>${point.city ?? ''}, ${point.region ?? ''}, ${point.country ?? ''}`
      );
      bounds.push([point.lat, point.lon]);
    }

    if (bounds.length === 1) map.setView(bounds[0], 8);
    else if (bounds.length > 1) map.fitBounds(bounds, { padding: [24, 24] });
    else map.setView([20, 0], 2);
  </script>
</body>
</html>

Map multiple IP addresses

For a small list, run lookups concurrently with a bounded worker pool, then render the successful records. For ranges or ASNs, use a documented bulk workflow rather than expanding an entire network blindly. IPinfo’s CLI repository documents a map workflow that accepts IP addresses, ranges/netblocks and ASNs and states support for up to 500,000 IP addresses; that is a vendor-stated capacity, not an independent benchmark. See the IPinfo CLI repository.

Safe batching pattern in Python

from concurrent.futures import ThreadPoolExecutor, as_completed
import os, requests

ips = ["8.8.8.8", "1.1.1.1", "2606:4700:4700::1111"]
headers = {"Accept": "application/json"}
if os.getenv("IPINFO_TOKEN"):
    headers["Authorization"] = f"Bearer {os.environ['IPINFO_TOKEN']}"

def lookup(ip):
    r = requests.get(f"https://ipinfo.io/{ip}/json", headers=headers, timeout=15)
    r.raise_for_status()
    item = r.json()
    try:
        lat, lon = (float(value) for value in item["loc"].split(",", 1))
    except (KeyError, ValueError):
        return {"ip": ip, "error": "No usable coordinates"}
    return {"ip": ip, "city": item.get("city"), "region": item.get("region"),
            "country": item.get("country"), "lat": lat, "lon": lon}

results = []
with ThreadPoolExecutor(max_workers=5) as pool:
    futures = {pool.submit(lookup, ip): ip for ip in ips}
    for future in as_completed(futures):
        try:
            results.append(future.result())
        except requests.RequestException as exc:
            results.append({"ip": futures[future], "error": str(exc)})

print(results)

Accuracy, privacy and interpretation

  • Do not infer a person: a household, office, carrier gateway or VPN can serve many users.
  • Expect provider disagreement: databases update at different times and may select different network points.
  • Check network context: cloud, hosting, mobile and VPN organizations often explain surprising cities.
  • Preserve uncertainty: display the provider’s city or region and avoid silently converting it into a street address.
  • Review retention: privacy practices differ. The reviewed ipinfo.app policy says its browser map sends coordinates to OpenStreetMap tile servers and describes request logs and short-lived caches; that policy does not apply to every map service. Read the cited policy before submitting sensitive data.

Choosing a data and map workflow

Question What to check
How much detail? Country only, or region, city, coordinates and postal code?
How many IPs? Single lookups, a list, ranges/netblocks or ASNs?
What plan? IPinfo says its free Lite plan provides unlimited authenticated country-level geolocation and basic ASN requests; city-level data and other datasets depend on the plan.
What privacy controls? Read the selected lookup provider’s logging, caching and tile-server disclosures.
What output? Interactive browser map, downloadable GeoJSON, a map URL or an internal dashboard?

Or skip the browser setup

If you need a clean image of the map or a report page, ScreenshotNeo can capture a URL with one request. It accepts cookie and consent banners before capture and removes more than 60 known consent platforms, newsletter popups and chat widgets. Bot checks, blank pages, timeouts, failed loads and cache hits are not billed, and the response identifies the result with X-Page-Verdict and X-Billed headers. Its MCP server also gives Claude, Cursor and other MCP clients take_screenshot, get_page_info and capture_pdf tools.

See the ScreenshotNeo API documentation for options such as full-page capture, custom CSS and JavaScript, waiting for a selector or network idle, dark mode, device presets, PDF output and signed links.

curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://your-domain.example/ip-map -o map.webp
import requests
r = requests.get("https://api.screenshotneo.com/v1/shot", params={"access_key": "YOUR_API_KEY", "url": "https://your-domain.example/ip-map"}, timeout=90)
open("map.webp", "wb").write(r.content)
const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://your-domain.example/ip-map' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);

The free plan includes 1,000 screenshots each month with no card. Paid plans start at $5 for 3,000 shots, and every feature is available on every plan. Create a free ScreenshotNeo account.

Troubleshooting

The map is blank

Check that the map container has a height, Leaflet CSS and JavaScript both loaded, and the browser can reach the tile host. A zero-height container produces no visible map even when markers are valid.

No marker appears

Log the parsed latitude and longitude. Reject NaN, missing values and swapped coordinates. Latitude must be between -90 and 90; longitude must be between -180 and 180.

The result points to the wrong city

Check whether the address belongs to a VPN, mobile carrier, cloud provider or shared gateway. Compare the provider’s organization and ASN fields, then treat the city as an estimate.

IPv6 lookups fail

Confirm that the URL encodes the IPv6 address correctly and that your HTTP client has IPv6 connectivity. Preserve the address as a string; do not convert it to a numeric type.

Requests return 401, 403 or 429

Verify authentication, plan access and rate limits. Add bounded retries with exponential backoff for transient 429 responses, and never retry invalid credentials indefinitely.

Markers overlap

Nearby points can represent different IPs or many addresses geolocated to one city. Use marker clustering or a list view, and show the IP and approximate label in the popup.

A ScreenshotNeo capture is incomplete

Wait for the map container or a marker selector, use a delay or network-idle wait when tiles load slowly, and enable full-page capture for a long results page. Inspect X-Page-Verdict and X-Billed when diagnosing a response.

Performance, reliability and cost notes

  • Cache repeated lookups according to the provider’s terms; IP-to-location data changes over time, so choose a refresh interval that matches your use case.
  • Use bounded concurrency instead of firing thousands of requests at once.
  • Store raw responses with a timestamp and provider name so a later map can be explained or regenerated.
  • Separate lookup failures from records that legitimately have no coordinates.
  • For a large map, cluster markers and avoid loading every point into the DOM at once.
  • Budget for both geolocation requests and map-tile usage. A free country-level plan may not include city-level fields.

FAQ

Can an IP map locate a phone?

No. It shows an estimated area associated with the public IP, not the phone’s GPS position or the person’s location.

Can I map private IP addresses?

No. Private addresses are reused inside local networks and have no unique public geographic location. Map the public egress address instead.

Is a city-level pin accurate to a neighborhood?

Not necessarily. City-level output can represent a broad service area or network facility. Do not interpret it as a verified neighborhood or address.

What should I display to users?

Show the provider, lookup time, returned granularity and an “approximate IP location” label. Avoid presenting the marker as a person’s position.

Can I export the map?

Yes. Save the normalized records as JSON or GeoJSON, or capture the rendered page as an image or PDF. Check the terms for any map tiles and geolocation data you redistribute.