MQTT Fundamentals

MQTT Bandwidth Usage: Calculating Overhead per Message

How many bytes does an MQTT message really cost? Work through connect, keep alive and publish overhead at each QoS level, add TCP and TLS, and build a monthly data estimate for cellular IoT devices.

Stacked bar breaking a single message into payload, MQTT, TCP and TLS bytes to show mqtt bandwidth usage
Run this on your own MQTT brokerGet a private, managed MQTT broker on the Free plan and start from ready-made code samples.
Try it free Code samples No card required

MQTT bandwidth usage is easy to estimate once you know where every byte comes from: the MQTT packet itself, the TCP/IP headers that carry it, the TLS encryption around it, and the background traffic of connects and keep alives. This guide calculates each layer for a realistic telemetry message at QoS 0, 1 and 2, then turns the numbers into a monthly data estimate you can use when choosing a cellular plan for your devices.

Why MQTT Is Light, and Where the Bytes Still Go

MQTT's fixed header is as small as two bytes, there are no text headers like HTTP, and one long-lived connection replaces a handshake per request. That makes the protocol overhead per message tiny. But on a metered link you pay for IP bytes, not MQTT bytes, and for small payloads the lower layers dominate. A useful mental model has four layers:

Layer Per-message cost Notes
Payload Your data Often 20 to 200 bytes of JSON for telemetry
MQTT 2 to 6 bytes + topic name Topic length is often the biggest MQTT cost
TLS About 22 to 29 bytes per record Depends on TLS version and cipher
TCP/IP 40 bytes per segment (IPv4, no options) Often 52 with TCP timestamps; 60 with IPv6

Plus two background costs that are not per message: the connection setup (TCP, TLS handshake, CONNECT) and keep alive pings while idle. Both are covered below. For the byte-level layout of the MQTT layer, see the guide to MQTT packet structure.

MQTT Overhead per Message by QoS

Take a typical example: MQTT 3.1.1, topic plant1/line2/pump3/telemetry (28 characters) and a 48-byte JSON payload such as {"t":1760601600,"flow":12.4,"p":3.1,"temp":41.7}.

The PUBLISH packet at QoS 0 is:

  • Fixed header: 2 bytes (type and flags, one byte of remaining length)
  • Topic: 2-byte length prefix + 28 bytes = 30 bytes
  • Payload: 48 bytes
  • Total: 80 bytes

At QoS 1 and 2, the PUBLISH gains a 2-byte packet identifier, and acknowledgement packets of 4 bytes each are added:

QoS Packets on the wire MQTT bytes Overhead vs payload
0 PUBLISH (80) 80 32 bytes
1 PUBLISH (82) + PUBACK (4) 86 38 bytes
2 PUBLISH (82) + PUBREC + PUBREL + PUBCOMP (4 each) 94 46 bytes

With MQTT 5, add one byte for the empty properties length in PUBLISH; success acknowledgements without properties stay at 4 bytes. At the MQTT layer, then, QoS choice changes the cost only modestly. The real difference appears at the transport layer, because every extra packet usually means an extra TCP segment. The MQTT QoS levels guide explains when the extra delivery guarantees are worth it.

The Topic Name Is Often the Largest MQTT Cost

In the example above, the topic is 30 of the 32 bytes of QoS 0 overhead. Deep ISA-95 style topics can easily exceed 50 characters. Two remedies: keep device-side topics concise, or use MQTT 5 topic aliases, which replace the topic with a 2-byte number after the first publish. The MQTT topic alias guide shows the savings in detail.

Adding TCP/IP and TLS

Each MQTT packet sent on its own travels in a TCP segment with at least 40 bytes of IPv4 and TCP headers. The receiver acknowledges data with TCP ACKs, which are also 40-byte segments unless they can piggyback on data flowing the other way. Delayed ACK behaviour varies by operating system and network stack, so treat ACK costs as approximate.

