Back to Articles

ipcat: The IP Address Categorization Dataset That Democratized Datacenter Detection

[ View on GitHub ]

ipcat: The IP Address Categorization Dataset That Democratized Datacenter Detection

Hook

Every day, your application serves requests from thousands of IPs, but can you tell which ones are coming from actual users versus bots, proxies, and datacenter infrastructure? This distinction is worth millions in fraud prevention.

Context

Before ipcat emerged, identifying whether an IP address belonged to a datacenter, hosting provider, or residential user required expensive commercial databases or rolling your own detection system through painful manual research. Security teams needed to distinguish legitimate traffic from bot farms. Analytics platforms wanted to filter out non-human visitors. Ad networks had to combat click fraud from datacenter-based operations. The available solutions were either prohibitively expensive for small teams or required maintaining fragile scrapers against hosting provider websites.

Nick Galbreath (client9) created ipcat as an open-source alternative: a curated dataset mapping IPv4 address ranges to their organizational owners, specifically targeting datacenters, hosting providers, and co-location facilities. By choosing CSV as the format and GitHub as the distribution mechanism, the project made IP categorization data accessible to anyone while enabling community contributions through pull requests. The transparency of git history meant you could audit every change, understand data provenance, and trust the dataset in ways proprietary solutions couldn't match. Released under the MIT license, it became a foundational dataset for countless fraud detection systems, analytics pipelines, and security tools.

Technical Insight

Data Sources

Read rows

Parse & convert to uint32

Query with IP

Match found

Updates

Updates

CSV Data Files

IP Ranges

CSV Parser

LoadCSV

Sorted IPRange Array

In-Memory Cache

Binary Search

Lookup Function

Datacenter Owner

Provider Info

Manual Contributions

Proprietary Algorithms

System architecture — auto-generated

The architecture of ipcat is deceptively simple but thoughtfully designed. At its core are CSV files with four columns: start IP address, end IP address, provider name, and provider URL. Each row represents a contiguous IP range owned by a single organization. The ranges are non-overlapping and sorted, which enables efficient binary search lookups. This design choice prioritizes integration ease over sophistication—any language with CSV parsing can consume this data immediately.

Here's how you'd integrate ipcat into a Go application for datacenter detection:

package main

import (
    "encoding/csv"
    "net"
    "os"
    "sort"
    "strconv"
    "strings"
)

type IPRange struct {
    StartIP uint32
    EndIP   uint32
    Owner   string
    URL     string
}

type IPCat struct {
    ranges []IPRange
}

func ipToUint32(ip net.IP) uint32 {
    ip = ip.To4()
    return uint32(ip[0])<<24 | uint32(ip[1])<<16 | uint32(ip[2])<<8 | uint32(ip[3])
}

func (ic *IPCat) LoadCSV(filename string) error {
    file, err := os.Open(filename)
    if err != nil {
        return err
    }
    defer file.Close()

    reader := csv.NewReader(file)
    records, err := reader.ReadAll()
    if err != nil {
        return err
    }

    for _, record := range records {
        startIP := net.ParseIP(record[0])
        endIP := net.ParseIP(record[1])
        
        ic.ranges = append(ic.ranges, IPRange{
            StartIP: ipToUint32(startIP),
            EndIP:   ipToUint32(endIP),
            Owner:   record[2],
            URL:     record[3],
        })
    }
    
    return nil
}

func (ic *IPCat) Lookup(ipStr string) (string, bool) {
    ip := net.ParseIP(ipStr)
    if ip == nil {
        return "", false
    }
    
    ipNum := ipToUint32(ip)
    
    // Binary search for the range
    idx := sort.Search(len(ic.ranges), func(i int) bool {
        return ic.ranges[i].EndIP >= ipNum
    })
    
    if idx < len(ic.ranges) && ic.ranges[idx].StartIP <= ipNum {
        return ic.ranges[idx].Owner, true
    }
    
    return "", false
}

func main() {
    cat := &IPCat{}
    cat.LoadCSV("datacenters.csv")
    
    testIPs := []string{"54.239.28.176", "8.8.8.8", "192.168.1.1"}
    
    for _, ip := range testIPs {
        if owner, found := cat.Lookup(ip); found {
            println(ip, "belongs to datacenter:", owner)
        } else {
            println(ip, "not a known datacenter IP")
        }
    }
}

The binary search approach is critical for performance. With thousands of IP ranges, linear scanning would be prohibitively slow for high-throughput applications. By converting IP addresses to 32-bit unsigned integers and maintaining sorted ranges, lookups become O(log n) operations. In production systems processing millions of requests, this efficiency matters enormously.

The CSV format also enables interesting use cases beyond simple lookups. You can aggregate statistics about IP space ownership, analyze geographic distribution of hosting providers, or combine ipcat data with other datasets like MaxMind's GeoIP for richer context. The datacenter.csv file becomes a bridge between different data sources, all because CSV is universally parseable.

