Compact size
Compact size in Bitcoin stores a count or length in one to nine bytes. Values from 253 use an FD, FE or FF prefix. See the full table.
By Dimitar Zurnov · Updated 4 Nov 2026
Adapted from Learn Me a Bitcoin by Greg Walker.
A compact size is a variable-length integer. Bitcoin uses it for counts and lengths, such as the number of inputs in a transaction or the size of a script. Small values take one byte. Larger values start with a prefix byte, FD, FE or FF, followed by 2, 4 or 8 bytes. People often call this a VarInt, but the VarInt in Bitcoin's database works differently.
How many bytes does it take?
The first byte decides the size. A value from 0 to 252 is stored as one byte, and that byte is the value itself. The three prefixes mark larger values, and the bytes after the prefix hold the number in little-endian order.
| First byte | Value range | Bytes after the prefix | Total size | Example |
|---|---|---|---|---|
00 to FC |
0 to 252 | None, the byte is the value | 1 byte | 64 is 100 |
FD |
253 to 65,535 | 2 | 3 bytes | FDE803 is 1,000 |
FE |
65,536 to 4,294,967,295 | 4 | 5 bytes | FEA0860100 is 100,000 |
FF |
4,294,967,296 to 18,446,744,073,709,551,615 | 8 | 9 bytes | FF00E40B5402000000 is 10,000,000,000 |
Inside a transaction, this field holds the counts and the variable lengths. You will see the single-byte form far more often than the others.
Why does 253 take three bytes?
Because FD, FE and FF are prefixes, the values 253, 254 and 255 cannot be stored as a single byte. The largest value that stands for itself is 252. So 253 is written FDFD00, and 254 is written FDFE00. The prefix tells the reader that the next two bytes hold the number.
Real transactions show this. Transaction 3454605a…e936 has 254 outputs, so its output count is FDFE00. Transaction e411dbeb…a455 has an output script of 4,026 bytes, so that script's length is FDBA0F.
What do some examples look like?
The table shows encodings for values across the four sizes.
| Value | Compact size | Bytes |
|---|---|---|
| 100 | 64 |
1 |
| 252 | fc |
1 |
| 253 | fdfd00 |
3 |
| 1,000 | fde803 |
3 |
| 65,535 | fdffff |
3 |
| 65,536 | fe00000100 |
5 |
| 100,000 | fea0860100 |
5 |
| 4,294,967,295 | feffffffff |
5 |
| 4,294,967,296 | ff0000000001000000 |
9 |
| 10,000,000,000 | ff00e40b5402000000 |
9 |
| 18,446,744,073,709,551,615 | ffffffffffffffffff |
9 |
The largest value any compact size field can hold is 18,446,744,073,709,551,615, which is 2⁶⁴ − 1. It needs the FF prefix followed by eight bytes of FF.
Why use a variable size at all?
Most counts are small, but a few transactions have thousands of outputs. A fixed 2-byte field could hold them all, but it wastes space on the rest.
Take the output counts of ten transactions: 2, 2, 1, 2, 27, 3, 3,000, 2, 1 and 2. A fixed 2-byte field costs 20 bytes for these ten. The compact size costs 12 bytes. Nine of the values take one byte each, and 3,000 takes three bytes: FDB80B, with the low byte first.
Each saving is small. Multiply it across the network's daily transactions, and the bytes add up.
How do you read a real example?
The input count of one transaction on the chain is stored as FDC503. The FD prefix says two bytes follow. Those bytes are C5 03 in little-endian order, so the number is 0x03C5, which is 965. You can check that transaction on our explorer, where its inputs are listed.
Now a one-byte case. The pizza transaction that bought two pizzas with 10,000 BTC has 131 inputs. Its input count is the single byte 83, which is 131 in hex.
Where do compact size fields appear?
- Transactions: the number of inputs and outputs, and the length of each scriptSig and scriptPubKey. SegWit transactions also store the number of witness items and the length of each one.
- Blocks: the number of transactions. It comes straight after the 80-byte block header, and it is the only compact size in the block outside the transactions.
- Network messages: the number of items in a message such as
inv, and the length of many payloads.
This legacy transaction, 414719d5…a492, shows the fields in order. Its compact size fields are the input count, the scriptSig size, the output count and the scriptPubKey size.
01000000 version
01 input count: 1
79fe…7dae previous TXID, natural order
00000000 vout: 0
6b scriptSig size: 107 bytes
483045…2270 scriptSig, shortened here
ffffffff sequence
01 output count: 1
72c9000000000000 amount: 51,570 sat
19 scriptPubKey size: 25 bytes
76a914…88ac scriptPubKey, shortened here
00000000 locktime
A block starts like this, with the header first and the count after it:
[80-byte block header]
01 <- transaction count: one transaction
01000000 ... <- the first transaction starts here
In a network message, the inv payload shows the same idea. The 01 says one item follows. Each item is a 4-byte type, 01000000 for a transaction, and then a 32-byte hash.
Why are FE and FF rare?
Several limits explain it, and they differ by field.
Scripts before tapscript are capped at 10,000 bytes, so their length fits the FD form. The cap is MAX_SCRIPT_SIZE in Bitcoin Core's script.h.
The input count never needs FE. Each input takes at least 41 bytes, or 164 weight units, and a block holds 4,000,000 weight units. So a block could hold no more than about 24,390 minimum-size inputs. That is far below 65,536.
The output count could in theory reach that size. A minimum output is 9 bytes, or 36 weight units, so 65,536 of them would fit in one block. Real outputs are larger, and the transaction dd9f6bbf…a1ab3 has 13,107 outputs, well short of 65,535.
Witness data is where you are most likely to meet FE and FF. BIP 342 removed the 10,000-byte script limit for tapscript. A witness item of 65,536 bytes or more needs the FE prefix for its length.
How does Bitcoin Core check the encoding?
The reader in Bitcoin Core's serialize.h is stricter than the description above. It rejects a value that uses a longer prefix than it needs, with the error "non-canonical ReadCompactSize()". So FD 01 00 for the number 1 is an error, and the correct form is 01. By default it also rejects any length above 0x02000000, which is 32 MiB. A parser that skips these checks can accept data that Bitcoin Core refuses.
How do you encode and decode it in code?
The functions below follow the rules above. The decoder does not reject a longer prefix than needed, so use it only on data you have already checked.
def encode(n):
if n <= 252:
return bytes([n])
if n <= 0xFFFF:
size, prefix = 2, b"\xfd"
elif n <= 0xFFFFFFFF:
size, prefix = 4, b"\xfe"
else:
size, prefix = 8, b"\xff"
body = n.to_bytes(size, "little")
return prefix + body
def decode(data):
"""Return (value, bytes used)."""
first = data[0]
if first < 0xFD:
return first, 1
size = {0xFD: 2, 0xFE: 4, 0xFF: 8}[first]
value = int.from_bytes(data[1:1 + size], "little")
return value, 1 + size
# fdc503
print(encode(965).hex())
# (965, 3)
print(decode(bytes.fromhex("fdc503")))
Is a compact size the same as a VarInt?
No, although the two names get mixed up. The BIP 141 text calls the compact size a var_int. The VarInt in Bitcoin's chainstate database works differently. It reads bytes one at a time until it finds one whose top bit is clear. A compact size reads its prefix first and knows the length from that prefix alone.
In this section: Data
Something look wrong? Tell us