An HTTP API and tiny web component that act as a stable proxy in front of Bremen’s official waste collection calendar (Bremer Abfallkalender). The official service has no public, stable API. Instead, it serves data under a dynamic, time‑varying base URL. This project discovers that dynamic URL at runtime and exposes a minimal, predictable API on top.
Key capabilities:
- List all streets known to the official calendar
- List all house numbers for a given street
- Fetch the pickup calendar for a given street and house number (as ICS or CSV)
- Compute and return the next upcoming collection day and its waste types (JSON)
- Serve Prometheus metrics
- Serve a lightweight frontend web component (
kalender.js)
Use cases:
- Integrate Bremen waste pickup schedules into home automation (Home Assistant, Node‑RED, etc.)
- Generate personal reminders (calendar subscriptions, notifications)
- Build dashboards without depending on hidden or unstable upstream URLs
The official Bremen waste calendar is implemented as a web app without a stable public API. Its base path changes via a redirect mechanism. This app stabilizes access by:
- Discovering the current dynamic base URL of the official service
- Calling the upstream JSON/ICS/CSV endpoints
- Normalizing and returning responses via a small, well‑documented API
This avoids hard‑coding brittle upstream URLs in clients while enabling clean integrations.
- On incoming requests, the backend asks the official service for the current base URL (a HEAD request reveals a
Locationheader). See/misc/example/example-official-requests.httpfor details. - Using that discovered base URL, the app queries upstream endpoints for streets, house numbers, and calendar files (ICS/CSV).
- It returns structured data (HAL+JSON) or passes through calendar files depending on the route and the
Acceptheader. - For the "next" endpoint, it parses the upstream CSV, finds the nearest future date, and classifies the waste types for that day.
No data is stored persistently; responses reflect current upstream content.
Base path: your deployment domain. Examples below assume https://your.host.
-
GET
/and/abfallkalender-api- Returns the OpenAPI 3 specification (YAML) of this service.
-
GET
/abfallkalender-api/streets- Lists all available streets in Bremen.
- Response:
application/json(HAL style) - Example snippet:
{ "_embedded": { "streets": [ { "name": "Aachener Straße", "_links": {"self": {"href": "https://your.host/abfallkalender-api/street/Aachener%20Stra%C3%9Fe"}} } ] } }
-
GET
/abfallkalender-api/street/{street}- Returns the street and all available house numbers.
- Response:
application/json(HAL style) - Path parameter
streetmust match the official spelling (URL‑encode umlauts/ß). - Each house number contains minimal links to keep the payload small:
_links.self→ calendar resource (default ICS in browsers; CSV viaAccept: text/csv)_links.next→ JSON with the next collection day
- Example snippet:
{ "name": "Aachener Straße", "houseNumbers": [ { "number": "22", "_links": { "self": {"href": "https://your.host/abfallkalender-api/street/Aachener%20Stra%C3%9Fe/number/22"}, "next": {"href": "https://your.host/abfallkalender-api/street/Aachener%20Stra%C3%9Fe/number/22/next"} } } ], "_links": {"self": {"href": "https://your.host/abfallkalender-api/street/Aachener%20Stra%C3%9Fe"}} }
-
GET
/abfallkalender-api/street/{street}/number/{number}- Returns the pickup calendar for the address.
- Content depends on the
Acceptheader:Accept: text/html→ lightweight HTML preview showing the ICS content inline (no download); includes a link to…/nextAccept: text/calendar→ upstream ICS contentAccept: text/csv→ upstream CSV content- No
Acceptheader → ICS by default (for CLI/cURL compatibility)
-
GET
/abfallkalender-api/street/{street}/number/{number}/next- Returns the next upcoming collection day and the detected waste types.
- Response (JSON):
{ "day_of_collection": "2025-01-15", "garbage_types": ["yellow", "blue"] } - Possible waste types:
yellow,blue,brown,black,christmas.
-
GET
/metrics- Exposes Prometheus metrics (
http_requests_total,http_request_duration_seconds).
- Exposes Prometheus metrics (
-
GET
/kalender.jsand/kalender.js.map- Serves a small web component that can render a calendar widget in the browser.
The full OpenAPI description lives in open-api-3.yaml and is served by the app at / and /abfallkalender-api.
docker build -t abfallkalender-api .
docker run --rm -p 8080:8080 -e PORT=8080 abfallkalender-apiYour API is now available at http://localhost:8080.
make run
# or
go run ./...The server listens on :${PORT} (defaults to 8080).
# All streets
curl -s https://your.host/abfallkalender-api/streets | jq .
# Street details incl. house numbers
curl -s "https://your.host/abfallkalender-api/street/Aachener%20Stra%C3%9Fe" | jq .
# ICS calendar
curl -s -H "Accept: text/calendar" \
"https://your.host/abfallkalender-api/street/Aachener%20Stra%C3%9Fe/number/22"
# CSV calendar
curl -s -H "Accept: text/csv" \
"https://your.host/abfallkalender-api/street/Aachener%20Stra%C3%9Fe/number/22"
# Next pickup
curl -s "https://your.host/abfallkalender-api/street/Aachener%20Stra%C3%9Fe/number/22/next" | jq .Developer‑friendly HTTP files for IDE clients are available under misc/example.
Prometheus metrics are exposed at /metrics and already instrumented with request count and latency histograms. Add your Prometheus scrape config accordingly.
- Upstream dependency: The app depends on Bremen’s official service being available. If the upstream format changes, this proxy may require updates.
- Exact spelling: Street and house number must match the upstream data. Use URL encoding for special characters (e.g.,
Straße→Stra%C3%9Fe). - City scope: This project focuses on the city of Bremen.
- No persistence: Data is not cached permanently; every request reflects upstream responses.
- Formats: ICS and CSV are passed through from the official service; JSON is produced by this proxy for directory endpoints and the
nextcomputation.
Apache License 2.0 — see LICENSE.