One architectural decision worth noting: ipcat separates discovery from distribution. The project maintainers used proprietary algorithms to discover and validate IP ranges, but they distribute the results as open data. This hybrid approach balances the difficulty of automated discovery (which requires sophisticated scraping, verification, and deduplication) with the benefits of open access. While you can't replicate their discovery pipeline, you can fork their data and maintain it through community contributions or your own research.

For web applications, you might integrate ipcat into middleware that flags or filters datacenter traffic. A Node.js Express example demonstrates the pattern:

const fs = require('fs');
const csv = require('csv-parser');

class IPCat {
    constructor() {
        this.ranges = [];
    }
    
    async load(filename) {
        return new Promise((resolve, reject) => {
            fs.createReadStream(filename)
                .pipe(csv())
                .on('data', (row) => {
                    this.ranges.push({
                        start: this.ipToInt(row.start),
                        end: this.ipToInt(row.end),
                        owner: row.owner,
                        url: row.url
                    });
                })
                .on('end', () => {
                    this.ranges.sort((a, b) => a.start - b.start);
                    resolve();
                })
                .on('error', reject);
        });
    }
    
    ipToInt(ip) {
        return ip.split('.').reduce((int, octet) => (int << 8) + parseInt(octet), 0) >>> 0;
    }
    
    lookup(ip) {
        const ipNum = this.ipToInt(ip);
        // Binary search implementation
        let left = 0, right = this.ranges.length - 1;
        
        while (left <= right) {
            const mid = Math.floor((left + right) / 2);
            const range = this.ranges[mid];
            
            if (ipNum < range.start) {
                right = mid - 1;
            } else if (ipNum > range.end) {
                left = mid + 1;
            } else {
                return range.owner;
            }
        }
        
        return null;
    }
}

const ipcat = new IPCat();
ipcat.load('datacenters.csv').then(() => {
    console.log('IPCat database loaded');
});

function datacenterDetection(req, res, next) {
    const clientIP = req.ip || req.connection.remoteAddress;
    const owner = ipcat.lookup(clientIP);
    
    if (owner) {
        req.isDatacenter = true;
        req.datacenterOwner = owner;
    }
    
    next();
}

module.exports = datacenterDetection;

This middleware pattern lets you make routing decisions, apply rate limits, or flag suspicious activity based on IP categorization—all with minimal latency overhead thanks to in-memory lookups.

Gotcha

The elephant in the room is that ipcat was archived in 2023. This isn't just a maintenance status—it fundamentally affects the data's reliability for production use. IP address allocations change constantly as hosting providers acquire new blocks, organizations restructure, or regional internet registries reassign space. What was accurate in 2023 grows increasingly stale with each passing month. The project documentation from 2011 explicitly noted missing coverage for Africa, Latin America, Korea, and Japan, and there's no indication these gaps were ever filled before archival.

The proprietary nature of the discovery algorithms creates another challenge. While the data is open, the methodology for generating it remains closed. This means you can't independently verify accuracy or replicate the process for updates. If you adopt ipcat, you're inheriting technical debt: the burden of maintaining the dataset falls entirely on you or your community fork. For high-stakes applications like fraud detection where false positives cost money, using outdated data can be worse than no data at all. A legitimate user connecting through a recently allocated IP block might get flagged as suspicious, or an attacker using hosting infrastructure not yet in the database slips through undetected.

Memory consumption can also surprise developers. Loading the complete dataset into memory works fine for single-instance applications, but in containerized environments with tight resource limits, or when running multiple services on shared infrastructure, the footprint becomes noticeable. The CSV format trades flexibility for efficiency—binary formats or specialized data structures could offer better compression and faster lookups, but at the cost of accessibility.

Verdict

Use ipcat if: you need a zero-cost starting point for datacenter detection in non-critical applications; you're building internal tools where 80% accuracy is acceptable; you have the resources to fork and maintain the dataset yourself; you value the transparency of open data over the convenience of commercial services; or you're prototyping fraud detection systems and need realistic test data. It's particularly valuable for educational purposes or as a component in larger systems where IP categorization is one signal among many.

Skip ipcat if: you need actively maintained, up-to-date IP intelligence for production fraud detection; your application operates globally and requires comprehensive coverage including underrepresented regions; you lack resources to fork and update the data regularly; false positives or false negatives carry significant business costs; or you need vendor support and SLAs. In these cases, commercial alternatives like MaxMind's GeoIP2 Anonymous IP or IPinfo.io's ASN database justify their cost through active maintenance, comprehensive coverage, and professional support. The archived status isn't a dealbreaker for all use cases, but it demands honest assessment of your accuracy requirements and maintenance capacity before adoption.

// ADD TO YOUR README
[![Featured on Starlog](https://starlog.is/api/badge/developer-tools/client9-ipcat.svg)](https://starlog.is/api/badge-click/developer-tools/client9-ipcat)