IoT 05.07.2026 2 min read by Ulrich Emmerich

Connect a Python service to the Home Assistant REST API

Read Home Assistant states and call actions from Python while handling tokens, timeouts, HTTP errors, and secure network transport.

REST-API Home Assistant Python LAN-Server
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.

Sources and technical documentation

Links marked with * are affiliate links. As an Amazon Associate I earn from qualifying purchases.

More articles about IoT