[How-To] Fetch and display Garbage Collection (Sophämtning) for Stockholm, Sweden (SVOA API)

Hi everyone,

I noticed there is a lack of clear documentation on how to reliably fetch and display garbage collection data specifically for Stockholm, Sweden (Stockholm Vatten och Avfall / SVOA). Instead of relying on third-party custom components that might break, I found a way to query their API directly using Home Assistant’s native REST sensor.

I wanted to share a complete guide on how to get this data, along with a bonus template for a very compact and dynamic dashboard card.

Step 1: Finding your API URL

SVOA uses a simple open API endpoint for searches, but your address needs to be URL-encoded. The base URL is: https://www.stockholmvattenochavfall.se/villa-och-radhus/avfallstjanster/nar-kommer-sopbilen/Search?address=

Add your address, area, and zip code to the end, replacing spaces with %20 and Swedish letters with their URL-encoded counterparts (å = %C3%A5, ä = %C3%A4, ö = %C3%B6). Example for “Exempelgatan 1, Stockholm, 12345”: .../Search?address=Exempelgatan%201,%20Stockholm,%2012345

Step 2: The REST Configuration

You can add this directly under the rest: domain in your configuration.yaml, or in a separate rest.yaml file (if you use rest: !include rest.yaml).

This code fetches the JSON data every 6 hours and uses Jinja loops to dynamically find the correct waste types (General, Food, and Garden waste) and extract the Date, Frequency, and Weekday.

- resource: "https://www.stockholmvattenochavfall.se/villa-och-radhus/avfallstjanster/nar-kommer-sopbilen/Search?address=Miklag%C3%A5rdsv%C3%A4gen%2012,%20Bromma,%2016855"
  scan_interval: 21600
  headers:
    User-Agent: "Mozilla/5.0"
    Accept: "application/json"
  sensor:
    # ==========================================
    # 1. RESTAVFAfellowLL
    # ==========================================
    - name: "Soptömning Restavfall Datum"
      unique_id: soptomning_restavfall_datum
      icon: mdi:trash-can
      value_template: >
        {% set ns = namespace(val='Ej aktiv') %}
        {% for key, value in value_json.items() %}
          {% if 'Restavfall' in key %}
            {% set ns.val = value[0].ExecutionDate %}
          {% endif %}
        {% endfor %}
        {{ ns.val }}

    - name: "Soptömning Restavfall Frekvens"
      unique_id: soptomning_restavfall_frekvens
      icon: mdi:update
      value_template: >
        {% set ns = namespace(val='Okänd') %}
        {% for key, value in value_json.items() %}
          {% if 'Restavfall' in key %}
            {% set ns.val = value[0].FetchFrequency %}
          {% endif %}
        {% endfor %}
        {{ ns.val }}

    - name: "Soptömning Restavfall Veckodag"
      unique_id: soptomning_restavfall_veckodag
      icon: mdi:calendar-today
      value_template: >
        {% set ns = namespace(val='Okänd') %}
        {% for key, value in value_json.items() %}
          {% if 'Restavfall' in key %}
            {% set ns.val = value[0].Weekday %}
          {% endif %}
        {% endfor %}
        {{ ns.val }}

    # ==========================================
    # 2. MATAVFALL
    # ==========================================
    - name: "Soptömning Matavfall Datum"
      unique_id: soptomning_matavfall_datum
      icon: mdi:food-apple
      value_template: >
        {% set ns = namespace(val='Ej aktiv') %}
        {% for key, value in value_json.items() %}
          {% if 'Matavfall' in key %}
            {% set ns.val = value[0].ExecutionDate %}
          {% endif %}
        {% endfor %}
        {{ ns.val }}

    - name: "Soptömning Matavfall Frekvens"
      unique_id: soptomning_matavfall_frekvens
      icon: mdi:update
      value_template: >
        {% set ns = namespace(val='Okänd') %}
        {% for key, value in value_json.items() %}
          {% if 'Matavfall' in key %}
            {% set ns.val = value[0].FetchFrequency %}
          {% endif %}
        {% endfor %}
        {{ ns.val }}

    - name: "Soptömning Matavfall Veckodag"
      unique_id: soptomning_matavfall_veckodag
      icon: mdi:calendar-today
      value_template: >
        {% set ns = namespace(val='Okänd') %}
        {% for key, value in value_json.items() %}
          {% if 'Matavfall' in key %}
            {% set ns.val = value[0].Weekday %}
          {% endif %}
        {% endfor %}
        {{ ns.val }}

    # ==========================================
    # 3. TRÄDGÅRDSAVFALL
    # ==========================================
    - name: "Soptömning Trädgård Datum"
      unique_id: soptomning_tradgard_datum
      icon: mdi:leaf
      value_template: >
        {% set ns = namespace(val='Ej säsong') %}
        {% for key, value in value_json.items() %}
          {% if 'Trädgård' in key %}
            {% set ns.val = value[0].ExecutionDate %}
          {% endif %}
        {% endfor %}
        {{ ns.val }}

    - name: "Soptömning Trädgård Frekvens"
      unique_id: soptomning_tradgard_frekvens
      icon: mdi:update
      value_template: >
        {% set ns = namespace(val='Ej säsong') %}
        {% for key, value in value_json.items() %}
          {% if 'Trädgård' in key %}
            {% set ns.val = value[0].FetchFrequency %}
          {% endif %}
        {% endfor %}
        {{ ns.val }}

    - name: "Soptömning Trädgård Veckodag"
      unique_id: soptomning_tradgard_veckodag
      icon: mdi:calendar-today
      value_template: >
        {% set ns = namespace(val='Ej säsong') %}
        {% for key, value in value_json.items() %}
          {% if 'Trädgård' in key %}
            {% set ns.val = value[0].Weekday %}
          {% endif %}
        {% endfor %}
        {{ ns.val }}