TLS wraps each write in a record. With TLS 1.3 and an AES-GCM cipher, each record adds 5 bytes of header, 1 byte of inner content type and a 16-byte authentication tag: 22 bytes. TLS 1.2 with AES-GCM adds 29 bytes, because of an 8-byte explicit nonce.

Putting it together for one telemetry message, IPv4 without TCP options, TLS 1.3, counting one pure TCP ACK per data segment:

QoS Plain TCP (port 1883) TLS 1.3 (port 8883)
0 80 + 40 + 40 ACK = 160 bytes 102 + 40 + 40 ACK = 182 bytes
1 122 + 44 + 40 ACK = 206 bytes 144 + 66 + 40 ACK = 250 bytes
2 122 + 3 x 44 + 2 ACKs, about 335 bytes 144 + 3 x 66 + 2 ACKs, about 420 bytes

For a 48-byte payload, the wire cost is roughly four to eight times the data. That ratio shrinks as payloads grow: a 1 KB payload has nearly the same absolute overhead, which becomes a small fraction. This is why batching readings, for example one message with ten samples instead of ten messages, is the single most effective bandwidth optimisation.

TLS is not optional for real deployments, and as the table shows, its per-message cost is modest. Its bigger cost is the handshake.

Connection Setup Costs

Every new connection pays for:

  1. TCP handshake: three segments, roughly 120 to 180 bytes.
  2. TLS handshake: typically a few kilobytes, mostly the server's certificate chain. TLS 1.3 session resumption, where your library supports it, cuts this substantially.
  3. CONNECT and CONNACK: a CONNECT with a 20-character client ID, username and password is around 80 bytes; CONNACK is 4 bytes in MQTT 3.1.1.
  4. SUBSCRIBE and SUBACK for each subscription, if not using a persistent session.

So one reconnect can cost as much as dozens of telemetry messages. Devices that connect, publish once and disconnect every minute waste most of their data on handshakes. Unless the device sleeps for long periods, keep the connection open, and use a persistent session so you do not need to re-subscribe after every reconnect. The persistent session guide explains how.

Keep Alive Costs

When a client has nothing to send for the keep alive interval, it sends PINGREQ (2 bytes) and the broker answers with PINGRESP (2 bytes). With TLS 1.3 and TCP/IP, a ping exchange is about 64 + 64 bytes, plus an ACK: roughly 170 bytes.

Keep alive Pings per day while idle Approximate data per day
30 s 2,880 about 485 KB
60 s 1,440 about 240 KB
300 s 288 about 48 KB
900 s 96 about 16 KB

Pings are only sent when the connection is otherwise idle, so a device publishing every 30 seconds with a 60-second keep alive sends no pings at all. Longer intervals save data but risk NAT timeouts on cellular networks, which drop idle connections and force a costly reconnect. Tune it with the guidance in the MQTT keep alive interval article.

Estimating Monthly Data for a Cellular Device

This Python calculator combines the pieces. Adjust the inputs for your device:

def mqtt_monthly_bytes(
    payload=48, topic_len=28, qos=1, msgs_per_day=1440,
    tls=True, reconnects_per_day=2, handshake_bytes=5000,
    keepalive_s=120, idle_seconds_per_day=0,
):
    ip_tcp = 40
    tls_rec = 22 if tls else 0
    publish = 2 + 2 + topic_len + payload + (2 if qos else 0)

    def seg(mqtt_bytes):
        return mqtt_bytes + tls_rec + ip_tcp

    if qos == 0:
        per_msg = seg(publish) + ip_tcp
    elif qos == 1:
        per_msg = seg(publish) + seg(4) + ip_tcp
    else:
        per_msg = seg(publish) + 3 * seg(4) + 2 * ip_tcp

    pings = idle_seconds_per_day / keepalive_s if keepalive_s else 0
    ping_bytes = pings * (2 * seg(2) + ip_tcp)
    connect_bytes = reconnects_per_day * (handshake_bytes if tls else 300)

    per_day = msgs_per_day * per_msg + ping_bytes + connect_bytes
    return per_day * 30

