Skip to main content

How to Convert Bytes to Int in Python

Converting bytes to integers in Python is a fundamental task that comes up frequently when working with binary files, network protocols, low-level system data, or hardware interfaces. Since bytes represent raw binary data, Python needs additional context, such as byte order (endianness) and signedness, to interpret them correctly as numerical values.

In this guide, you will learn the most reliable and commonly used ways to convert bytes into integers in Python, complete with clear explanations, practical examples with outputs, and common pitfalls to avoid.

What Does Converting Bytes to Int Mean?

A bytes object in Python represents a sequence of raw binary data. Converting it to an integer means interpreting that binary sequence as a numeric value. However, the same bytes can produce different integer values depending on how you interpret them.

data = b'\x00\x01'

# Interpreted as big-endian → 1
print(int.from_bytes(data, "big"))

# Interpreted as little-endian → 256
print(int.from_bytes(data, "little"))

Output:

1
256

As you can see, the byte order directly affects the resulting integer. This is why understanding endianness and signedness is essential before performing any conversion.

What is Endianness?

Big-endian means the most significant byte (MSB) comes first, while little-endian means the least significant byte (LSB) comes first. Network protocols typically use big-endian, whereas many modern CPUs (such as x86) use little-endian internally.

The built-in int.from_bytes() method is the most Pythonic, readable, and flexible way to convert bytes to integers. It is available in Python 3.2 and later and requires no additional imports.

Syntax

int.from_bytes(bytes, byteorder, *, signed=False)

Parameters

ParameterDescription
bytesA bytes or bytearray object to convert
byteorder"big" (MSB first) or "little" (LSB first)
signedFalse (default) for unsigned integers, True for signed integers using two's complement
Python 3.11+ Change

Starting with Python 3.11, the byteorder parameter defaults to "big" if omitted, making it optional. In earlier versions, you must always specify it explicitly.

Examples

# Big-endian: most significant byte first
byte_val_1 = b'\x00\x01'
res_1 = int.from_bytes(byte_val_1, "big")
print(f"Big-endian b'\\x00\\x01': {res_1}")
# Output: Big-endian b'\x00\x01': 1

# Little-endian: least significant byte first
byte_val_2 = b'\x00\x10'
res_2 = int.from_bytes(byte_val_2, "little")
print(f"Little-endian b'\\x00\\x10': {res_2}")
# Output: Little-endian b'\x00\x10': 4096

# Signed integer using two's complement
byte_val_3 = b'\xfc\x00'
res_3 = int.from_bytes(byte_val_3, "big", signed=True)
print(f"Signed big-endian b'\\xfc\\x00': {res_3}")
# Output: Signed big-endian b'\xfc\x00': -1024

# Single byte conversion
byte_val_4 = b'\xff'
res_4 = int.from_bytes(byte_val_4, "big")
print(f"Single byte b'\\xff' unsigned: {res_4}")
# Output: Single byte b'\xff' unsigned: 255

# Same single byte, but signed
res_5 = int.from_bytes(byte_val_4, "big", signed=True)
print(f"Single byte b'\\xff' signed: {res_5}")
# Output: Single byte b'\xff' signed: -1

Output:

Big-endian b'\x00\x01': 1
Little-endian b'\x00\x10': 4096
Signed big-endian b'\xfc\x00': -1024
Single byte b'\xff' unsigned: 255
Single byte b'\xff' signed: -1

How the Math Works

Understanding the underlying arithmetic helps verify your conversions:

  • Big-endian b'\x00\x01': 0x00 * 256 + 0x01 = 1
  • Little-endian b'\x00\x10': 0x00 + 0x10 * 256 = 4096
  • Signed b'\xfc\x00': The raw unsigned value is 64512, but two's complement interpretation over 2 bytes yields -1024

Method 2: Using struct.unpack()

The struct module is specifically designed for interpreting bytes as packed binary data. It is the preferred approach when working with binary file formats, network protocols, or any context where data follows a well-defined structure with fixed-size fields.

Syntax

import struct

struct.unpack(format, buffer)

The function returns a tuple, even when unpacking a single value, so you typically access element [0].

Examples

import struct

# 2-byte unsigned short, big-endian
byte_data_1 = b'\x01\x02'
res_1 = struct.unpack('>H', byte_data_1)[0]
print(f"Big-endian unsigned short: {res_1}")
# Output: Big-endian unsigned short: 258

# 4-byte unsigned int, little-endian
byte_data_2 = b'\x01\x00\x00\x00'
res_2 = struct.unpack('<I', byte_data_2)[0]
print(f"Little-endian unsigned int: {res_2}")
# Output: Little-endian unsigned int: 1

# 4-byte signed int, big-endian
byte_data_3 = b'\xff\xff\xff\xfc'
res_3 = struct.unpack('>i', byte_data_3)[0]
print(f"Big-endian signed int: {res_3}")
# Output: Big-endian signed int: -4

# Unpacking multiple values at once
byte_data_4 = b'\x00\x01\x00\x02'
res_4 = struct.unpack('>HH', byte_data_4)
print(f"Two unsigned shorts: {res_4}")
# Output: Two unsigned shorts: (1, 2)

Output:

Big-endian unsigned short: 258
Little-endian unsigned int: 1
Big-endian signed int: -4
Two unsigned shorts: (1, 2)

Format String Reference

CharacterMeaningSize (bytes)
>Big-endian byte order-
<Little-endian byte order-
!Network byte order (big-endian)-
BUnsigned char1
bSigned char1
HUnsigned short2
hSigned short2
IUnsigned int4
iSigned int4
QUnsigned long long8
qSigned long long8
Buffer Size Must Match

