Native API Component
The ESPHome native API is used to communicate with clients directly, with a highly-optimized network protocol. Currently, only the ESPHome tool, Home Assistant and ioBroker use this native API.
After adding an api: line to your ESPHome configuration you can go to the Home Assistant
web interface and navigate to the “Integrations” screen in the “Configuration” panel. Then wait
for the ESPHome device to show up under the discovered section (can take up to 5 minutes) or add
the device manually by choosing “ESPHome” from the integration overview and entering
“<NODE_NAME>.local” or the IP address of the unit in the “Host” field.
The ESPHome native API is based on a custom TCP protocol using protocol buffers. You can find the protocol data structure definitions here: api.proto A Python library that implements this protocol is aioesphomeapi.
NOTE
Actions were previously called Services. ESPHome changed the name in line with
Home Assistant
but will continue to support YAML with services and homeassistant.service for the foreseeable future.
Documentation will only refer to Actions.
# Example configuration entryapi:# Example with more optionsapi: port: 6053 batch_delay: 50ms # Reduce latency for real-time applications listen_backlog: 2 # Allow 2 pending connections in queue max_connections: 6 # Allow up to 6 simultaneous connections max_send_queue: 10 # Maximum queued messages per connection before disconnect encryption: key: "YOUR_ENCRYPTION_KEY_HERE" reboot_timeout: 30minConfiguration variables
Section titled “Configuration variables”-
port (Optional, int): The port to run the API server on. Defaults to
6053. -
listen_backlog (Optional, int): The maximum number of pending connections in the listen queue. Must be between 1 and 10. Defaults to
1for ESP8266/RP2040,4for ESP32 and other platforms. Lower values use less memory but may reject connections during bursts. -
max_connections (Optional, int): The maximum number of simultaneous API connections allowed. Must be between 1 and 20. Defaults to
4for ESP8266/RP2040,8for ESP32 and other platforms. Each connection uses approximately 500-1000 bytes of RAM.NOTE
Each API connection consumes approximately 500–1000 bytes of RAM while connected. ESP8266 and RP2040 devices have limited RAM available (ESP8266 typically has around 40KB of free RAM after boot, but this can drop to under 20KB once sensors and other components are configured; RP2040 uses LWIP raw sockets with similar constraints), so be careful not to set this value too high or it may cause out-of-memory crashes. The defaults are set to balance memory usage with allowing multiple simultaneous connections.
-
max_send_queue (Optional, int): The maximum number of messages that can be queued for sending per connection before the connection is dropped. Must be between 1 and 64. Defaults to:
5for ESP8266/RP2040,8for ESP32/BK72xx/LN882x/nRF52/RTL87xx,16for host platform.
This prevents memory exhaustion when a client is slow or network-stalled. Each queued message uses approximately 8-12 bytes of overhead plus the message size.
NOTE
When the send queue is full for a connection, the device will log an error and disconnect that client to prevent out-of-memory crashes. Slow clients, poor WiFi connections causing retries, or network congestion may trigger this. Increase this value if legitimate clients are being disconnected, but be mindful of memory constraints on embedded devices.
-
encryption (Optional): If present, encryption will be enabled for the API. Using encryption helps to secure the communication between the device running ESPHome and the connected client(s).
-
key (Optional, string): A 32-byte base64-encoded string to be used as the encryption key. If not provided, the key may be set at runtime, but encryption will not be used until it is set.
If you need a key, you can use the key below; it is randomly generated by your browser each time this page loads:
-
NOTE
Support for configuring the encryption key on-the-fly will be implemented in a future release of Home Assistant.
TIP
For comprehensive security guidance including API encryption best practices, see the Security Best Practices guide.
- actions (Optional, list): A list of user-defined actions. See User-defined Actions.
- batch_delay (Optional, Time): The delay time for batching multiple state update messages
together to reduce network overhead. Lower values send updates sooner but use more network packets,
while higher values batch more efficiently but add latency. Must be between
0msand65535ms(65.535 seconds). Defaults to100ms.
NOTE
Setting batch_delay: 0ms enables immediate sending mode for state updates. This is useful for
applications that require real-time responsiveness, such as IR remote binary sensors where rapid
ON→OFF transitions must be preserved. However, this will increase network traffic and may impact
WiFi performance with many rapidly-changing sensors. Only use this setting when necessary.
-
custom_services (Optional, boolean): Enable compilation of custom API services for external components that use the C++
CustomAPIDeviceclass. Only needed when external components register their own services via the native API. Defaults tofalse. -
homeassistant_services (Optional, boolean): Enable compilation of Home Assistant service call support for external components that use the C++
CustomAPIDevice::call_homeassistant_service()orCustomAPIDevice::fire_homeassistant_event()methods. This is automatically enabled when usinghomeassistant.serviceorhomeassistant.eventactions, or thehomeassistantplatform for number or switch components. Only needs to be manually set when external components call Home Assistant services without using the built-in actions. Defaults tofalse. -
homeassistant_states (Optional, boolean): Enable compilation of Home Assistant state subscription support for external components that use the C++
CustomAPIDevice::subscribe_homeassistant_state()method. This is automatically enabled when using anyhomeassistantplatform components (sensor, binary_sensor, text_sensor, switch, or number). Only needs to be manually set when external components subscribe to Home Assistant states without using the built-in components. Defaults tofalse. -
reboot_timeout (Optional, Time): The amount of time to wait before rebooting when no client connects to the API. This is needed because sometimes the low level ESP functions report that the ESP is connected to the network, when in fact it is not - only a full reboot fixes it. Can be disabled by setting this to
0s. Defaults to15min. -
id (Optional, ID): Manually specify the ID used for code generation.
-
on_client_connected (Optional, Action): An automation to perform when a client connects to the API. See
on_client_connectedTrigger. -
on_client_disconnected (Optional, Action): An automation to perform when a client disconnects from the API. See
on_client_disconnectedTrigger.
Actions
Section titled “Actions”Before using any of the actions below, you’ll need to tell Home Assistant to allow your device to perform actions.
NOTE
Starting with ESPHome 2025.10.0, you can configure actions to receive and process responses from
Home Assistant using capture_response, on_success, and on_error. See Action Response Handling for details.
Open the ESPHome integration page on your Home Assistant instance:
Then:
-
Find your device in the device list
-
Click the “configure” button next to it
-
Check the “Allow the device to perform Home Assistant actions” box
-
Then click “submit”
homeassistant.event Action
Section titled “homeassistant.event Action”NOTE
Be sure to follow the instructions above to tell Home Assistant to allow your device to perform actions.
When using the native API with Home Assistant, you can create events in the Home Assistant event bus straight from ESPHome Automations.
# In some triggeron_...: # Simple - homeassistant.event: event: esphome.button_pressed data: message: Button was pressedConfiguration variables
Section titled “Configuration variables”-
event (Required, string): The event to create - must begin with
esphome. -
data (Optional, mapping): Optional static data to pass along with the event.
-
data_template (Optional, mapping): Optional template data to pass along with the event. This is evaluated on the Home Assistant side with Home Assistant’s templating engine.
-
variables (Optional, mapping): Optional variables that can be used in the
data_template. Values are lambdas and will be evaluated before sending the request.
homeassistant.action Action
Section titled “homeassistant.action Action”NOTE
Be sure to follow the instructions above to tell Home Assistant to allow your device to perform actions.
When using the native API with Home Assistant, you can perform Home Assistant actions straight from ESPHome Automations.
# In some triggeron_...: # Simple - homeassistant.action: action: notify.html5 data: message: Button was pressed # With templates and variables - homeassistant.action: action: notify.html5 data: title: New Humidity data_template: message: The humidity is {{ my_variable }}%. variables: my_variable: |- return id(my_sensor).state;Configuration variables
Section titled “Configuration variables”-
action (Required, string): The Home Assistant Action to perform.
-
data (Optional, mapping): Optional static data to perform the action with.
-
data_template (Optional, mapping): Optional template data to perform the action with. This is evaluated on the Home Assistant side with Home Assistant’s templating engine.
-
variables (Optional, mapping): Optional variables that can be used in the
data_template. Values are lambdas and will be evaluated before sending the request. -
capture_response (Optional, boolean): Enable capturing the response from the Home Assistant action call. When enabled,
on_successmust be configured. Defaults tofalse. -
response_template (Optional, templatable, string): Optional Jinja template to process the action response data. This template is evaluated on the Home Assistant side with Home Assistant’s templating engine. Requires
capture_response: true. -
on_success (Optional, Automation): Optional automation to execute when the Home Assistant action call succeeds. When
capture_response: true, the response data is available as aresponsevariable of typeJsonObjectConst. See Action Response Handling. -
on_error (Optional, Automation): Optional automation to execute when the Home Assistant action call fails. See Action Response Handling.
Data structures are not possible, but you can create a script in Home Assistant and call with all the parameters in plain format.
# Home Assistant Configurationscript: ... set_light_rgb: alias: 'ESPHome RGB light set' sequence: - action: light.turn_on data_template: entity_id: '{{ light_name }}' rgb_color: - '{{ red }}' - '{{ green }}' - '{{ blue }}'Then, in ESPHome:
# In some triggeron_...: - homeassistant.action: action: script.set_light_rgb data: light_name: 'my_light' red: '255' green: '199' blue: '71'Action Response Handling
Section titled “Action Response Handling”NOTE
Action response handling is available in ESPHome 2025.10.0 and later.
You can configure actions to receive and process responses from Home Assistant. This enables bidirectional communication where ESPHome can not only call Home Assistant actions but also handle their responses.
Basic Success/Error Handling
Section titled “Basic Success/Error Handling”Use on_success and on_error to respond to action completion:
on_...: - homeassistant.action: action: light.toggle data: entity_id: light.demo_light on_success: - logger.log: "Toggled demo light" on_error: - logger.log: "Failed to toggle demo light"Capturing Response Data
Section titled “Capturing Response Data”To capture and process response data from actions, set capture_response: true. When enabled, on_success must be configured
and the response data is available as a JsonObjectConst variable named response.
# Example: Get weather forecast and parse JSON responseon_...: - homeassistant.action: action: weather.get_forecasts data: entity_id: weather.forecast_home type: hourly capture_response: true on_success: - lambda: |- JsonObjectConst next_hour = response["response"]["weather.forecast_home"]["forecast"][0]; float next_temperature = next_hour["temperature"].as<float>(); ESP_LOGI("weather", "Temperature next hour: %.1f", next_temperature);Using Response Templates
Section titled “Using Response Templates”Use response_template to extract and format data from complex responses using Home Assistant’s Jinja templating engine.
This requires capture_response: true.
# Example: Extract temperature using a templateon_...: - homeassistant.action: action: weather.get_forecasts data: entity_id: weather.forecast_home type: hourly capture_response: true response_template: "{{ response['weather.forecast_home']['forecast'][0]['temperature'] }}" on_success: - lambda: |- float temperature = response["response"].as<float>(); ESP_LOGI("weather", "Temperature next hour: %.1f", temperature);When response_template is used, the processed result is available in response["response"].
homeassistant.tag_scanned Action
Section titled “homeassistant.tag_scanned Action”NOTE
Be sure to follow the instructions above to tell Home Assistant to allow your device to make action calls.
When using the native API with Home Assistant, you can push tag_scanned to Home Assistant straight from ESPHome Automations.
# In some triggeron_...: # Simple - homeassistant.tag_scanned: some-tagConfiguration variables
Section titled “Configuration variables”- tag (Required, templatable, string): The id of the scanned tag
Triggers
Section titled “Triggers”on_client_connected Trigger
Section titled “on_client_connected Trigger”This trigger is activated each time a client connects to the API. Two variables of
type std::string are available for use by actions called from within this trigger:
client_address: the IP address of the client that connectedclient_info: the name of the client that connected
api: # ... on_client_connected: - logger.log: format: "Client %s connected to API with IP %s" args: ["client_info.c_str()", "client_address.c_str()"]on_client_disconnected Trigger
Section titled “on_client_disconnected Trigger”This trigger is activated each time the API disconnects from the API. Two variables of
type std::string are available for use by actions called from within this trigger:
client_address: the IP address of the client that disconnectedclient_info: the name of the client that disconnected
api: # ... on_client_disconnected: - logger.log: "API client disconnected!"Conditions
Section titled “Conditions”api.connected Condition
Section titled “api.connected Condition”This Condition checks if at least one client is connected to the ESPHome native API.
Configuration variables
Section titled “Configuration variables”- state_subscription_only (Optional, boolean): If enabled, only counts clients that have subscribed to entity state updates. This filters out logger-only connections (such as
esphome logscommand), which can cause false positives when waiting for Home Assistant. Defaults tofalse.
Check if any client is connected:
on_...: if: condition: api.connected: then: - logger.log: Client is connected to API!The lambda equivalent for this is id(api_id).is_connected().
Check if a client subscribed to entity states is connected (typically Home Assistant):
on_boot: - wait_until: condition: api.connected: state_subscription_only: true - logger.log: Home Assistant is connected! - homeassistant.event: event: esphome.device_bootedThe lambda equivalent for this is id(api_id).is_connected(true).
Use Cases:
- Use
state_subscription_only: false(default) to detect any API connection - Use
state_subscription_only: truewhen you need to ensure Home Assistant (or other connections that subscribe to states) is connected before sending events or calling services, preventing errors from logger-only connections
User-defined Actions
Section titled “User-defined Actions”It is also possible to get data from Home Assistant to ESPHome with user-defined actions. When you declare actions in your ESPHome YAML file, they will automatically show up in Home Assistant and you can call them directly.
NOTE
User-defined actions can also send responses back to the calling client using the api.respond action.
See Action Responses for details.
# Example configuration entryapi: actions: - action: start_laundry then: - switch.turn_on: relay - delay: 3h - switch.turn_off: relayFor example with the configuration seen above, after uploading you will see an action
called esphome.livingroom_start_laundry (livingroom is the node name) which you can
then call.
Additionally, you can also transmit data from Home Assistant to ESPHome with this method:
# Example configuration entryapi: actions: - action: start_effect variables: my_brightness: int my_effect: string then: - light.turn_on: id: my_light brightness: !lambda 'return my_brightness;' effect: !lambda 'return my_effect;'Using the variables key you can tell ESPHome which variables to expect from Home Assistant.
For example the action seen above would be executed with something like this:
# Example Home Assistant Actionaction: esphome.livingroom_start_effectdata_template: my_brightness: "{{ states.brightness.state }}" my_effect: "Rainbow"Then each variable you define in the variables section is accessible in the automation
triggered by the user-defined action through the name you gave it in the variables section
(note: this is a local variable, so do not wrap it in id(...) to access it).
There are currently 4 types of variables:
- bool: A boolean (ON/OFF). C++ type:
bool - int: An integer. C++ type:
int/int32_t - float: A floating point number. C++ type:
float - string: A string. C++ type:
std::string
Each of these also exist in array form:
- bool[]: An array of boolean values. C++ type:
std::vector<bool> - … - Same for other types.
Action Responses
Section titled “Action Responses”User-defined actions can send responses back to the calling client (such as Home Assistant). This enables bidirectional communication where actions can report success/error status or return structured JSON data.
Response Modes
Section titled “Response Modes”The response behavior is controlled by the supports_response option, which can be set explicitly or
auto-detected based on your action configuration:
- none (default): No response is sent. The action is “fire and forget”.
- status: Reports success/error status without data. Auto-detected when
api.respondis used withoutdata:. - optional: Returns JSON data when the client requests it. Auto-detected when
api.respondis used withdata:. - only: Always returns JSON data. Must be set explicitly. Use this for query-type actions.
Configuration variables
Section titled “Configuration variables”- supports_response (Optional, string): The response mode for this action. One of
none,status,optional, oronly. If not specified, the mode is auto-detected based onapi.respondusage in the action.
Trigger variables
Section titled “Trigger variables”When supports_response is not none, the following variables are available in the action:
- call_id (
uint32_t): A unique identifier for this action call. Used internally byapi.respond. - return_response (
bool): Only available inoptionalmode. Indicates whether the client requested a response. You don’t typically need to check this -api.respondhandles it automatically.
api.respond Action
Section titled “api.respond Action”This action sends a response back to the client that called the user-defined action. It can report success/error status and optionally include JSON data.
Configuration variables
Section titled “Configuration variables”- success (Optional, boolean, templatable): Whether the action succeeded.
Defaults to
true. - error_message (Optional, string, templatable): An error message to include
when
successisfalse. Defaults to an empty string. - data (Optional, lambda): A lambda that populates a JSON object
with response data. The lambda receives a
rootvariable of typeJsonObjectthat you can populate with key-value pairs.
Status Response Example
Section titled “Status Response Example”Report success or error without returning data:
api: actions: - action: validate_input variables: value: int then: - if: condition: lambda: 'return value < 0;' then: - api.respond: success: false error_message: "Value must be positive" else: - api.respond: success: trueData Response Example
Section titled “Data Response Example”Return structured JSON data to the caller:
api: actions: - action: get_sensor_data variables: sensor_name: string then: - api.respond: data: !lambda |- root["sensor"] = sensor_name; root["value"] = id(my_sensor).state; root["unit"] = "°C"; root["timestamp"] = id(homeassistant_time).now().timestamp;This action will be auto-detected as optional mode because it uses api.respond with data:.
Query Action Example
Section titled “Query Action Example”For actions that always return data (like queries), explicitly set supports_response: only:
api: actions: - action: get_device_info supports_response: only then: - api.respond: data: !lambda |- root["hostname"] = App.get_name(); root["version"] = ESPHOME_VERSION; root["uptime"] = millis() / 1000;Nested JSON Data
Section titled “Nested JSON Data”You can create complex nested JSON structures:
api: actions: - action: get_full_status supports_response: only then: - api.respond: data: !lambda |- root["device"]["name"] = "living_room"; root["device"]["version"] = 1; root["sensors"]["temperature"] = id(temp_sensor).state; root["sensors"]["humidity"] = id(humidity_sensor).state;Calling from Home Assistant
Section titled “Calling from Home Assistant”Actions with response support appear in Home Assistant with their response mode indicated. You can call them and receive the response data:
# Home Assistant automation exampleaction: esphome.device_get_sensor_datadata: sensor_name: "living_room"response_variable: sensor_responseThe response will be available in the sensor_response variable with the structure you defined in the
data: lambda.
Advantages over MQTT
Section titled “Advantages over MQTT”The ESPHome native API has many advantages over using MQTT for communication with Home Automation software (currently only Home Assistant and ioBroker). But MQTT is a great protocol and will never be removed. Features of native API (vs. MQTT):
-
Much more efficient: ESPHome encodes all messages in a highly optimized format with protocol buffers - for example binary sensor state messages are about 1/10 of the size.
-
One-click configuration: ESPHome just needs one click to set up in Home Assistant - no more messing around with retained MQTT discovery messages and alike.
-
One less single point of failure: In the ESPHome native API each ESP is its own server. With MQTT, when the broker shuts off nothing can communicate anymore.
-
Stability: Since ESPHome has far more control over the protocol than with MQTT, it’s really easy for us to roll out stability improvements.
-
Low Latency: The native API is optimized for very low latency, usually this is only a couple of milliseconds and far less than can be noticed by the eye.