for qos in (0, 1, 2):
    mb = mqtt_monthly_bytes(qos=qos) / 1_000_000
    print(f"QoS {qos}: {mb:.1f} MB per month")

With one message per minute at QoS 1 over TLS, the result is around 11 MB per month per device, with only a small share coming from the two assumed reconnects. Change msgs_per_day to 144 (every ten minutes) with a 300-second keep alive and the idle time added, and the total drops sharply. These are calculated estimates, not measurements; validate them by checking actual usage on a test SIM or by capturing traffic for a day with tcpdump and summing the bytes.

A Bandwidth Reduction Checklist

  1. Batch samples into fewer, larger messages where latency allows.
  2. Use compact payloads: short JSON keys, CBOR or Protobuf. See MQTT payload format.
  3. Shorten device topics or use topic aliases with MQTT 5.
  4. Choose QoS per topic: QoS 0 for frequent telemetry where an occasional loss is acceptable, QoS 1 for events and commands.
  5. Keep connections open and use persistent sessions instead of reconnecting per message.
  6. Tune keep alive to the longest value your network's NAT timeout allows.
  7. Publish on change rather than at a fixed rate when values are stable.
  8. Avoid unnecessary subscriptions: a wildcard subscription on a device receives every matching message, which costs downlink data.

Run Efficient Devices on ConnectMQ

ConnectMQ supports MQTT 3.1.1 and 5.0, including topic aliases and session expiry, over TLS on port 8883 with a Let's Encrypt certificate, so the optimisations above work without special configuration. Prototype on the free public MQTT broker, then measure real traffic against a private broker. The MQTT tutorial covers the fundamentals if you are new to the protocol.

Get Started with ConnectMQ

A managed MQTT broker lets you focus on device efficiency instead of server operations. Create a free Serverless broker at app.connectmq.com, no credit card required, and use the live topic explorer to see exactly what your devices send. For a broader comparison, read our guide to choosing a cloud MQTT broker.

Frequently Asked Questions

How much overhead does MQTT add per message?

At the MQTT layer, a PUBLISH adds 4 bytes plus the topic name at QoS 0, and 6 bytes plus the topic at QoS 1 and 2, before acknowledgements. TCP/IP and TLS add roughly 60 or more bytes per packet on top.

Does TLS make MQTT much more expensive?

Per message, TLS 1.3 adds about 22 bytes per record, which is modest. The larger cost is the handshake of a few kilobytes per new connection, so keep connections open rather than reconnecting frequently.

How much data does MQTT keep alive use?

A ping exchange costs a few bytes at the MQTT layer but around 170 bytes with TCP, IP and TLS. At a 60-second interval on an idle connection, that is roughly 240 KB per day.

Is QoS 0 always the cheapest?

Yes, in bytes, because it sends no acknowledgement packets. But if lost messages force your application to resend data or poll, QoS 1 can end up cheaper overall.

Related articles

Why Your MQTT Client Keeps Disconnecting (and How to Fix It)

Reconnect loops are one of the most common MQTT problems. Learn the five usual causes, from duplicate client IDs to keep alive timeouts and oversized packets, and follow a diagnosis flow that pinpoints yours.

8 min read

Who Invented MQTT? The History from Oil Pipelines to IoT

MQTT was invented in 1999 by Andy Stanford-Clark of IBM and Arlen Nipper of Arcom to monitor oil pipelines over satellite. Follow its path to open source, OASIS, ISO and today's managed cloud brokers.

7 min read

MQTT Maximum Message Size: Limits and Large Payload Strategies

MQTT can technically carry a 256 MB packet, but brokers, client libraries and devices impose far lower limits. Learn where the real limits are, what happens when you exceed them, and how to send large payloads.

7 min read

All articles →

Your private MQTT broker is a few clicks away

Create an account, get your broker credentials and connect your first device — no servers to install, patch or scale.