Home Assistant REST Sensor - Beginner-Friendly Guide
The REST sensor lets Home Assistant fetch data from a device or web service over HTTP. Think of it as HA visiting a URL in the background, reading the response, and turning that into a sensor you can use on dashboards and in automations.
REST, HTTP, GET/POST, and JSON - quick definitions
- REST - a common way devices/services share data over the web.
- HTTP - the protocol used for web requests. You'll see URLs like
http://192.168.1.50/status. - GET - request data (read-only).
- POST - send some data and get a response back (e.g., ask for special info).
- JSON - a simple text format. Example:
{"temperature": 21.7, "device": "living_room"}.
Quick start - your first REST sensor
This example fetches your external IP from a free API and shows it as a sensor value.
sensor:
- platform: rest
name: External IP
resource: https://api.ipify.org/?format=json
value_template: "{{ value_json.ip }}"
The endpoint returns {"ip":"203.0.113.10"}. value_json.ip picks the ip field from the JSON response.
Two ways to define REST sensors
- Single sensor via
sensor:+platform: rest- good for one value. - Multiple sensors from one call via the
rest:section - one HTTP request, many values.
rest:
- resource: http://192.168.1.50/status
sensor:
- name: Room Temperature
value_template: "{{ value_json.temperature }}"
unit_of_measurement: "°C"
- name: Room Humidity
value_template: "{{ value_json.humidity }}"
unit_of_measurement: "%" Tip - using rest: avoids hammering the device with repeated calls for each sensor.
Core options you'll use most
1) Where to fetch
- resource - the URL. Example:
http://192.168.1.50/status. - resource_template - same as
resourcebut you can use templates. Use eitherresourceorresource_template, not both. - params - URL query parameters (supports templates). Equivalent to adding
?key=valueto the URL.
sensor:
- platform: rest
name: Energy for Yesterday
resource_template: https://api.example.com/energy
params:
start_date: "{{ (now() - timedelta(days=1)).strftime('%Y-%m-%d') }}"
end_date: "{{ now().strftime('%Y-%m-%d') }}" 2) How to fetch
- method -
GET(default) orPOST. - payload - body sent with
POST. Usually JSON text. - headers - HTTP headers (e.g.,
Authorization,Content-Type). Can use templates. - username, password, authentication - for HTTP Basic/Digest auth to protected endpoints.
- timeout - seconds to wait (default 10).
- verify_ssl - verify HTTPS certs (default
true). Setfalseif you're using a self-signed cert on your LAN. - encoding - character encoding if the server doesn't send one (default UTF-8).
sensor:
- platform: rest
name: Heater Status
resource: http://192.168.1.75/api/heater
method: POST
headers:
Content-Type: application/json
payload: '{ "device": "heater_1" }'
value_template: "{{ value_json.status }}" 3) What to extract
- value_template - turn the response into the sensor's state (short, single value).
- json_attributes - copy extra fields into attributes (good for long text or multiple values).
- json_attributes_path - JSONPath to where those attributes live in the response.
sensor:
- platform: rest
name: Weather Snapshot
resource: https://api.example.com/weather/today
value_template: "{{ value_json.summary }}"
json_attributes_path: "$.details.current"
json_attributes:
- temperature
- humidity
- wind_kph
In this example, value_template sets the main state to the short summary text, while json_attributes adds useful details as attributes.
4) How it appears in HA
- name - friendly name.
- device_class - tells HA what the sensor represents (e.g.,
temperature,humidity,battery). - unit_of_measurement - like
°C,%,kWh. - state_class - for statistics/history (
measurement,total_increasing, etc.). - icon - custom Material Design icon template if you want.
- picture - URL or template for an entity picture.
- unique_id - lets you rename the entity in the UI without YAML edits.
5) Availability & updates
- availability - template that decides if the sensor is "available". Return
true/false. - force_update - if
true, record new history entries even when the value didn't change. - scan_interval - how often to poll the endpoint (seconds). Useful to slow down frequent calls.
sensor:
- platform: rest
name: Boiler Pressure
resource: http://192.168.1.91/boiler
value_template: "{{ value_json.pressure_bar }}"
unit_of_measurement: "bar"
device_class: pressure
state_class: measurement
availability: "{{ value_json.connected is defined and value_json.connected }}"
scan_interval: 60Security - tokens, headers, and secrets
Many APIs need a Bearer token in the Authorization header. Don't hardcode secrets in YAML - use secrets.yaml.
# configuration.yaml
sensor:
- platform: rest
name: My Secure Thing
resource: https://api.example.com/device
headers:
Authorization: !secret my_secure_token
value_template: "{{ value_json.state }}"
# secrets.yaml
my_secure_token: "Bearer eyJhbGciOi..."
If the endpoint uses HTTP Basic or Digest auth, you can also set username, password, and authentication: basic (or digest).
Templating essentials
- value_json - the parsed JSON response. Example:
{{ value_json.temperature }}. - value - the raw text if not JSON.
- this - the current entity's state object (advanced; handy in
availabilityor icon templates).
sensor:
- platform: rest
name: Plain Text Example
resource: http://192.168.1.60/plain
value_template: "{{ value | trim }}"Handling long text - use attributes
Sensor state is limited to 255 characters. Store anything longer as attributes and keep the state short (like an ID or a short title).
sensor:
- platform: rest
name: News Article
resource: https://example.com/article.json
value_template: "{{ value_json.id }}"
json_attributes:
- title
- body View attributes in Developer Tools - States. You can also create template sensors to display attributes elsewhere.
Understanding json_attributes_path
When an API returns a complicated JSON structure, the data you want might not be at the very top - it could be nested several levels deep.
Instead of listing every level in json_attributes, you can use json_attributes_path to tell Home Assistant
"start here" before grabbing the listed fields.
Let's look at an example response from an imaginary weather API:
{
"data": {
"current": {
"temperature": 20.5,
"humidity": 56,
"wind": {
"speed": 12.4,
"direction": "NW"
}
}
}
}
If you only want the temperature and humidity, those are inside data → current.
To reach them, you can set the path to $.data.current.
sensor:
- platform: rest
name: "Weather Snapshot"
resource: https://api.example.com/weather
value_template: "{{ value_json.data.current.temperature }}"
unit_of_measurement: "°C"
json_attributes_path: "$.data.current"
json_attributes:
- humidity
- wind This tells Home Assistant:
- Use
value_templatefor the temperature (the main value). - Start inside
data → currentwhen looking for attributes. - Copy
humidityandwind(a small dictionary itself) as attributes.
If you omit the path, Home Assistant looks for attributes at the top level of the response.
Using json_attributes_path just saves you from repeating long dotted paths like
value_json.data.current.humidity for every field.
Working with XML endpoints
If the server reports XML (text/xml, application/xml, etc.), Home Assistant auto-converts it to JSON (keys become JSON fields). You can then use value_json as if it were JSON originally.
sensor:
- platform: rest
name: Steam Time Remaining
resource: http://192.168.1.105/status.xml
value_template: "{{ value_json.response.time0 }}"Using JSONPath for attributes
json_attributes_path lets you point at a nested part of a response and copy fields from there into attributes.
sensor:
- platform: rest
name: First User
resource: https://jsonplaceholder.typicode.com/users
value_template: "{{ value_json[0].name }}"
json_attributes_path: "$.[0].address"
json_attributes:
- street
- suite
- city
- zipcodePOST with parameters and content type
When posting JSON, set Content-Type: application/json. You can also combine params with a payload.
sensor:
- platform: rest
name: Billing Summary
resource: https://api.example.com/billing/summary
method: POST
headers:
Content-Type: application/json
Authorization: "Bearer {{ states('input_text.api_token') }}"
params:
month: "{{ now().strftime('%Y-%m') }}"
payload: >
{ "account_id": "{{ states('input_text.account_id') }}",
"include": ["totals","last_payment"] }
value_template: "{{ value_json.totals.amount_due }}"Making multiple sensors from one call (recommended)
rest:
- resource: http://192.168.1.50/env
scan_interval: 30
sensor:
- name: Env Temp
device_class: temperature
unit_of_measurement: "°C"
state_class: measurement
value_template: "{{ value_json.temp }}"
- name: Env Humidity
device_class: humidity
unit_of_measurement: "%"
state_class: measurement
value_template: "{{ value_json.humid }}"
- name: Env Status
value_template: "{{ value_json.status }}"
json_attributes:
- last_seen
- firmwareAvailability template - mark sensor as offline/online
sensor:
- platform: rest
name: Inverter Power
resource: http://192.168.1.88/inverter
value_template: "{{ value_json.power_w }}"
unit_of_measurement: "W"
availability: >
{{ value_json is defined and value_json.ok is defined and value_json.ok }}Troubleshooting tips
- Test the URL in a browser or with
curlto see the raw response. - Use the right field in
value_template- print the whole object temporarily:value_template: "{{ value_json | tojson }}" - Quotes in templates - if you use single quotes inside a template, wrap the template in double quotes (or vice versa).
- resource vs resource_template - you must pick one.
- SSL issues on local HTTPS - try
verify_ssl: falsewhile testing. - State too long - keep state short and move the rest to
json_attributes. - Slow endpoints - increase
timeoutand/orscan_interval.
# Inspect raw response during setup
sensor:
- platform: rest
name: Debug Response
resource: http://192.168.1.50/status
value_template: "{{ value_json | tojson }}"Copy-paste reference - common options in one place
sensor:
- platform: rest
name: Example REST Sensor
unique_id: example_rest_sensor_1
resource_template: https://api.example.com/data
params:
q: "kitchen"
date: "{{ now().strftime('%Y-%m-%d') }}"
method: GET # or POST
headers:
Authorization: !secret api_bearer
User-Agent: Home Assistant
Accept: application/json
# payload: '{ "example": true }' # only for POST
timeout: 15
verify_ssl: true
encoding: utf-8
value_template: "{{ value_json.result.value }}"
json_attributes_path: "$.result.details"
json_attributes:
- foo
- bar
- baz
device_class: power
unit_of_measurement: "W"
state_class: measurement
icon: "{{ 'mdi:flash' if (value|float(0) > 0) else 'mdi:flash-off' }}"
picture: "{{ value_json.image_url if value_json.image_url is defined else none }}"
availability: "{{ value_json.available | default(false) }}"
force_update: false
scan_interval: 60Recap
- Use value_template for the main value and json_attributes for extra fields or long text.
- Secure tokens via secrets.yaml and headers. For HTTP auth, use username/password/authentication.
- Shape the result with device_class, unit_of_measurement, and state_class.
- Fetch many values at once using the rest: block with multiple sensor entries.
- Remember - use either resource or resource_template.
With these building blocks, you can pull in data from almost any device or service and make it feel "native" in Home Assistant.