Sync data
This guide shows how to keep a copy of your Telescope properties in your own system, such as a data warehouse, a database or an internal app. For Power BI, follow the Power BI guide instead.
Pagination
List endpoints return up to 100 items per page, with the most recently created first:
{
"next": "https://api.telescope.eco/external/properties/?cursor=cD0yMDI2...",
"previous": null,
"results": [{ "id": "...", "name": "...", "updated_at": "..." }]
}Keep requesting next until it is null. The page size is fixed.
- Treat the cursor as opaque. Don't build or change it yourself.
- Only follow
nextto Telescope. The safest way is to read thecursorquery parameter fromnextand send it to the base URL you configured, together with your other query parameters. Then your API key can never be sent to another host.
A reliable sync
Every run fetches the full list and replaces your copy:
- Fetch every page of
/external/properties/. - Upsert each property by
idinto your copy. - Remove the rest. The API doesn't report deletions, so delete the properties in your copy that this run didn't return.
Points worth getting right:
- Remove only after the last page. If a run fails halfway, a property you didn't get may be on a page you never fetched.
- Changes during a run are safe. Pages are ordered by when each property was created, so editing a property doesn't move it to another page. A property created during a run arrives with the next one.
- Run it on a schedule, for example nightly. Each run also brings the latest hazard and biodiversity results, buildings and energy certificates.
Nested results
A property's updated_at changes only when the property itself changes. New or changed hazard results, biodiversity results, buildings and energy certificates don't change it. Each hazard and biodiversity result has its own updated_at, so you can tell which results changed since your last run.
To get one property's current results between runs, fetch it with GET /external/properties/{id}/.
Example in Python
from urllib.parse import parse_qs, urlparse
import requests
BASE_URL = "https://api.telescope.eco"
session = requests.Session()
session.headers["X-API-Key"] = "your-secret"
def list_all(path: str, **params) -> list[dict]:
results = []
while True:
response = session.get(f"{BASE_URL}{path}", params=params, timeout=60)
response.raise_for_status()
page = response.json()
results.extend(page["results"])
if not page["next"]:
return results
# Only the cursor is taken from `next`, so the key never leaves BASE_URL
params["cursor"] = parse_qs(urlparse(page["next"]).query)["cursor"][0]
def sync(ids_in_your_copy: set[str]) -> None:
properties = list_all("/external/properties/")
for prop in properties:
upsert_property(prop) # your storage, keyed by prop["id"]
# Reached only after the last page, so a failed run removes nothing
for property_id in ids_in_your_copy - {prop["id"] for prop in properties}:
delete_property(property_id)Portfolios
GET /external/portfolios/ lists root portfolios only. To get the whole tree, request each portfolio's detail, which includes its direct children, and repeat for each child:
GET /external/portfolios/ → root portfolios
GET /external/portfolios/{id}/ → that portfolio's child portfolios and propertiesEach property also carries its portfolio_id, so you can attach properties to the tree without walking it through the portfolio details.
Filter what you fetch
The property list takes filters, for example to only fetch properties with a relevant landslide hazard at high exposure:
GET /external/properties/?hazard=landslide&exposure=HIGH,VERY_HIGH&hazard_relevant=trueSee property filters for all of them.
Generate a typed client
The OpenAPI schema works with code generators such as OpenAPI Generator or openapi-python-client. Many generators send Authorization: Bearer by default. Configure them to send the key in the X-API-Key header instead, with no prefix.

