Connect a Python service to the Home Assistant REST API
Address Home Assistant through HTTP
The REST API is useful for individual operations: read a state, call an action, or place a temporary state in Home Assistant's state machine. Unlike MQTT Discovery, POST /api/states/... does not register a permanent integration.
1. Create and protect a token
Create a Long-Lived Access Token in the Home Assistant user profile. Prefer a dedicated, minimally privileged user. The token is shown only once and belongs in a protected credential file or service environment—not in source code.
curl --fail --show-error \
-H "Authorization: Bearer TOKEN" \
-H "Content-Type: application/json" \
http://HOME-ASSISTANT-IP:8123/api/
Plain HTTP is only appropriate on a trusted LAN. Use HTTPS or a VPN across other networks, and do not expose port 8123 directly merely for this script.
2. Build a Python client with error handling
import os
import requests
HA_URL = "http://HOME-ASSISTANT-IP:8123"
TOKEN = os.environ["HA_TOKEN"]
HEADERS = {"Authorization": f"Bearer {TOKEN}", "Content-Type": "application/json"}
def ha_request(method, path, **kwargs):
response = requests.request(
method, f"{HA_URL}{path}", headers=HEADERS,
timeout=(5, 15), **kwargs
)
response.raise_for_status()
return response
Timeouts prevent a network fault from blocking the service indefinitely. raise_for_status() exposes authentication and server failures.
Read a state
response = ha_request("GET", "/api/states/sensor.outdoor_temperature")
data = response.json()
print(data["state"])
States are strings. Handle unknown, unavailable, and unexpected units before calculations.
Write a temporary state
payload = {
"state": "42.5",
"attributes": {"friendly_name": "LAN server load", "unit_of_measurement": "%"},
}
ha_request("POST", "/api/states/sensor.lanserver_load", json=payload)
This creates a state representation, not an integration that fetches its own data. Republish it after a restart. MQTT Discovery is usually better for persistent, regularly updated sensors.
Call an action
payload = {"entity_id": "light.living_room"}
ha_request("POST", "/api/services/light/turn_on", json=payload)
Use fixed or strictly validated entity IDs and payloads. Never turn unchecked user input into arbitrary service calls.
Troubleshooting
401: inspect the token, user, and Authorization header.404: verify the path and entity ID.- Timeout: inspect routing, firewall, DNS, and Home Assistant availability.
- TLS errors: correct the certificate chain and hostname; do not permanently disable verification.
- Unexpected JSON: log the response without exposing the token.