The byte buffer length must exactly match the size expected by the format string. A mismatch will raise a struct.error:

import struct
struct.unpack('>I', b'\x01\x02') # Expects 4 bytes, got 2
# struct.error: unpack requires a buffer of 4 bytes

When to Choose struct.unpack() Over int.from_bytes()

  • You need to unpack multiple values from a single byte sequence in one call
  • You are parsing structured binary formats (file headers, packet layouts)
  • You want the format to serve as self-documenting code for the binary layout

Method 3: Using numpy.frombuffer()

When working with large binary datasets or performance-critical applications, NumPy provides a fast and memory-efficient way to convert bytes to integers. This method truly shines when you need to interpret a byte buffer as an array of integers rather than a single value.

Syntax

import numpy as np

numpy.frombuffer(buffer, dtype)

Examples

import numpy as np

# Big-endian unsigned short (2 bytes)
byte_data_1 = b'\x01\x02'
res_1 = np.frombuffer(byte_data_1, dtype='>u2')[0]
print(f"Big-endian unsigned short: {res_1}")
# Output: Big-endian unsigned short: 258

# Big-endian signed int (4 bytes)
byte_data_2 = b'\x01\x00\x00\x00'
res_2 = np.frombuffer(byte_data_2, dtype='>i4')[0]
print(f"Big-endian signed int: {res_2}")
# Output: Big-endian signed int: 16777216

# Little-endian unsigned int (4 bytes)
byte_data_3 = b'\x01\x00\x00\x00'
res_3 = np.frombuffer(byte_data_3, dtype='<u4')[0]
print(f"Little-endian unsigned int: {res_3}")
# Output: Little-endian unsigned int: 1

# Converting an array of values at once
byte_data_4 = b'\x01\x00\x02\x00\x03\x00\x04\x00'
res_4 = np.frombuffer(byte_data_4, dtype='<u2')
print(f"Array of little-endian unsigned shorts: {res_4}")
# Output: Array of little-endian unsigned shorts: [1 2 3 4]

Output:

Big-endian unsigned short: 258
Big-endian signed int: 16777216
Little-endian unsigned int: 1
Array of little-endian unsigned shorts: [1 2 3 4]

NumPy dtype Notation

PrefixMeaning
>Big-endian
<Little-endian
uUnsigned integer
iSigned integer
NumberSize in bytes (1, 2, 4, or 8)

For example, <u4 means "little-endian unsigned 4-byte integer."

When to Use NumPy

  • Processing large binary streams with many values
  • Working with scientific or sensor data stored in binary format
  • Situations where vectorized operations on the resulting integers are needed
  • Performance-critical applications where converting thousands or millions of values one by one would be too slow
note

NumPy is a third-party library and must be installed separately (pip install numpy). For simple, single-value conversions, int.from_bytes() or struct.unpack() are more appropriate since they require no external dependencies.

Common Mistake: Using the Wrong Byte Order

One of the most frequent bugs when converting bytes to integers is assuming the wrong endianness. The same bytes produce completely different values depending on byte order.

Incorrect assumption (expected 1, used big-endian):

result = int.from_bytes(b'\x01\x00', "big")
print(result)

Output:

256

Correct approach (data is actually little-endian):

result = int.from_bytes(b'\x01\x00', "little")
print(result)

Output:

1
tip

Always check the documentation of your data source to confirm its endianness. Network data typically uses big-endian (also called "network byte order"), while data from x86-based systems is usually little-endian. You can also check your system's native byte order with sys.byteorder.

Common Mistake: Forgetting to Handle Signed Integers

When bytes represent a signed value (such as a negative temperature reading or a signed offset), omitting the signed=True parameter produces an incorrect positive number.

Incorrect (treats b'\xff\xff' as unsigned):

result = int.from_bytes(b'\xff\xff', "big")
print(result)

Output:

65535

Correct (interprets as signed using two's complement):

result = int.from_bytes(b'\xff\xff', "big", signed=True)
print(result)

Output:

-1
note

If you are using struct.unpack(), choose the correct format character: use lowercase letters (b, h, i, q) for signed types and uppercase letters (B, H, I, Q) for unsigned types.

Quick Comparison of All Three Methods

Featureint.from_bytes()struct.unpack()numpy.frombuffer()
Built-inYesYesNo (requires NumPy)
Handles arbitrary byte lengthsYesNo (fixed sizes only)No (fixed sizes only)
Unpack multiple values at onceNoYesYes
Best for bulk dataNoNoYes
ReadabilityHighMediumMedium
Recommended for general useYesStructured formatsLarge datasets

Best Practices

  • Prefer int.from_bytes() for most single-value conversions due to its clarity and simplicity.
  • Use struct.unpack() when dealing with structured binary formats or when you need to unpack multiple fields from a single buffer.
  • Use NumPy for large-scale binary data processing where performance matters.
  • Always verify byte order and signedness against the specification of your data source before performing any conversion.
  • Document binary formats clearly in your codebase so other developers understand the expected byte layout.
  • Validate input length before conversion to avoid unexpected errors from truncated or malformed data.

Conclusion

Python provides multiple powerful tools to convert bytes into integers. For most everyday applications, int.from_bytes() is the best choice, offering excellent readability and full control over byte order and signedness. For parsing structured binary data with multiple fields, struct.unpack() is the natural fit. When performance is critical and you are processing large volumes of binary data, NumPy delivers the speed and efficiency you need.

Understanding how bytes map to integers, and being mindful of endianness and signedness, ensures correctness, portability, and reliability whenever you work with low-level binary data in Python.