Restart Home Assistant after adding this to generate the sensors.

Step 3: The Dashboard Card (Bonus)

If you have a wall-mounted tablet (Kiosk mode), standard cards often take up too much space. Here is a highly compressed, dynamic card to display the data we just fetched.

Features of this card:

  • Icon colors change automatically (Grey = Winter break/inactive, Blue/Brown/Green = Normal, Orange = 3 days left, Red = 1 day left or Today).
  • Injects a dynamic countdown text right next to the name (Idag), (Imorgon), (Om X dgr), or (Vinteruppehåll).

Requirements for the card: Install template-entity-row and card-mod via HACS Frontend.

The Code:

type: entities
title: Avfallshämtning
card_mod:
  style: |
    #states > * {
      margin-top: -10px !important;
      margin-bottom: -10px !important;
    }
entities:
  - type: custom:template-entity-row
    entity: sensor.soptomning_restavfall_datum
    icon: mdi:trash-can
    name: >
      {% set datum = states('sensor.soptomning_restavfall_datum') %} {% set ts =
      as_timestamp(datum ~ ' 00:00:00', 0) %} {% set now_ts =
      as_timestamp(now().strftime('%Y-%m-%d 00:00:00'), 0) %} {% set dagar =
      ((ts - now_ts) / 86400) | round(0) | int %} {% if ts == 0 or datum in
      ['unavailable', 'unknown', 'Ej aktiv'] %} Restavfall (Ej aktiv) {% elif
      dagar <= 0 %} Restavfall (Idag) {% elif dagar == 1 %} Restavfall (Imorgon)
      {% else %} Restavfall (Om {{ dagar }} dgr) {% endif %}
    state: >
      {{ states('sensor.soptomning_restavfall_veckodag') }} {{
      states('sensor.soptomning_restavfall_datum') }}
    color: >
      {% set datum = states('sensor.soptomning_restavfall_datum') %} {% set ts =
      as_timestamp(datum ~ ' 00:00:00', 0) %} {% set now_ts =
      as_timestamp(now().strftime('%Y-%m-%d 00:00:00'), 0) %} {% set dagar =
      ((ts - now_ts) / 86400) | round(0) | int %} {% if ts == 0 or datum in
      ['unavailable', 'unknown', 'Ej aktiv'] %} grey {% elif dagar <= 0 %} red
      {% elif dagar <= 3 %} orange {% else %} dodgerblue {% endif %}
  - type: custom:template-entity-row
    entity: sensor.soptomning_matavfall_datum
    icon: mdi:food-apple
    name: >
      {% set datum = states('sensor.soptomning_matavfall_datum') %} {% set ts =
      as_timestamp(datum ~ ' 00:00:00', 0) %} {% set now_ts =
      as_timestamp(now().strftime('%Y-%m-%d 00:00:00'), 0) %} {% set dagar =
      ((ts - now_ts) / 86400) | round(0) | int %} {% if ts == 0 or datum in
      ['unavailable', 'unknown', 'Ej aktiv'] %} Matavfall (Ej aktiv) {% elif
      dagar <= 0 %} Matavfall (Idag) {% elif dagar == 1 %} Matavfall (Imorgon)
      {% else %} Matavfall (Om {{ dagar }} dgr) {% endif %}
    state: >
      {{ states('sensor.soptomning_matavfall_veckodag') }} {{
      states('sensor.soptomning_matavfall_datum') }}
    color: >
      {% set datum = states('sensor.soptomning_matavfall_datum') %} {% set ts =
      as_timestamp(datum ~ ' 00:00:00', 0) %} {% set now_ts =
      as_timestamp(now().strftime('%Y-%m-%d 00:00:00'), 0) %} {% set dagar =
      ((ts - now_ts) / 86400) | round(0) | int %} {% if ts == 0 or datum in
      ['unavailable', 'unknown', 'Ej aktiv'] %} grey {% elif dagar <= 0 %} red
      {% elif dagar <= 3 %} orange {% else %} brown {% endif %}
  - type: custom:template-entity-row
    entity: sensor.soptomning_tradgard_datum
    icon: mdi:leaf
    name: >
      {% set datum = states('sensor.soptomning_tradgard_datum') %} {% set ts =
      as_timestamp(datum ~ ' 00:00:00', 0) %} {% set now_ts =
      as_timestamp(now().strftime('%Y-%m-%d 00:00:00'), 0) %} {% set dagar =
      ((ts - now_ts) / 86400) | round(0) | int %} {% if ts == 0 or datum in
      ['unavailable', 'unknown', 'Ej säsong'] %} Trädgård (Ej säsong) {% elif
      dagar > 30 %} Trädgård (Vinteruppehåll) {% elif dagar <= 0 %} Trädgård
      (Idag) {% elif dagar == 1 %} Trädgård (Imorgon) {% else %} Trädgård (Om {{
      dagar }} dgr) {% endif %}
    state: >
      {% set datum = states('sensor.soptomning_tradgard_datum') %} {% if datum
      in ['unavailable', 'unknown', 'Ej säsong'] %} 
        -
      {% else %}
        {{ states('sensor.soptomning_tradgard_veckodag') }} {{ datum }}
      {% endif %}
    color: >
      {% set datum = states('sensor.soptomning_tradgard_datum') %} {% set ts =
      as_timestamp(datum ~ ' 00:00:00', 0) %} {% set now_ts =
      as_timestamp(now().strftime('%Y-%m-%d 00:00:00'), 0) %} {% set dagar =
      ((ts - now_ts) / 86400) | round(0) | int %} {% if ts == 0 or datum in
      ['unavailable', 'unknown', 'Ej säsong'] or dagar > 30 %} grey {% elif
      dagar <= 0 %} red {% elif dagar <= 3 %} orange {% else %} #4CAF50 {% endif
      %}
