QGIS actions and an OpenStreetMap API
In april of this year I went to the 2023 QGIS User Conference in ‘s-Hertogenbosch. At that conference there was a very interesting workshop on using actions in QGIS by Ujaval Gandhi of Spatial Thoughts. At the time I decided to attend another workshop and talk that were also very interesting, but luckily Ujaval shared his workshop notes online, so I tried all his examples after the conference and I was really impressed with the possibilities of actions in QGIS. You should really check them out.
A couple of weeks ago I read a blog post by Brendan Ashworth of Bunting Labs about their API to download from OpenStreetMap. The thing that caught my attention was the fact that this API delivers GeoJSON data as a result of a request. So I wondered if it would be possible to use this API in QGIS actions. In this blog post I will show you how I did just that.
Objective
The thing I tried to achieve is that if I activate the QGIS action and I click on a point in a point type layer, all buildings from OpenStreetMap in a radius of 100 meter show up on the map in a Temporary Scratch Layer, including all the data from tags in the Attribute Table.
Where to put the QGIS action code?
First I added a point layer with addresses to my QGIS Project. Any point layer will work for this example. If you would like to do the same for a polyline or a polygon layer, you would need to change code below to do so. If you have added your point layer, you should open its properties and select “Actions” in the left panel of the Properties window.

There you click on the button with the green plus sign below the (probably empty) Action List.

A new window pops up where you can set the settings for your action. Make sure you set the Type to “Python” and add at least a Description. You can also add a Short Name and an Icon. The Action Scopes Canvas and Feature should be checked.

You can now add the code in the “Action Text” text area.
The code for the QGIS action
The following code is the one I created to achieve the above objective. In the comments I describe what each step does.
# Import the necessary packages
import requests
import json
from qgis.core import Qgis, QgsVectorLayer, QgsJsonUtils, QgsProject
# Get the coordinates of the point you clicked on in the map. The parts between [% %] are QGIS Expressions. Those work the same as if you would use them in the Field Calculator. Python interprets it as the resulting value.
pointxy = "[%replace(to_string(x(transform($geometry, @layer_crs ,'EPSG:4326'))),',','.')%],[%replace( to_string(y(transform($geometry, @layer_crs ,'EPSG:4326'))),',','.')%]"
# The parameters of the request to the API. Replace MY_API_KEY with your own key, that you can get at https://buntinglabs.com/solutions/openstreetmap-extracts. We request all buildings within a radius of 100 meter around the current point.
params = {
"tags": "building=*",
"api_key": "MY_API_KEY",
"center": pointxy,
"radius": "100"
}
# Make the request to the extract-in-radius endpoint of the API
response = requests.get("https://osm.buntinglabs.com/v1/osm/extract-in-radius", params=params)
# Convert the response to a JSON string
geoj = json.dumps(response.json())
# Find the attribute fields in the GeoJSON
fields = QgsJsonUtils.stringToFields(geoj)
# Get the features from the GeoJSON and use the previously found attribute fields
feats = QgsJsonUtils.stringToFeatureList(geoj,fields, None)
# If the API returns 1 or more features, execute the code below else print a message to the console log
if len(feats) > 0:
# Get the geometry type of the result of the request. For our buildings example it will always be a Multipolygon, but if you request something else from the API, it could be another type.
geom_type = feats[0].geometry().type()
if geom_type == QgsWkbTypes.PointGeometry:
geotype = "MultiPoint"
elif geom_type == QgsWkbTypes.LineGeometry:
geotype = "MultiLineString"
elif geom_type == QgsWkbTypes.PolygonGeometry:
geotype = "MultiPolygon"
else:
print("geometry type not defined in the result")
# Create an empty Temporary Scratch Layer called "Buildings" of the previously found geometry type
layer = QgsVectorLayer(geotype, "Buildings", "memory")
# Add the attribute fields to the Temporary Scratch Layer
layer.dataProvider().addAttributes(fields)
# Add the empty Temporary Scratch Layer to the map
registry = QgsProject.instance()
registry.addMapLayer(layer)
# Add each feature from the API request to the Temporary Scratch Layer
for feature in feats:
layer.dataProvider().addFeatures([feature])
else:
print("no features found in the geoJSON")
# Reload the visualisation of the Temporary Scratch Layer
layer.updateExtents()
layer.reload()
The result
This action results in a Temporary Scratch Layer with the polygons of the buildings 100 meter around the clicked point.

Hi Michel,
Thanks for sharing this usefull code.
Great tutorial!