{
  "title": "Playbook: Find the Geolocation of an External IP Address",
  "subtitle": null,
  "description": "Determine the geographic location of an external IP address using multiple sources, and validate that the results are consistent.",
  "lane": "playbooks",
  "slug": "geoip-lookup",
  "url": "https://ajvanbeest.com/playbooks/geoip-lookup/",
  "maturity": "evergreen",
  "date": "2026-01-01",
  "updated": null,
  "tags": [
    "security",
    "playbooks",
    "geoip",
    "osint",
    "investigation"
  ],
  "wordCount": 974,
  "readingMinutes": 5,
  "markdown": "## Purpose\n\nDetermine the geographic location of an external IP address using multiple sources, and validate that the results are consistent.\n\n\n## Required Tools & Access\n\n| Tool | Access Needed | Free Tier? | Notes |\n|------|---------------|------------|-------|\n| [IPinfo.io](https://ipinfo.io) | API token or web interface | Yes (50k/month) | Privacy detection requires paid plan |\n| [ip-api.com](http://ip-api.com) | No auth for non-commercial | Yes | Includes proxy/hosting flags |\n| [VirusTotal](https://virustotal.com) | API key or web interface | Yes (limited) | |\n| [AbuseIPDB](https://abuseipdb.com) | API key or web interface | Yes (1k/day) | Tor/proxy detection |\n| [Tor Exit Node List](https://check.torproject.org/torbulkexitlist) | None | Yes | Official Tor Project list |\n\nYou need at least two geolocation sources to cross-validate results.\n\n\n## Input\n\n- One external IPv4 or IPv6 address (e.g., `8.8.8.8`)\n\n\n## Steps\n\n### 1. Query IPinfo.io\n\n**Web interface:**\n1. Go to https://ipinfo.io\n2. Enter the IP address in the search box\n3. Record: City, Region, Country, Organization, ASN\n\n**API:**\n```bash\ncurl \"https://ipinfo.io/{IP_ADDRESS}?token={YOUR_TOKEN}\"\n```\n\nRecord the following fields:\n- `city`\n- `region`\n- `country`\n- `org`\n- `asn`\n\n\n### 2. Query ip-api.com\n\n**Web interface:**\n1. Go to http://ip-api.com\n2. The IP appears in the URL; modify to: `http://ip-api.com/json/{IP_ADDRESS}`\n3. Record: City, Region, Country, ISP, AS\n\n**API:**\n```bash\ncurl \"http://ip-api.com/json/{IP_ADDRESS}\"\n```\n\nRecord the following fields:\n- `city`\n- `regionName`\n- `country`\n- `isp`\n- `as`\n\n\n### 3. Query VirusTotal (optional but recommended)\n\n**Web interface:**\n1. Go to https://virustotal.com\n2. Click \"Search\" and enter the IP address\n3. Go to the \"Details\" tab\n4. Record: Country, ASN, Network\n\n**API:**\n```bash\ncurl --header \"x-apikey: {YOUR_API_KEY}\" \\\n  \"https://www.virustotal.com/api/v3/ip_addresses/{IP_ADDRESS}\"\n```\n\nRecord from `data.attributes`:\n- `country`\n- `asn`\n- `as_owner`\n\n\n### 4. Check for VPN/Proxy/Tor\n\nThis step determines whether the IP is masking its true origin.\n\n#### 4a. Check ip-api.com flags\n\nIf you queried ip-api.com in Step 2, check these additional fields in the response:\n- `proxy`: true/false (detects proxy/VPN)\n- `hosting`: true/false (datacenter/hosting provider, often indicates VPN)\n\n**API (with extra fields):**\n```bash\ncurl \"http://ip-api.com/json/{IP_ADDRESS}?fields=status,country,city,isp,as,proxy,hosting\"\n```\n\n#### 4b. Check AbuseIPDB\n\n**Web interface:**\n1. Go to https://abuseipdb.com\n2. Enter the IP address\n3. Look for: Usage Type, ISP, and \"Is Tor\" flag\n\n**API:**\n```bash\ncurl -G \"https://api.abuseipdb.com/api/v2/check\" \\\n  --data-urlencode \"ipAddress={IP_ADDRESS}\" \\\n  -H \"Key: {YOUR_API_KEY}\" \\\n  -H \"Accept: application/json\"\n```\n\nRecord from response:\n- `data.usageType` (e.g., \"Data Center/Web Hosting/Transit\")\n- `data.isTor` (true/false)\n- `data.totalReports` (abuse report count)\n\n#### 4c. Check Tor Exit Node List\n\n**Manual check:**\n1. Download the list: https://check.torproject.org/torbulkexitlist\n2. Search for the IP address in the file\n3. If found, the IP is a known Tor exit node\n\n**Command line:**\n```bash\ncurl -s \"https://check.torproject.org/torbulkexitlist\" | grep -q \"{IP_ADDRESS}\" && echo \"TOR EXIT NODE\" || echo \"Not a Tor exit\"\n```\n\n#### Summary: Privacy/Anonymization Flags\n\n| Check | Result |\n|-------|--------|\n| ip-api proxy flag | |\n| ip-api hosting flag | |\n| AbuseIPDB usageType | |\n| AbuseIPDB isTor | |\n| Tor exit node list | |\n\n**Interpretation:**\n- **Tor exit node**: Location data is meaningless; this is the exit point, not the user\n- **Proxy/VPN detected**: Location may be the VPN endpoint, not the user\n- **Hosting/Datacenter**: Likely a server, VPN endpoint, or automated traffic—not a typical end user\n\n\n## Validation\n\nCompare the results from each source:\n\n| Field | IPinfo.io | ip-api.com | VirusTotal |\n|-------|-----------|------------|------------|\n| Country | | | |\n| City/Region | | | |\n| ASN/Org | | | |\n\n**Validation checks:**\n\n1. **Country match**: Do all sources agree on the country?\n   - If YES: High confidence in country-level location\n   - If NO: Flag discrepancy; IP may use anycast or be misattributed\n\n2. **City/Region match**: Do at least 2 sources agree on city or region?\n   - If YES: Reasonable confidence in city-level location\n   - If NO: Report country only; city-level data is unreliable for this IP\n\n3. **ASN/Org match**: Do the ASN numbers match?\n   - If YES: Confirms the network owner\n   - If NO: Investigate further; one source may have stale data\n\n**Known limitations:**\n- VPNs, proxies, and Tor exit nodes will show the endpoint location, not the user (Step 4 helps detect these)\n- CDN and anycast IPs (e.g., Cloudflare, Google) may return multiple valid locations\n- Mobile IPs may geolocate to carrier headquarters, not actual user location\n- Privacy detection is not perfect; some VPNs use residential IPs that evade detection\n\n\n## Output\n\nProduce a summary in this format:\n\n```\nIP Address: {IP}\nLookup Date: {YYYY-MM-DD}\n\nLocation:\n  Country: {country} (confidence: high/medium/low)\n  City/Region: {city, region} (confidence: high/medium/low)\n\nNetwork:\n  ASN: {asn}\n  Organization: {org name}\n\nPrivacy/Anonymization:\n  Tor Exit Node: {yes/no}\n  Proxy/VPN Detected: {yes/no/unknown}\n  Hosting/Datacenter: {yes/no}\n  ⚠️ Location Reliability: {reliable / unreliable - anonymized}\n\nValidation:\n  Sources queried: {list sources}\n  Country consensus: {yes/no}\n  City consensus: {yes/no}\n\nNotes:\n  {any discrepancies or flags}\n```\n\n**Important:** If any privacy/anonymization flag is positive, add a warning that the geolocation data represents the proxy/VPN/Tor endpoint, NOT the actual user location.\n\n\n## Logging\n\nRecord the following in your investigation log or ticket:\n\n1. The IP address queried\n2. Date/time of lookup\n3. Sources consulted\n4. **Location results**: Country, City/Region, confidence level\n5. **Network info**: ASN, Organization\n6. **Privacy flags**: Tor, Proxy/VPN, Hosting/Datacenter\n7. Location reliability assessment\n8. Any discrepancies noted\n9. Analyst name or \"automated\" if run by script\n\n**Example log entry:**\n```\n[2026-01-01 14:30] GeoIP lookup for 203.0.113.42\nSources: IPinfo.io, ip-api.com, VirusTotal, AbuseIPDB, Tor list\nResult: Netherlands, Amsterdam (high confidence)\nASN: AS12345 - Example Hosting Inc.\nPrivacy: Not Tor, no proxy detected, hosting=yes (datacenter IP)\nNotes: All geo sources agree. Datacenter IP suggests server or VPN endpoint.\nAnalyst: AJ\n```\n\n\n## Automation Notes\n\nThis playbook is a good candidate for scripting because:\n- All steps are deterministic\n- APIs return structured data\n- Validation logic is straightforward\n\nA script should:\n1. Accept IP as input\n2. Query all configured sources\n3. Compare results automatically\n4. Output the summary format above\n5. Flag discrepancies for human review",
  "html": "<h2 id=\"purpose\">Purpose</h2>\n<p>Determine the geographic location of an external IP address using multiple sources, and validate that the results are consistent.</p>\n<h2 id=\"required-tools--access\">Required Tools &amp; Access</h2>\n<table>\n<thead>\n<tr>\n<th>Tool</th>\n<th>Access Needed</th>\n<th>Free Tier?</th>\n<th>Notes</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td><a href=\"https://ipinfo.io\">IPinfo.io</a></td>\n<td>API token or web interface</td>\n<td>Yes (50k/month)</td>\n<td>Privacy detection requires paid plan</td>\n</tr>\n<tr>\n<td><a href=\"http://ip-api.com\">ip-api.com</a></td>\n<td>No auth for non-commercial</td>\n<td>Yes</td>\n<td>Includes proxy/hosting flags</td>\n</tr>\n<tr>\n<td><a href=\"https://virustotal.com\">VirusTotal</a></td>\n<td>API key or web interface</td>\n<td>Yes (limited)</td>\n<td></td>\n</tr>\n<tr>\n<td><a href=\"https://abuseipdb.com\">AbuseIPDB</a></td>\n<td>API key or web interface</td>\n<td>Yes (1k/day)</td>\n<td>Tor/proxy detection</td>\n</tr>\n<tr>\n<td><a href=\"https://check.torproject.org/torbulkexitlist\">Tor Exit Node List</a></td>\n<td>None</td>\n<td>Yes</td>\n<td>Official Tor Project list</td>\n</tr>\n</tbody>\n</table>\n<p>You need at least two geolocation sources to cross-validate results.</p>\n<h2 id=\"input\">Input</h2>\n<ul>\n<li>One external IPv4 or IPv6 address (e.g., <code>8.8.8.8</code>)</li>\n</ul>\n<h2 id=\"steps\">Steps</h2>\n<h3 id=\"1-query-ipinfoio\">1. Query IPinfo.io</h3>\n<p><strong>Web interface:</strong></p>\n<ol>\n<li>Go to <a href=\"https://ipinfo.io\">https://ipinfo.io</a></li>\n<li>Enter the IP address in the search box</li>\n<li>Record: City, Region, Country, Organization, ASN</li>\n</ol>\n<p><strong>API:</strong></p>\n<pre class=\"astro-code astro-code-themes gruvbox-light-medium gruvbox-dark-medium\" style=\"--shiki-light:#3c3836;--shiki-dark:#ebdbb2;--shiki-light-bg:#fbf1c7;--shiki-dark-bg:#282828; overflow-x: auto;\" tabindex=\"0\" data-language=\"bash\"><code><span class=\"line\"><span style=\"--shiki-light:#B57614;--shiki-dark:#FABD2F\">curl</span><span style=\"--shiki-light:#7C6F64;--shiki-dark:#A89984\"> \"</span><span style=\"--shiki-light:#79740E;--shiki-dark:#B8BB26\">https://ipinfo.io/{IP_ADDRESS}?token={YOUR_TOKEN}</span><span style=\"--shiki-light:#7C6F64;--shiki-dark:#A89984\">\"</span></span></code></pre>\n<p>Record the following fields:</p>\n<ul>\n<li><code>city</code></li>\n<li><code>region</code></li>\n<li><code>country</code></li>\n<li><code>org</code></li>\n<li><code>asn</code></li>\n</ul>\n<h3 id=\"2-query-ip-apicom\">2. Query ip-api.com</h3>\n<p><strong>Web interface:</strong></p>\n<ol>\n<li>Go to <a href=\"http://ip-api.com\">http://ip-api.com</a></li>\n<li>The IP appears in the URL; modify to: <code>http://ip-api.com/json/{IP_ADDRESS}</code></li>\n<li>Record: City, Region, Country, ISP, AS</li>\n</ol>\n<p><strong>API:</strong></p>\n<pre class=\"astro-code astro-code-themes gruvbox-light-medium gruvbox-dark-medium\" style=\"--shiki-light:#3c3836;--shiki-dark:#ebdbb2;--shiki-light-bg:#fbf1c7;--shiki-dark-bg:#282828; overflow-x: auto;\" tabindex=\"0\" data-language=\"bash\"><code><span class=\"line\"><span style=\"--shiki-light:#B57614;--shiki-dark:#FABD2F\">curl</span><span style=\"--shiki-light:#7C6F64;--shiki-dark:#A89984\"> \"</span><span style=\"--shiki-light:#79740E;--shiki-dark:#B8BB26\">http://ip-api.com/json/{IP_ADDRESS}</span><span style=\"--shiki-light:#7C6F64;--shiki-dark:#A89984\">\"</span></span></code></pre>\n<p>Record the following fields:</p>\n<ul>\n<li><code>city</code></li>\n<li><code>regionName</code></li>\n<li><code>country</code></li>\n<li><code>isp</code></li>\n<li><code>as</code></li>\n</ul>\n<h3 id=\"3-query-virustotal-optional-but-recommended\">3. Query VirusTotal (optional but recommended)</h3>\n<p><strong>Web interface:</strong></p>\n<ol>\n<li>Go to <a href=\"https://virustotal.com\">https://virustotal.com</a></li>\n<li>Click “Search” and enter the IP address</li>\n<li>Go to the “Details” tab</li>\n<li>Record: Country, ASN, Network</li>\n</ol>\n<p><strong>API:</strong></p>\n<pre class=\"astro-code astro-code-themes gruvbox-light-medium gruvbox-dark-medium\" style=\"--shiki-light:#3c3836;--shiki-dark:#ebdbb2;--shiki-light-bg:#fbf1c7;--shiki-dark-bg:#282828; overflow-x: auto;\" tabindex=\"0\" data-language=\"bash\"><code><span class=\"line\"><span style=\"--shiki-light:#B57614;--shiki-dark:#FABD2F\">curl</span><span style=\"--shiki-light:#8F3F71;--shiki-dark:#D3869B\"> --header</span><span style=\"--shiki-light:#7C6F64;--shiki-dark:#A89984\"> \"</span><span style=\"--shiki-light:#79740E;--shiki-dark:#B8BB26\">x-apikey: {YOUR_API_KEY}</span><span style=\"--shiki-light:#7C6F64;--shiki-dark:#A89984\">\"</span><span style=\"--shiki-light:#8F3F71;--shiki-dark:#D3869B\"> \\</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#7C6F64;--shiki-dark:#A89984\">  \"</span><span style=\"--shiki-light:#79740E;--shiki-dark:#B8BB26\">https://www.virustotal.com/api/v3/ip_addresses/{IP_ADDRESS}</span><span style=\"--shiki-light:#7C6F64;--shiki-dark:#A89984\">\"</span></span></code></pre>\n<p>Record from <code>data.attributes</code>:</p>\n<ul>\n<li><code>country</code></li>\n<li><code>asn</code></li>\n<li><code>as_owner</code></li>\n</ul>\n<h3 id=\"4-check-for-vpnproxytor\">4. Check for VPN/Proxy/Tor</h3>\n<p>This step determines whether the IP is masking its true origin.</p>\n<h4 id=\"4a-check-ip-apicom-flags\">4a. Check ip-api.com flags</h4>\n<p>If you queried ip-api.com in Step 2, check these additional fields in the response:</p>\n<ul>\n<li><code>proxy</code>: true/false (detects proxy/VPN)</li>\n<li><code>hosting</code>: true/false (datacenter/hosting provider, often indicates VPN)</li>\n</ul>\n<p><strong>API (with extra fields):</strong></p>\n<pre class=\"astro-code astro-code-themes gruvbox-light-medium gruvbox-dark-medium\" style=\"--shiki-light:#3c3836;--shiki-dark:#ebdbb2;--shiki-light-bg:#fbf1c7;--shiki-dark-bg:#282828; overflow-x: auto;\" tabindex=\"0\" data-language=\"bash\"><code><span class=\"line\"><span style=\"--shiki-light:#B57614;--shiki-dark:#FABD2F\">curl</span><span style=\"--shiki-light:#7C6F64;--shiki-dark:#A89984\"> \"</span><span style=\"--shiki-light:#79740E;--shiki-dark:#B8BB26\">http://ip-api.com/json/{IP_ADDRESS}?fields=status,country,city,isp,as,proxy,hosting</span><span style=\"--shiki-light:#7C6F64;--shiki-dark:#A89984\">\"</span></span></code></pre>\n<h4 id=\"4b-check-abuseipdb\">4b. Check AbuseIPDB</h4>\n<p><strong>Web interface:</strong></p>\n<ol>\n<li>Go to <a href=\"https://abuseipdb.com\">https://abuseipdb.com</a></li>\n<li>Enter the IP address</li>\n<li>Look for: Usage Type, ISP, and “Is Tor” flag</li>\n</ol>\n<p><strong>API:</strong></p>\n<pre class=\"astro-code astro-code-themes gruvbox-light-medium gruvbox-dark-medium\" style=\"--shiki-light:#3c3836;--shiki-dark:#ebdbb2;--shiki-light-bg:#fbf1c7;--shiki-dark-bg:#282828; overflow-x: auto;\" tabindex=\"0\" data-language=\"bash\"><code><span class=\"line\"><span style=\"--shiki-light:#B57614;--shiki-dark:#FABD2F\">curl</span><span style=\"--shiki-light:#8F3F71;--shiki-dark:#D3869B\"> -G</span><span style=\"--shiki-light:#7C6F64;--shiki-dark:#A89984\"> \"</span><span style=\"--shiki-light:#79740E;--shiki-dark:#B8BB26\">https://api.abuseipdb.com/api/v2/check</span><span style=\"--shiki-light:#7C6F64;--shiki-dark:#A89984\">\"</span><span style=\"--shiki-light:#8F3F71;--shiki-dark:#D3869B\"> \\</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#8F3F71;--shiki-dark:#D3869B\">  --data-urlencode</span><span style=\"--shiki-light:#7C6F64;--shiki-dark:#A89984\"> \"</span><span style=\"--shiki-light:#79740E;--shiki-dark:#B8BB26\">ipAddress={IP_ADDRESS}</span><span style=\"--shiki-light:#7C6F64;--shiki-dark:#A89984\">\"</span><span style=\"--shiki-light:#8F3F71;--shiki-dark:#D3869B\"> \\</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#8F3F71;--shiki-dark:#D3869B\">  -H</span><span style=\"--shiki-light:#7C6F64;--shiki-dark:#A89984\"> \"</span><span style=\"--shiki-light:#79740E;--shiki-dark:#B8BB26\">Key: {YOUR_API_KEY}</span><span style=\"--shiki-light:#7C6F64;--shiki-dark:#A89984\">\"</span><span style=\"--shiki-light:#8F3F71;--shiki-dark:#D3869B\"> \\</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#8F3F71;--shiki-dark:#D3869B\">  -H</span><span style=\"--shiki-light:#7C6F64;--shiki-dark:#A89984\"> \"</span><span style=\"--shiki-light:#79740E;--shiki-dark:#B8BB26\">Accept: application/json</span><span style=\"--shiki-light:#7C6F64;--shiki-dark:#A89984\">\"</span></span></code></pre>\n<p>Record from response:</p>\n<ul>\n<li><code>data.usageType</code> (e.g., “Data Center/Web Hosting/Transit”)</li>\n<li><code>data.isTor</code> (true/false)</li>\n<li><code>data.totalReports</code> (abuse report count)</li>\n</ul>\n<h4 id=\"4c-check-tor-exit-node-list\">4c. Check Tor Exit Node List</h4>\n<p><strong>Manual check:</strong></p>\n<ol>\n<li>Download the list: <a href=\"https://check.torproject.org/torbulkexitlist\">https://check.torproject.org/torbulkexitlist</a></li>\n<li>Search for the IP address in the file</li>\n<li>If found, the IP is a known Tor exit node</li>\n</ol>\n<p><strong>Command line:</strong></p>\n<pre class=\"astro-code astro-code-themes gruvbox-light-medium gruvbox-dark-medium\" style=\"--shiki-light:#3c3836;--shiki-dark:#ebdbb2;--shiki-light-bg:#fbf1c7;--shiki-dark-bg:#282828; overflow-x: auto;\" tabindex=\"0\" data-language=\"bash\"><code><span class=\"line\"><span style=\"--shiki-light:#B57614;--shiki-dark:#FABD2F\">curl</span><span style=\"--shiki-light:#8F3F71;--shiki-dark:#D3869B\"> -s</span><span style=\"--shiki-light:#7C6F64;--shiki-dark:#A89984\"> \"</span><span style=\"--shiki-light:#79740E;--shiki-dark:#B8BB26\">https://check.torproject.org/torbulkexitlist</span><span style=\"--shiki-light:#7C6F64;--shiki-dark:#A89984\">\"</span><span style=\"--shiki-light:#427B58;--shiki-dark:#8EC07C\"> |</span><span style=\"--shiki-light:#B57614;--shiki-dark:#FABD2F\"> grep</span><span style=\"--shiki-light:#8F3F71;--shiki-dark:#D3869B\"> -q</span><span style=\"--shiki-light:#7C6F64;--shiki-dark:#A89984\"> \"</span><span style=\"--shiki-light:#79740E;--shiki-dark:#B8BB26\">{IP_ADDRESS}</span><span style=\"--shiki-light:#7C6F64;--shiki-dark:#A89984\">\"</span><span style=\"--shiki-light:#7C6F64;--shiki-dark:#A89984\"> &amp;&amp;</span><span style=\"--shiki-light:#AF3A03;--shiki-dark:#FE8019\"> echo</span><span style=\"--shiki-light:#7C6F64;--shiki-dark:#A89984\"> \"</span><span style=\"--shiki-light:#79740E;--shiki-dark:#B8BB26\">TOR EXIT NODE</span><span style=\"--shiki-light:#7C6F64;--shiki-dark:#A89984\">\"</span><span style=\"--shiki-light:#427B58;--shiki-dark:#8EC07C\"> ||</span><span style=\"--shiki-light:#AF3A03;--shiki-dark:#FE8019\"> echo</span><span style=\"--shiki-light:#7C6F64;--shiki-dark:#A89984\"> \"</span><span style=\"--shiki-light:#79740E;--shiki-dark:#B8BB26\">Not a Tor exit</span><span style=\"--shiki-light:#7C6F64;--shiki-dark:#A89984\">\"</span></span></code></pre>\n<h4 id=\"summary-privacyanonymization-flags\">Summary: Privacy/Anonymization Flags</h4>\n<table>\n<thead>\n<tr>\n<th>Check</th>\n<th>Result</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>ip-api proxy flag</td>\n<td></td>\n</tr>\n<tr>\n<td>ip-api hosting flag</td>\n<td></td>\n</tr>\n<tr>\n<td>AbuseIPDB usageType</td>\n<td></td>\n</tr>\n<tr>\n<td>AbuseIPDB isTor</td>\n<td></td>\n</tr>\n<tr>\n<td>Tor exit node list</td>\n<td></td>\n</tr>\n</tbody>\n</table>\n<p><strong>Interpretation:</strong></p>\n<ul>\n<li><strong>Tor exit node</strong>: Location data is meaningless; this is the exit point, not the user</li>\n<li><strong>Proxy/VPN detected</strong>: Location may be the VPN endpoint, not the user</li>\n<li><strong>Hosting/Datacenter</strong>: Likely a server, VPN endpoint, or automated traffic—not a typical end user</li>\n</ul>\n<h2 id=\"validation\">Validation</h2>\n<p>Compare the results from each source:</p>\n<table>\n<thead>\n<tr>\n<th>Field</th>\n<th>IPinfo.io</th>\n<th>ip-api.com</th>\n<th>VirusTotal</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>Country</td>\n<td></td>\n<td></td>\n<td></td>\n</tr>\n<tr>\n<td>City/Region</td>\n<td></td>\n<td></td>\n<td></td>\n</tr>\n<tr>\n<td>ASN/Org</td>\n<td></td>\n<td></td>\n<td></td>\n</tr>\n</tbody>\n</table>\n<p><strong>Validation checks:</strong></p>\n<ol>\n<li>\n<p><strong>Country match</strong>: Do all sources agree on the country?</p>\n<ul>\n<li>If YES: High confidence in country-level location</li>\n<li>If NO: Flag discrepancy; IP may use anycast or be misattributed</li>\n</ul>\n</li>\n<li>\n<p><strong>City/Region match</strong>: Do at least 2 sources agree on city or region?</p>\n<ul>\n<li>If YES: Reasonable confidence in city-level location</li>\n<li>If NO: Report country only; city-level data is unreliable for this IP</li>\n</ul>\n</li>\n<li>\n<p><strong>ASN/Org match</strong>: Do the ASN numbers match?</p>\n<ul>\n<li>If YES: Confirms the network owner</li>\n<li>If NO: Investigate further; one source may have stale data</li>\n</ul>\n</li>\n</ol>\n<p><strong>Known limitations:</strong></p>\n<ul>\n<li>VPNs, proxies, and Tor exit nodes will show the endpoint location, not the user (Step 4 helps detect these)</li>\n<li>CDN and anycast IPs (e.g., Cloudflare, Google) may return multiple valid locations</li>\n<li>Mobile IPs may geolocate to carrier headquarters, not actual user location</li>\n<li>Privacy detection is not perfect; some VPNs use residential IPs that evade detection</li>\n</ul>\n<h2 id=\"output\">Output</h2>\n<p>Produce a summary in this format:</p>\n<pre class=\"astro-code astro-code-themes gruvbox-light-medium gruvbox-dark-medium\" style=\"--shiki-light:#3c3836;--shiki-dark:#ebdbb2;--shiki-light-bg:#fbf1c7;--shiki-dark-bg:#282828; overflow-x: auto;\" tabindex=\"0\" data-language=\"plaintext\"><code><span class=\"line\"><span>IP Address: {IP}</span></span>\n<span class=\"line\"><span>Lookup Date: {YYYY-MM-DD}</span></span>\n<span class=\"line\"><span></span></span>\n<span class=\"line\"><span>Location:</span></span>\n<span class=\"line\"><span>  Country: {country} (confidence: high/medium/low)</span></span>\n<span class=\"line\"><span>  City/Region: {city, region} (confidence: high/medium/low)</span></span>\n<span class=\"line\"><span></span></span>\n<span class=\"line\"><span>Network:</span></span>\n<span class=\"line\"><span>  ASN: {asn}</span></span>\n<span class=\"line\"><span>  Organization: {org name}</span></span>\n<span class=\"line\"><span></span></span>\n<span class=\"line\"><span>Privacy/Anonymization:</span></span>\n<span class=\"line\"><span>  Tor Exit Node: {yes/no}</span></span>\n<span class=\"line\"><span>  Proxy/VPN Detected: {yes/no/unknown}</span></span>\n<span class=\"line\"><span>  Hosting/Datacenter: {yes/no}</span></span>\n<span class=\"line\"><span>  ⚠️ Location Reliability: {reliable / unreliable - anonymized}</span></span>\n<span class=\"line\"><span></span></span>\n<span class=\"line\"><span>Validation:</span></span>\n<span class=\"line\"><span>  Sources queried: {list sources}</span></span>\n<span class=\"line\"><span>  Country consensus: {yes/no}</span></span>\n<span class=\"line\"><span>  City consensus: {yes/no}</span></span>\n<span class=\"line\"><span></span></span>\n<span class=\"line\"><span>Notes:</span></span>\n<span class=\"line\"><span>  {any discrepancies or flags}</span></span></code></pre>\n<p><strong>Important:</strong> If any privacy/anonymization flag is positive, add a warning that the geolocation data represents the proxy/VPN/Tor endpoint, NOT the actual user location.</p>\n<h2 id=\"logging\">Logging</h2>\n<p>Record the following in your investigation log or ticket:</p>\n<ol>\n<li>The IP address queried</li>\n<li>Date/time of lookup</li>\n<li>Sources consulted</li>\n<li><strong>Location results</strong>: Country, City/Region, confidence level</li>\n<li><strong>Network info</strong>: ASN, Organization</li>\n<li><strong>Privacy flags</strong>: Tor, Proxy/VPN, Hosting/Datacenter</li>\n<li>Location reliability assessment</li>\n<li>Any discrepancies noted</li>\n<li>Analyst name or “automated” if run by script</li>\n</ol>\n<p><strong>Example log entry:</strong></p>\n<pre class=\"astro-code astro-code-themes gruvbox-light-medium gruvbox-dark-medium\" style=\"--shiki-light:#3c3836;--shiki-dark:#ebdbb2;--shiki-light-bg:#fbf1c7;--shiki-dark-bg:#282828; overflow-x: auto;\" tabindex=\"0\" data-language=\"plaintext\"><code><span class=\"line\"><span>[2026-01-01 14:30] GeoIP lookup for 203.0.113.42</span></span>\n<span class=\"line\"><span>Sources: IPinfo.io, ip-api.com, VirusTotal, AbuseIPDB, Tor list</span></span>\n<span class=\"line\"><span>Result: Netherlands, Amsterdam (high confidence)</span></span>\n<span class=\"line\"><span>ASN: AS12345 - Example Hosting Inc.</span></span>\n<span class=\"line\"><span>Privacy: Not Tor, no proxy detected, hosting=yes (datacenter IP)</span></span>\n<span class=\"line\"><span>Notes: All geo sources agree. Datacenter IP suggests server or VPN endpoint.</span></span>\n<span class=\"line\"><span>Analyst: AJ</span></span></code></pre>\n<h2 id=\"automation-notes\">Automation Notes</h2>\n<p>This playbook is a good candidate for scripting because:</p>\n<ul>\n<li>All steps are deterministic</li>\n<li>APIs return structured data</li>\n<li>Validation logic is straightforward</li>\n</ul>\n<p>A script should:</p>\n<ol>\n<li>Accept IP as input</li>\n<li>Query all configured sources</li>\n<li>Compare results automatically</li>\n<li>Output the summary format above</li>\n<li>Flag discrepancies for human review</li>\n</ol>",
  "links": [
    "https://ipinfo.io",
    "http://ip-api.com",
    "https://virustotal.com",
    "https://abuseipdb.com",
    "https://check.torproject.org/torbulkexitlist"
  ],
  "alternates": {
    "html": "https://ajvanbeest.com/playbooks/geoip-lookup/",
    "markdown": "https://ajvanbeest.com/playbooks/geoip-lookup.md",
    "json": "https://ajvanbeest.com/playbooks/geoip-lookup.json"
  }
}
