Subscribing with Wildcards
Monitor many devices or variables with a single subscription using + and # wildcards.
Subscribing with wildcards
MQTT wildcards let you subscribe to multiple topics at once, so you can monitor many devices or variables with a single subscription.
| Wildcard | Name | Matches |
|---|---|---|
+ | Single-level | Exactly one level in the topic hierarchy |
# | Multi-level | All remaining levels — must be the last character |
Throughout the examples below, assume three devices:
| Device | Variables |
|---|---|
device-a | temperature, pressure, humidity |
device-b | temperature |
device-c | pressure, humidity |
All examples use these connection parameters:
| Field | Value | Required |
|---|---|---|
| Host | api.surfact.com | Yes |
| Port | 1883 (no TLS) / 8883 (TLS) | Yes |
| Username | Your Surfact token | Yes |
| Password | Any character or blank | No |
| Quality of Service | 0 or 1 | No |
Single-level wildcard (+)
+)The + wildcard matches exactly one level. Use it when you know the topic structure but want to vary a single segment.
One variable across all devices
/v1.6/devices/+/pressure/lv
/v1.6/devices/+/pressure
This is equivalent to subscribing to pressure on every device that has it (device-a and device-c).
# Last value only
mosquitto_sub -h "api.surfact.com" -t "/v1.6/devices/+/pressure/lv" -u "$SURFACT_TOKEN" -p 8883 -q 1 -v
# Full data point (with timestamp and context)
mosquitto_sub -h "api.surfact.com" -t "/v1.6/devices/+/pressure" -u "$SURFACT_TOKEN" -p 8883 -q 1 -vimport paho.mqtt.client as mqtt
token = "your-surfact-token"
def on_connect(client, userdata, flags, rc):
client.subscribe("/v1.6/devices/+/pressure/lv")
def on_message(client, userdata, msg):
# The topic shows which device sent the update
print(f"Topic: {msg.topic}")
print(f"Value: {msg.payload.decode()}")
client = mqtt.Client(client_id="my-device-12345678901234567890")
client.username_pw_set(username=token, password="")
client.on_connect = on_connect
client.on_message = on_message
client.connect("api.surfact.com", 8883)
client.loop_forever()All variables from one device
/v1.6/devices/device-a/+/lv
/v1.6/devices/device-a/+
This subscribes to every variable on device-a (temperature, pressure, humidity).
def on_message(client, userdata, msg):
parts = msg.topic.split('/')
variable = parts[3] # variable is at index 3
print(f"Variable: {variable} -> {msg.payload.decode()}")Multi-level wildcard (#)
#)The # wildcard matches any number of remaining levels and must be the last character in the topic.
All variables from a specific device
/v1.6/devices/device-a/#
Equivalent to subscribing to every variable on device-a. You'll receive full data points (value, timestamp, context) whenever any of its variables update.
mosquitto_sub -h "api.surfact.com" -t "/v1.6/devices/device-a/#" -u "$SURFACT_TOKEN" -p 8883 -q 1 -vimport paho.mqtt.client as mqtt
import json
token = "your-surfact-token"
device_label = "device-a"
def on_connect(client, userdata, flags, rc):
client.subscribe(f"/v1.6/devices/{device_label}/#")
def on_message(client, userdata, msg):
parts = msg.topic.split('/')
variable = parts[3] if len(parts) > 3 else "unknown"
try:
data = json.loads(msg.payload.decode())
print(f"{variable}: value={data.get('value')} ts={data.get('timestamp')}")
except json.JSONDecodeError:
print(f"{variable}: {msg.payload.decode()}")
client = mqtt.Client(client_id="my-device-12345678901234567890")
client.username_pw_set(username=token, password="")
client.on_connect = on_connect
client.on_message = on_message
client.connect("api.surfact.com", 8883)
client.loop_forever()Everything, from all devices
/v1.6/devices/#
This subscribes to all variables on all devices.
Warning: this can generate extremely high message volumes in production. Use it only for testing or small-scale deployments.
Reading the topic
The topic itself tells you the source of each message — always parse it in your message handler:
/v1.6/devices/device-a/temperature
│ │ │
│ │ └─ Variable label
│ └─ Device label
└─ API version
Messages can be full JSON objects (base variable topics) or plain values (/lv topics), so your handler should tolerate both.
Use cases & risk
| Pattern | Use case | Risk |
|---|---|---|
/v1.6/devices/+/temperature | One variable across all devices | ✅ Low |
/v1.6/devices/device-a/+ | All variables from one device | ✅ Low |
/v1.6/devices/+/+ | Any variable from any device (one level) | ⚠️ Medium |
/v1.6/devices/device-a/# | Everything for one device | ⚠️ Medium |
/v1.6/devices/# | Everything, everywhere (testing only) | 🚨 High |
Best practices
✅ Do
- Use wildcards to reduce the number of subscriptions
- Parse the topic to identify the source device and variable
- Prefer single-level (
+) wildcards when you know the structure - Test with small datasets before deploying to production
⚠️ Don't
- Use
/v1.6/devices/#in production with many devices - Subscribe to more data than you can process
- Forget to handle both JSON and plain-value message formats
Single-level vs. multi-level
| Feature | Single-level (+) | Multi-level (#) |
|---|---|---|
| Matches | Exactly one level | All remaining levels |
| Position | Any level | Must be last |
| Example | /v1.6/devices/+/temperature | /v1.6/devices/device-a/# |
| Message volume | Medium | High to very high |
Updated 3 months ago