view_layout:
  position: sidebar

Hope this helps anyone who need a garbage collect card in Stockholm out! Let me know if you have any questions.

Thank you, i was just thinking about adding collection dates to my dash.
SVOA seems to have updated the GET url input, the special åäö characters are now to be entered in the address directly without translation to "%C3%A5".

Great code @toolonghere - Thanks! :heart:
Added support for the new waste types: “Kärl 2 - Papper och Plast” and “Kärl 3 - Glas och Metall”

Just add to the end of your REST code, if needed, and You will recive 6 new enitys and 2 new waste types.
The code:

    # ==========================================
    # 4. KÄRL 2 - PAPPER OCH PLAST
    # ==========================================
    - name: "Soptömning Kärl 2 - Papper och Plast Datum"
      unique_id: soptomning_karl_2_datum
      icon: mdi:cube-outline
      value_template: >
        {% set ns = namespace(val='Ej aktiv') %}
        {% for key, value in value_json.items() %}
          {% if 'Kärl 2' in key %}
            {% set ns.val = value[0].ExecutionDate %}
          {% endif %}
        {% endfor %}
        {{ ns.val }}

    - name: "Soptömning Kärl 2 - Papper och Plast Frekvens"
      unique_id: soptomning_karl_2_frekvens
      icon: mdi:update
      value_template: >
        {% set ns = namespace(val='Okänd') %}
        {% for key, value in value_json.items() %}
          {% if 'Kärl 2' in key %}
            {% set ns.val = value[0].FetchFrequency %}
          {% endif %}
        {% endfor %}
        {{ ns.val }}

    - name: "Soptömning Kärl 2 - Papper och Plast Veckodag"
      unique_id: soptomning_karl_2_veckodag
      icon: mdi:calendar-today
      value_template: >
        {% set ns = namespace(val='Okänd') %}
        {% for key, value in value_json.items() %}
          {% if 'Kärl 2' in key %}
            {% set ns.val = value[0].Weekday %}
          {% endif %}
        {% endfor %}
        {{ ns.val }}

    # ==========================================
    # 5. KÄRL 3 - GLAS OCH METALL
    # ==========================================
    - name: "Soptömning Kärl 3 - Glas och Metall Datum"
      unique_id: soptomning_karl_3_datum
      icon: mdi:bottle-wine
      value_template: >
        {% set ns = namespace(val='Ej aktiv') %}
        {% for key, value in value_json.items() %}
          {% if 'Kärl 3' in key %}
            {% set ns.val = value[0].ExecutionDate %}
          {% endif %}
        {% endfor %}
        {{ ns.val }}

    - name: "Soptömning Kärl 3 - Glas och Metall Frekvens"
      unique_id: soptomning_karl_3_frekvens
      icon: mdi:update
      value_template: >
        {% set ns = namespace(val='Okänd') %}
        {% for key, value in value_json.items() %}
          {% if 'Kärl 3' in key %}
            {% set ns.val = value[0].FetchFrequency %}
          {% endif %}
        {% endfor %}
        {{ ns.val }}

    - name: "Soptömning Kärl 3 - Glas och Metall Veckodag"
      unique_id: soptomning_karl_3_veckodag
      icon: mdi:calendar-today
      value_template: >
        {% set ns = namespace(val='Okänd') %}
        {% for key, value in value_json.items() %}
          {% if 'Kärl 3' in key %}
            {% set ns.val = value[0].Weekday %}
          {% endif %}
        {% endfor %}
        {{ ns.val }}

