Skip to content

roniemartinez/IPToCC

Repository files navigation

IPToCC

CI crates.io PyPI npm Python Rust License All Contributors

Fast, offline IPv4/IPv6 to ISO 3166-1 alpha-2 country code lookup. One Rust core with Python, WebAssembly, and CLI bindings.

Important

iptocc 3.0 is a complete Rust rewrite. Previous versions (2.x and earlier) were pure-Python on top of pandas. The 3.x line ships a Rust core with PyO3 Python bindings, wasm-bindgen WASM bindings, and a standalone iptocc crate. The Python public API stays compatible for the common case (see Migrating from 2.x).

Features

  • Offline lookup, no external API calls.
  • IPv4 and IPv6 in a single call.
  • Accepts a single address or a batch of addresses.
  • Lookup data embedded in the binary; no runtime file I/O after startup.
  • Sub-microsecond per-call latency on native Rust; low hundreds of nanoseconds through Python or WebAssembly.
  • iptocc CLI installed by all three ecosystems.
  • Database refreshed nightly from the five Regional Internet Registries.

Install

Python (3.10 or newer)

pip install iptocc

Rust

cargo add iptocc

Node, browser, Deno, bundlers

npm install @roniemartinez/iptocc

Usage

Python

from iptocc import country_code

# Single lookup
country_code("8.8.8.8")                # "US"
country_code("2001:4860:4860::8888")   # "US"
country_code("10.0.0.0")               # None

# Batch lookup (any iterable of strings)
country_code(["8.8.8.8", "1.0.16.1", "10.0.0.0"])
# ["US", "JP", None]

Rust

use iptocc::{country_code, country_codes};

let cc = country_code("8.8.8.8");
assert_eq!(cc, Some("US"));

let codes = country_codes(["8.8.8.8", "1.0.16.1", "10.0.0.0"]);
assert_eq!(codes, vec![Some("US"), Some("JP"), None]);

Node

const { country_code } = require("@roniemartinez/iptocc");

country_code("8.8.8.8");                     // "US"
country_code(["8.8.8.8", "1.0.16.1"]);       // ["US", "JP"]

CLI

$ iptocc 8.8.8.8
US

$ iptocc 8.8.8.8 1.0.16.1 10.0.0.0 193.0.6.139
8.8.8.8 US
1.0.16.1 JP
10.0.0.0 -
193.0.6.139 NL

Comparison with other Python IP-to-country libraries

Features

Feature iptocc 2.x (legacy) ip_to_country iptocc 3.x (this)
IPv4
IPv6
Offline lookup
Batch API
CLI
Python
Rust
WASM
Nightly data refresh

Performance (Python)

Measured on an Apple M3 Pro across the same 1,000 unique IPv4 addresses, one-shot wall-clock timing via time.perf_counter(). Lookup-only; one-time data load is excluded. Lower is better.

Library 1 lookup 1,000 lookups (total) Relative to ip_to_country
ip_to_country (bisect over array.array) ~1.2 us ~1.2 ms (loop; no batch API) 1x (baseline)
iptocc 2.1.2 (legacy, pandas DataFrame filter) ~78 ms ~78 s (loop; no batch API) ~65,000x slower
iptocc 3.x (Rust + PyO3) ~100 ns ~28 us (batch) ~42x faster

So iptocc 3.x is roughly ~42x faster than ip_to_country on a 1,000-address workload, while adding IPv6, a batch API, a CLI, and Rust/WASM bindings. (The legacy 2.1.2 row is included for historical context; its pandas-based filtering is orders of magnitude slower than any packed-binary approach.)

See BENCHMARK.md for the full per-RIR breakdown and the Rust-core / WASM numbers.

Migrating from 2.x

The legacy iptocc.get_country_code(ip) is now iptocc.country_code(ip). The return value is still str | None on a hit or miss.

# 2.x
from iptocc import get_country_code, CountryCodeNotFound
try:
    cc = get_country_code("8.8.8.8")
except CountryCodeNotFound:
    cc = None

# 3.x
from iptocc import country_code
cc = country_code("8.8.8.8")  # returns None on miss, no exception

Data sources

Lookups are based on the delegated extended statistics published by the five Regional Internet Registries:

  • AFRINIC: https://ftp.afrinic.net/stats/afrinic/delegated-afrinic-extended-latest
  • ARIN: https://ftp.arin.net/pub/stats/arin/delegated-arin-extended-latest
  • APNIC: https://ftp.apnic.net/public/apnic/stats/apnic/delegated-apnic-extended-latest
  • LACNIC: https://ftp.lacnic.net/pub/stats/lacnic/delegated-lacnic-extended-latest
  • RIPE NCC: https://ftp.ripe.net/pub/stats/ripencc/delegated-ripencc-extended-latest

A nightly GitHub Action refreshes these files, regenerates the embedded lookup tables, bumps the package versions, and publishes new releases automatically.

Development

Common dev tasks are exposed via Taskfile:

task test          # run Rust, Python, and WASM tests
task bench         # run benchmarks across all three runtimes
task build:python  # rebuild the pyo3 extension
task build:wasm    # rebuild the wasm-pack nodejs bundle

See BENCHMARK.md for performance numbers and how to reproduce them.

References

License

Licensed under either of Apache License, Version 2.0 or MIT license at your option.

Contributors ✨

Thanks goes to these wonderful people (emoji key):

Ronie Martinez
Ronie Martinez

💻 📖 🚇 🚧 👀
Tate Barber
Tate Barber

💻 📖 🚇
mathgeek12
mathgeek12

🐛
James Dolan
James Dolan

🤔

This project follows the all-contributors specification. Contributions of any kind welcome!

About

Get country code of IPv4/IPv6 address. Address lookup is done offline.

Topics

Resources

License

Apache-2.0, MIT licenses found

Licenses found

Apache-2.0
LICENSE-APACHE
MIT
LICENSE-MIT

Stars

Watchers

Forks

Packages

 
 
 

Contributors