Skip to main content

How to Convert an IP Address to Integer in Python

Converting IP addresses to integers enables efficient database storage, fast sorting, and simple range queries. A 4-byte integer is smaller and faster to index than a 15-character string, and subnet checks become straightforward numeric comparisons.

Python's built-in ipaddress module handles this conversion securely and reliably for both IPv4 and IPv6.

In this guide, you will learn how to perform these conversions, explore practical use cases, and understand why the ipaddress module is the right tool for the job.

Converting an IP Address to an Integer

The most direct approach is to create an IP address object using the ipaddress module and cast it to int:

import ipaddress

# IPv4 conversion
ip = ipaddress.ip_address("192.168.1.1")
ip_int = int(ip)

print(f"{ip} -> {ip_int}")

Output:

192.168.1.1 -> 3232235777

The math behind this conversion follows the positional value of each octet:

192 * 256³ + 168 * 256² + 1 * 256¹ + 1 * 256⁰ = 3232235777

Each octet represents 8 bits, so an IPv4 address maps to a 32-bit unsigned integer.

Converting an Integer Back to an IP Address

Pass the integer directly to the ipaddress.ip_address() constructor to reverse the conversion:

import ipaddress

ip_int = 3232235777
ip = ipaddress.ip_address(ip_int)

print(ip)
print(type(ip))

Output:

192.168.1.1
<class 'ipaddress.IPv4Address'>
note

The module automatically determines whether the integer falls within the IPv4 range (up to 2^32 - 1) or the IPv6 range, and returns the appropriate object type.

Handling IPv6 Addresses

The same approach works seamlessly for IPv6 addresses, though the resulting integers are much larger since IPv6 uses 128-bit addresses:

import ipaddress

# IPv6 to integer
ipv6 = ipaddress.ip_address("2001:db8::1")
ipv6_int = int(ipv6)

print(f"{ipv6} -> {ipv6_int}")

# Integer back to IPv6
restored = ipaddress.ip_address(ipv6_int)
print(f"{ipv6_int} -> {restored}")

Output:

2001:db8::1 -> 42540766411282592856903984951653826561
42540766411282592856903984951653826561 -> 2001:db8::1
IPv6 Integer Size

IPv6 integers can be up to 2^128, which is approximately 340 undecillion. If you plan to store IPv6 addresses as integers in a database, ensure your column type supports numbers of this magnitude. Use DECIMAL, NUMERIC, or equivalent large-integer types rather than standard BIGINT, which is limited to 64 bits.

Practical Applications

Efficient Database Storage

Storing IP addresses as integers significantly improves indexing and query performance compared to storing them as variable-length strings:

import ipaddress
import sqlite3

def store_ip(conn, ip_string):
"""Store an IP address as an integer for efficient indexing."""
ip = ipaddress.ip_address(ip_string)
ip_int = int(ip)
version = ip.version

conn.execute(
"INSERT INTO access_log (ip_int, ip_version) VALUES (?, ?)",
(ip_int, version)
)

def retrieve_ip(ip_int):
"""Convert a stored integer back to a readable IP string."""
return str(ipaddress.ip_address(ip_int))

# Example usage
# store_ip(conn, "192.168.1.1")
# ip_string = retrieve_ip(3232235777) # "192.168.1.1"

IP Range Checking

Integer comparison makes range checks trivial, which is especially useful for firewall rules, geolocation lookups, or rate limiting:

import ipaddress

def is_ip_in_range(ip_string, start_ip, end_ip):
"""Check if an IP falls within a numeric range."""
ip = int(ipaddress.ip_address(ip_string))
start = int(ipaddress.ip_address(start_ip))
end = int(ipaddress.ip_address(end_ip))

return start <= ip <= end

print(is_ip_in_range("192.168.1.50", "192.168.1.0", "192.168.1.255"))
print(is_ip_in_range("10.0.0.1", "192.168.1.0", "192.168.1.255"))

Output:

True
False

Subnet Membership

The ipaddress module also provides a clean way to check whether an IP belongs to a subnet without manual integer arithmetic:

import ipaddress

def is_in_subnet(ip_string, subnet_string):
"""Check if an IP belongs to a given subnet."""
ip = ipaddress.ip_address(ip_string)
network = ipaddress.ip_network(subnet_string, strict=False)

return ip in network

print(is_in_subnet("192.168.1.100", "192.168.1.0/24"))
print(is_in_subnet("192.168.2.1", "192.168.1.0/24"))

Output:

True
False

Sorting IP Addresses Correctly

String-based sorting produces incorrect results for IP addresses because it compares character by character. Converting to integers ensures proper numeric ordering:

import ipaddress