It seems SVOA changed the API, now this should work:

- resource: "https://www.stockholmvattenochavfall.se/villa-och-radhus/avfallstjanster/nar-kommer-sopbilen/Search?address=xxxxxxxxxxxxxxxxxxx"
  scan_interval: 7200
  headers:
    User-Agent: "Mozilla/5.0"
    Accept: "application/json"
  sensor:
    # ==========================================
    # 1. RESTAVFALL och MATAVFALL
    # ==========================================
    - name: "Soptömning Restavfall Datum"
      unique_id: soptomning_restavfall_datum
      icon: mdi:trash-can
      value_template: >
        {% set ns = namespace(val='Ej aktiv') %}
        {% for item in value_json %}
          {% if 'restavfall' in item.group %}
            {% set ns.val = item.executionDate %}
          {% endif %}
        {% endfor %}
        {{ ns.val }}

    - name: "Soptömning Restavfall Frekvens"
      unique_id: soptomning_restavfall_frekvens
      icon: mdi:update
      value_template: >
        {% set ns = namespace(val='Okänd') %}
        {% for item in value_json %}
          {% if 'restavfall' in item.group %}
            {% set ns.val = item.fetchFrequency %}
          {% endif %}
        {% endfor %}
        {{ ns.val }}

    - name: "Soptömning Restavfall Veckodag"
      unique_id: soptomning_restavfall_veckodag
      icon: mdi:calendar-today
      value_template: >
        {% set ns = namespace(val='Okänd') %}
        {% for item in value_json %}
          {% if 'restavfall' in item.group %}
            {% set ns.val = item.weekday %}
          {% endif %}
        {% endfor %}
        {{ ns.val }}

    # ==========================================
    # 2. PAPPER och PLAST
    # ==========================================
    - name: "Soptömning Papper och Plast Datum"
      unique_id: soptomning_papper_datum
      icon: mdi:package-variant-closed
      value_template: >
        {% set ns = namespace(val='Ej aktiv') %}
        {% for item in value_json %}
          {% if 'papper' in item.group %}
            {% set ns.val = item.executionDate %}
          {% endif %}
        {% endfor %}
        {{ ns.val }}

    - name: "Soptömning Papper och Plast Frekvens"
      unique_id: soptomning_papper_frekvens
      icon: mdi:update
      value_template: >
        {% set ns = namespace(val='Okänd') %}
        {% for item in value_json %}
          {% if 'papper' in item.group %}
            {% set ns.val = item.fetchFrequency %}
          {% endif %}
        {% endfor %}
        {{ ns.val }}

    - name: "Soptömning Papper och Plast Veckodag"
      unique_id: soptomning_papper_veckodag
      icon: mdi:calendar-today
      value_template: >
        {% set ns = namespace(val='Okänd') %}
        {% for item in value_json %}
          {% if 'papper' in item.group %}
            {% set ns.val = item.weekday %}
          {% endif %}
        {% endfor %}
        {{ ns.val }}

    # ==========================================
    # 3. GLAS och METALL
    # ==========================================
    - name: "Soptömning Glas och Metall Datum"
      unique_id: soptomning_glas_datum
      icon: mdi:glass-fragile
      value_template: >
        {% set ns = namespace(val='Ej säsong') %}
        {% for item in value_json %}
          {% if 'glas' in item.group %}
            {% set ns.val = item.executionDate %}
          {% endif %}
        {% endfor %}
        {{ ns.val }}

    - name: "Soptömning Glas och Metall Frekvens"
      unique_id: soptomning_glas_frekvens
      icon: mdi:update
      value_template: >
        {% set ns = namespace(val='Ej säsong') %}
        {% for item in value_json %}
          {% if 'glas' in item.group %}
            {% set ns.val = item.fetchFrequency %}
          {% endif %}
        {% endfor %}
        {{ ns.val }}

    - name: "Soptömning Glas och Metall Veckodag"
      unique_id: soptomning_glas_veckodag
      icon: mdi:calendar-today
      value_template: >
        {% set ns = namespace(val='Ej säsong') %}
        {% for item in value_json %}
          {% if 'glas' in item.group %}
            {% set ns.val = item.weekday %}
          {% endif %}
        {% endfor %}
        {{ ns.val }}

    # ==========================================
    # 4. TRÄDGÅRDSAVFALL
    # ==========================================
    - name: "Soptömning Trädgård Datum"
      unique_id: soptomning_tradgard_datum
      icon: mdi:leaf
      value_template: >
        {% set ns = namespace(val='Ej säsong') %}
        {% for item in value_json %}
          {% if 'Trädgård' in item.group %}
            {% set ns.val = item.executionDate %}
          {% endif %}
        {% endfor %}
        {{ ns.val }}

    - name: "Soptömning Trädgård Frekvens"
      unique_id: soptomning_tradgard_frekvens
      icon: mdi:update
      value_template: >
        {% set ns = namespace(val='Ej säsong') %}
        {% for item in value_json %}
          {% if 'Trädgård' in item.group %}
            {% set ns.val = item.fetchFrequency %}
          {% endif %}
        {% endfor %}
        {{ ns.val }}

    - name: "Soptömning Trädgård Veckodag"
      unique_id: soptomning_tradgard_veckodag
      icon: mdi:calendar-today
      value_template: >
        {% set ns = namespace(val='Ej säsong') %}
        {% for item in value_json %}
          {% if 'Trädgård' in item.group %}
            {% set ns.val = item.weekday %}
          {% endif %}
        {% endfor %}
        {{ ns.val }}