ips = ["10.0.0.1", "192.168.1.1", "8.8.8.8", "172.16.0.1"]

# Sort using integer value for correct numeric ordering
sorted_ips = sorted(ips, key=lambda x: int(ipaddress.ip_address(x)))
print(sorted_ips)

Output:

['8.8.8.8', '10.0.0.1', '172.16.0.1', '192.168.1.1']

Without integer conversion, a naive string sort would incorrectly place "8.8.8.8" after "192.168.1.1" because the character "8" comes after "1" in ASCII order.

Batch Conversion Utilities

For applications that process large numbers of IP addresses, having reusable utility functions keeps your code clean and consistent:

import ipaddress
from typing import List

def ip_to_int(ip: str) -> int:
"""Convert an IP string to an integer."""
return int(ipaddress.ip_address(ip))

def int_to_ip(ip_int: int) -> str:
"""Convert an integer to an IP string."""
return str(ipaddress.ip_address(ip_int))

def batch_convert_to_int(ips: List[str]) -> List[int]:
"""Convert a list of IP strings to integers."""
return [ip_to_int(ip) for ip in ips]

def batch_convert_to_ip(integers: List[int]) -> List[str]:
"""Convert a list of integers to IP strings."""
return [int_to_ip(i) for i in integers]

# Usage
ips = ["192.168.1.1", "10.0.0.1", "8.8.8.8"]
integers = batch_convert_to_int(ips)
print(f"Integers: {integers}")

restored = batch_convert_to_ip(integers)
print(f"IPs: {restored}")

Output:

Integers: [3232235777, 167772161, 134744072]
IPs: ['192.168.1.1', '10.0.0.1', '8.8.8.8']

Handling Mixed IP Versions Safely

When processing user input or log files, you may encounter a mix of IPv4 addresses, IPv6 addresses, and invalid entries. Wrapping conversions in error handling ensures your application remains robust:

import ipaddress

def convert_ip_safely(ip_string):
"""Convert an IP address with version detection and validation."""
try:
ip = ipaddress.ip_address(ip_string)
return {
'ip': str(ip),
'integer': int(ip),
'version': ip.version,
'is_private': ip.is_private,
'is_loopback': ip.is_loopback
}
except ValueError as e:
return {'error': str(e)}

print(convert_ip_safely("192.168.1.1"))
print(convert_ip_safely("::1"))
print(convert_ip_safely("invalid"))

Output:

{'ip': '192.168.1.1', 'integer': 3232235777, 'version': 4, 'is_private': True, 'is_loopback': False}
{'ip': '::1', 'integer': 1, 'version': 6, 'is_private': True, 'is_loopback': True}
{'error': "'invalid' does not appear to be an IPv4 or IPv6 address"}

Why You Should Avoid Manual Bit Manipulation

You might encounter tutorials suggesting manual conversion using bit shifting or string splitting. While this can work for simple cases, it is error-prone and lacks input validation.

Avoid Manual Calculations

Manual approaches like (a << 24) + (b << 16) + (c << 8) + d silently accept invalid input:

# Manual approach: no validation, accepts garbage input
def bad_ip_to_int(ip):
parts = [int(x) for x in ip.split('.')]
return (parts[0] << 24) + (parts[1] << 16) + (parts[2] << 8) + parts[3]

# This produces a meaningless result without any error
result = bad_ip_to_int("999.999.999.999")
print(result) # 16826165991 (no error raised!)

The ipaddress module validates input automatically:

import ipaddress

try:
ipaddress.ip_address("999.999.999.999")
except ValueError as e:
print(e)

Output:

'999.999.999.999' does not appear to be an IPv4 or IPv6 address

The module also handles edge cases like compressed IPv6 notation, mapped addresses, and leading zeros that manual parsing would likely miss.

Quick Reference

OperationCode
IP string to integerint(ipaddress.ip_address("192.168.1.1"))
Integer to IP stringstr(ipaddress.ip_address(3232235777))
Numeric range checkstart_int <= ip_int <= end_int
Subnet membershipipaddress.ip_address(ip) in ipaddress.ip_network(subnet)
Version detectionipaddress.ip_address(ip).version

Conclusion

Converting IP addresses to integers in Python is simple and reliable when you use the built-in ipaddress module. Casting an IP address object to int gives you an efficient numeric representation suitable for database storage, fast comparisons, range queries, and correct sorting. The same module handles both IPv4 and IPv6 seamlessly, validates input automatically, and manages edge cases that manual bit manipulation would miss.

Best Practice

Always use the standard ipaddress module for IP address conversions. It validates input format, handles compressed IPv6 notation and mapped addresses, and works correctly for both IP versions without requiring any manual parsing or bit shifting.