The 76th Festival di Sanremo will take place from February 24 to February 28,
Saturday, Feb 28, 2026
Bluesky
now
Lys
🚨5 MINUTES REMINDER!
Bluesky
now
Lys
TONIGHT | 3 selection shows across Europe!
A Eurovision-flavored bot
Lys is a bot that publishes scheduled reminders about upcoming Eurovision national selection shows to social media. The reminders include the show's date and time, as well as links to watch it.
Launched on Twitter back in 2019, Lys has since found its way to Bluesky and Threads.
At the core of the app is the bot itself. Lambdas, triggered on a schedule defined through CloudWatch Cron Triggers, read events from a DynamoDB table, then build & publish posts to social media via their public APIs.
The Python code base is modular and makes it easy to support new platforms and customize posts.
Once live, maintaining the calendar table in DynamoDB turned out to be the most time consuming.
A nightly Python script parses the Eurovoix RSS feed to extract show dates and compile them into meaningful suggestions to review manually in the management app.
Once accepted, suggestions are enriched with static data to generate events.
Eurovoix
Jun 26, 2025
🇪🇸 Spain: Benidorm Fest 2026 Dates & Artistic Director Announced - Eurovoix
Spanish broadcaster RTVE has revealed that the Grand Final of Benidorm Fest 2026 will be held on February 14.
Sep 1, 2025
Feb 10, 2026
Feb 12, 2026
Feb 14, 2026
[...] Final of Benidorm Fest 2026 will be held on February 14
I got the idea for this project shortly after moving to Sarajevo. The city is served by a competent transport network, but it sadly lacks a centralized app that provides all sorts of information (line routes, departures, etc.).
Since I was looking for an excuse to try my hand at Node.js backend development, I thought I'd give making such an app a go.
Not only is there no centralized source of data, sometimes the data is at best hard to find, at worst inexistent, and most often inaccurate.
Finding even the list of stops for a line turned out to be a challenge. I ended up focusing on the main lines (so I could at least write a bit of code!)
Although the main focus of this project was the Node.js backend, I couldn't help using my recently acquired Next.js knowledge to build a small web app to display all that exposed data.
It turned out to be a good idea: there's no better way to design a good API than being one of its users. I ended up re-designing my endpoints more than once, because my initial approach didn't turn out very practical in the end.
The first endpoints I implemented are to describe lines and the routes they take.
This one, describe-line, returns the type of the line, its directions, and all stops alongside each direction - along with the available connections at that stop.
{"name":"3","type":"tram","directions":["Ilidža","Baščaršija"],"routes":[{"direction":"Ilidža","stops":[{"id":24,"name":"Baščaršija","connections":[{"line":"200E","type":"bus","directions":["Međunarodni Aerodrom Sarajevo"]},{"line":"1","type":"tram","directions":["Baščaršija","Željeznička Stanica"]},{"line":"2","type":"tram","directions":["Čengić Vila","Baščaršija"]},{"line":"5","type":"tram","directions":["Nedžarići","Baščaršija"]}]},{"id":25,"name":"Katedrala","connections":[{"line":"200E","type":"bus","directions":["Međunarodni Aerodrom Sarajevo"]},{"line":"31E","type":"bus","directions":["Dobrinja"]},{"line":"1","type":"tram","directions":["Željeznička Stanica"]},{"line":"2","type":"tram","directions":["Čengić Vila"]},{"line":"5","type":"tram","directions":["Nedžarići"]}]},{"id":26,"name":"Banka","connections":[{"line":"200E","type":"bus","directions":["Međunarodni Aerodrom Sarajevo"]},{"line":"31E","type":"bus","directions":["Dobrinja"]},{"line":"1","type":"tram","directions":["Željeznička Stanica"]},{"line":"2","type":"tram","directions":["Čengić Vila"]},{"line":"5","type":"tram","directions":["Nedžarići"]}]},{"id":27,"name":"Park","connections":[{"line":"200E","type":"bus","directions":["Međunarodni Aerodrom Sarajevo"]},{"line":"31E","type":"bus","directions":["Dobrinja"]},{"line":"1","type":"tram","directions":["Željeznička Stanica"]},{"line":"2","type":"tram","directions":["Čengić Vila"]},{"line":"5","type":"tram","directions":["Nedžarići"]}]},{"id":18,"name":"Marijin Dvor","connections":[{"line":"200E","type":"bus","directions":["Međunarodni Aerodrom Sarajevo","Bentbaša"]},{"line":"31E","type":"bus","directions":["Dobrinja","Vijećnica"]},{"line":"1","type":"tram","directions":["Baščaršija","Željeznička Stanica"]},{"line":"2","type":"tram","directions":["Čengić Vila","Baščaršija"]},{"line":"5","type":"tram","directions":["Nedžarići","Baščaršija"]},{"line":"6","type":"tram","directions":["Skenderija","Ilidža"]}]},{"id":70,"name":"Tehnička Škola","connections":[{"line":"31E","type":"bus","directions":["Dobrinja"]},{"line":"1","type":"tram","directions":["Željeznička Stanica"]},{"line":"2","type":"tram","directions":["Čengić Vila"]},{"line":"4","type":"tram","directions":["Ilidža"]},{"line":"5","type":"tram","directions":["Nedžarići"]},{"line":"6","type":"tram","directions":["Ilidža"]}]},{"id":16,"name":"Univerzitet","connections":[{"line":"200E","type":"bus","directions":["Bentbaša","Međunarodni Aerodrom Sarajevo"]},{"line":"31E","type":"bus","directions":["Dobrinja","Vijećnica"]},{"line":"2","type":"tram","directions":["Čengić Vila","Baščaršija"]},{"line":"4","type":"tram","directions":["Ilidža","Željeznička Stanica"]},{"line":"5","type":"tram","directions":["Nedžarići","Baščaršija"]},{"line":"6","type":"tram","directions":["Skenderija","Ilidža"]}]},{"id":15,"name":"Pofalići","connections":[{"line":"200E","type":"bus","directions":["Bentbaša","Međunarodni Aerodrom Sarajevo"]},{"line":"31E","type":"bus","directions":["Dobrinja","Vijećnica"]},{"line":"2","type":"tram","directions":["Čengić Vila","Baščaršija"]},{"line":"4","type":"tram","directions":["Ilidža","Željeznička Stanica"]},{"line":"5","type":"tram","directions":["Nedžarići","Baščaršija"]},{"line":"6","type":"tram","directions":["Skenderija","Ilidža"]}]},{"id":14,"name":"Socijalno","connections":[{"line":"200E","type":"bus","directions":["Bentbaša","Međunarodni Aerodrom Sarajevo"]},{"line":"31E","type":"bus","directions":["Dobrinja","Vijećnica"]},{"line":"2","type":"tram","directions":["Čengić Vila","Baščaršija"]},{"line":"4","type":"tram","directions":["Ilidža","Željeznička Stanica"]},{"line":"5","type":"tram","directions":["Nedžarići","Baščaršija"]},{"line":"6","type":"tram","directions":["Skenderija","Ilidža"]}]},{"id":13,"name":"Dolac Malta","connections":[{"line":"200E","type":"bus","directions":["Bentbaša","Međunarodni Aerodrom Sarajevo"]},{"line":"31E","type":"bus","directions":["Dobrinja","Vijećnica"]},{"line":"2","type":"tram","directions":["Čengić Vila","Baščaršija"]},{"line":"4","type":"tram","directions":["Ilidža","Željeznička Stanica"]},{"line":"5","type":"tram","directions":["Nedžarići","Baščaršija"]},{"line":"6","type":"tram","directions":["Skenderija","Ilidža"]}]},{"id":12,"name":"Čengić Vila","connections":[{"line":"200E","type":"bus","directions":["Bentbaša","Međunarodni Aerodrom Sarajevo"]},{"line":"31E","type":"bus","directions":["Dobrinja","Vijećnica"]},{"line":"2","type":"tram","directions":["Čengić Vila","Baščaršija"]},{"line":"4","type":"tram","directions":["Ilidža","Željeznička Stanica"]},{"line":"5","type":"tram","directions":["Nedžarići","Baščaršija"]},{"line":"6","type":"tram","directions":["Skenderija","Ilidža"]}]},{"id":11,"name":"Otoka","connections":[{"line":"200E","type":"bus","directions":["Bentbaša","Međunarodni Aerodrom Sarajevo"]},{"line":"31E","type":"bus","directions":["Dobrinja","Vijećnica"]},{"line":"4","type":"tram","directions":["Ilidža","Željeznička Stanica"]},{"line":"5","type":"tram","directions":["Nedžarići","Baščaršija"]},{"line":"6","type":"tram","directions":["Skenderija","Ilidža"]},{"line":"101","type":"trolleybus","directions":["Trg Austrije","Otoka"]},{"line":"102","type":"trolleybus","directions":["Jezero","Otoka"]},{"line":"108","type":"trolleybus","directions":["Dobrinja","Otoka"]}]},{"id":10,"name":"Alipašin Most","connections":[{"line":"200E","type":"bus","directions":["Bentbaša","Međunarodni Aerodrom Sarajevo"]},{"line":"31E","type":"bus","directions":["Dobrinja","Vijećnica"]},{"line":"4","type":"tram","directions":["Ilidža","Željeznička Stanica"]},{"line":"5","type":"tram","directions":["Nedžarići","Baščaršija"]},{"line":"6","type":"tram","directions":["Skenderija","Ilidža"]}]},{"id":9,"name":"RTV","connections":[{"line":"200E","type":"bus","directions":["Bentbaša","Međunarodni Aerodrom Sarajevo"]},{"line":"31E","type":"bus","directions":["Dobrinja","Vijećnica"]},{"line":"4","type":"tram","directions":["Ilidža","Željeznička Stanica"]},{"line":"5","type":"tram","directions":["Nedžarići","Baščaršija"]},{"line":"6","type":"tram","directions":["Skenderija","Ilidža"]}]},{"id":8,"name":"Alipašino Polje","connections":[{"line":"200E","type":"bus","directions":["Međunarodni Aerodrom Sarajevo","Bentbaša"]},{"line":"31E","type":"bus","directions":["Dobrinja","Vijećnica"]},{"line":"4","type":"tram","directions":["Ilidža","Željeznička Stanica"]},{"line":"5","type":"tram","directions":["Nedžarići","Baščaršija"]},{"line":"6","type":"tram","directions":["Skenderija","Ilidža"]}]},{"id":7,"name":"Nedžarići","connections":[{"line":"200E","type":"bus","directions":["Bentbaša","Međunarodni Aerodrom Sarajevo"]},{"line":"31E","type":"bus","directions":["Dobrinja","Vijećnica"]},{"line":"4","type":"tram","directions":["Ilidža","Željeznička Stanica"]},{"line":"5","type":"tram","directions":["Nedžarići","Baščaršija"]},{"line":"6","type":"tram","directions":["Skenderija","Ilidža"]}]},{"id":6,"name":"Avaz","connections":[{"line":"200E","type":"bus","directions":["Bentbaša","Međunarodni Aerodrom Sarajevo"]},{"line":"4","type":"tram","directions":["Ilidža","Željeznička Stanica"]},{"line":"6","type":"tram","directions":["Skenderija","Ilidža"]}]},{"id":5,"name":"Stup","connections":[{"line":"4","type":"tram","directions":["Ilidža","Željeznička Stanica"]},{"line":"6","type":"tram","directions":["Skenderija","Ilidža"]}]},{"id":3,"name":"Energoinvest","connections":[{"line":"4","type":"tram","directions":["Ilidža","Željeznička Stanica"]},{"line":"6","type":"tram","directions":["Skenderija","Ilidža"]}]},{"id":2,"name":"Kasindolska","connections":[{"line":"4","type":"tram","directions":["Ilidža","Željeznička Stanica"]},{"line":"6","type":"tram","directions":["Skenderija","Ilidža"]}]},{"id":1,"name":"Ilidža","connections":[{"line":"4","type":"tram","directions":["Ilidža","Željeznička Stanica"]},{"line":"6","type":"tram","directions":["Skenderija","Ilidža"]}]}]},{"direction":"Baščaršija","stops":[{"id":1,"name":"Ilidža","connections":[{"line":"4","type":"tram","directions":["Ilidža","Željeznička Stanica"]},{"line":"6","type":"tram","directions":["Skenderija","Ilidža"]}]},{"id":2,"name":"Kasindolska","connections":[{"line":"4","type":"tram","directions":["Ilidža","Željeznička Stanica"]},{"line":"6","type":"tram","directions":["Skenderija","Ilidža"]}]},{"id":3,"name":"Energoinvest","connections":[{"line":"4","type":"tram","directions":["Ilidža","Željeznička Stanica"]},{"line":"6","type":"tram","directions":["Skenderija","Ilidža"]}]},{"id":5,"name":"Stup","connections":[{"line":"4","type":"tram","directions":["Ilidža","Željeznička Stanica"]},{"line":"6","type":"tram","directions":["Skenderija","Ilidža"]}]},{"id":6,"name":"Avaz","connections":[{"line":"200E","type":"bus","directions":["Bentbaša","Međunarodni Aerodrom Sarajevo"]},{"line":"4","type":"tram","directions":["Ilidža","Željeznička Stanica"]},{"line":"6","type":"tram","directions":["Skenderija","Ilidža"]}]},{"id":7,"name":"Nedžarići","connections":[{"line":"200E","type":"bus","directions":["Bentbaša","Međunarodni Aerodrom Sarajevo"]},{"line":"31E","type":"bus","directions":["Dobrinja","Vijećnica"]},{"line":"4","type":"tram","directions":["Ilidža","Željeznička Stanica"]},{"line":"5","type":"tram","directions":["Nedžarići","Baščaršija"]},{"line":"6","type":"tram","directions":["Skenderija","Ilidža"]}]},{"id":8,"name":"Alipašino Polje","connections":[{"line":"200E","type":"bus","directions":["Međunarodni Aerodrom Sarajevo","Bentbaša"]},{"line":"31E","type":"bus","directions":["Dobrinja","Vijećnica"]},{"line":"4","type":"tram","directions":["Ilidža","Željeznička Stanica"]},{"line":"5","type":"tram","directions":["Nedžarići","Baščaršija"]},{"line":"6","type":"tram","directions":["Skenderija","Ilidža"]}]},{"id":9,"name":"RTV","connections":[{"line":"200E","type":"bus","directions":["Bentbaša","Međunarodni Aerodrom Sarajevo"]},{"line":"31E","type":"bus","directions":["Dobrinja","Vijećnica"]},{"line":"4","type":"tram","directions":["Ilidža","Željeznička Stanica"]},{"line":"5","type":"tram","directions":["Nedžarići","Baščaršija"]},{"line":"6","type":"tram","directions":["Skenderija","Ilidža"]}]},{"id":10,"name":"Alipašin Most","connections":[{"line":"200E","type":"bus","directions":["Bentbaša","Međunarodni Aerodrom Sarajevo"]},{"line":"31E","type":"bus","directions":["Dobrinja","Vijećnica"]},{"line":"4","type":"tram","directions":["Ilidža","Željeznička Stanica"]},{"line":"5","type":"tram","directions":["Nedžarići","Baščaršija"]},{"line":"6","type":"tram","directions":["Skenderija","Ilidža"]}]},{"id":11,"name":"Otoka","connections":[{"line":"200E","type":"bus","directions":["Bentbaša","Međunarodni Aerodrom Sarajevo"]},{"line":"31E","type":"bus","directions":["Dobrinja","Vijećnica"]},{"line":"4","type":"tram","directions":["Ilidža","Željeznička Stanica"]},{"line":"5","type":"tram","directions":["Nedžarići","Baščaršija"]},{"line":"6","type":"tram","directions":["Skenderija","Ilidža"]},{"line":"101","type":"trolleybus","directions":["Trg Austrije","Otoka"]},{"line":"102","type":"trolleybus","directions":["Jezero","Otoka"]},{"line":"108","type":"trolleybus","directions":["Dobrinja","Otoka"]}]},{"id":12,"name":"Čengić Vila","connections":[{"line":"200E","type":"bus","directions":["Bentbaša","Međunarodni Aerodrom Sarajevo"]},{"line":"31E","type":"bus","directions":["Dobrinja","Vijećnica"]},{"line":"2","type":"tram","directions":["Čengić Vila","Baščaršija"]},{"line":"4","type":"tram","directions":["Ilidža","Željeznička Stanica"]},{"line":"5","type":"tram","directions":["Nedžarići","Baščaršija"]},{"line":"6","type":"tram","directions":["Skenderija","Ilidža"]}]},{"id":13,"name":"Dolac Malta","connections":[{"line":"200E","type":"bus","directions":["Bentbaša","Međunarodni Aerodrom Sarajevo"]},{"line":"31E","type":"bus","directions":["Dobrinja","Vijećnica"]},{"line":"2","type":"tram","directions":["Čengić Vila","Baščaršija"]},{"line":"4","type":"tram","directions":["Ilidža","Željeznička Stanica"]},{"line":"5","type":"tram","directions":["Nedžarići","Baščaršija"]},{"line":"6","type":"tram","directions":["Skenderija","Ilidža"]}]},{"id":14,"name":"Socijalno","connections":[{"line":"200E","type":"bus","directions":["Bentbaša","Međunarodni Aerodrom Sarajevo"]},{"line":"31E","type":"bus","directions":["Dobrinja","Vijećnica"]},{"line":"2","type":"tram","directions":["Čengić Vila","Baščaršija"]},{"line":"4","type":"tram","directions":["Ilidža","Željeznička Stanica"]},{"line":"5","type":"tram","directions":["Nedžarići","Baščaršija"]},{"line":"6","type":"tram","directions":["Skenderija","Ilidža"]}]},{"id":15,"name":"Pofalići","connections":[{"line":"200E","type":"bus","directions":["Bentbaša","Međunarodni Aerodrom Sarajevo"]},{"line":"31E","type":"bus","directions":["Dobrinja","Vijećnica"]},{"line":"2","type":"tram","directions":["Čengić Vila","Baščaršija"]},{"line":"4","type":"tram","directions":["Ilidža","Željeznička Stanica"]},{"line":"5","type":"tram","directions":["Nedžarići","Baščaršija"]},{"line":"6","type":"tram","directions":["Skenderija","Ilidža"]}]},{"id":16,"name":"Univerzitet","connections":[{"line":"200E","type":"bus","directions":["Bentbaša","Međunarodni Aerodrom Sarajevo"]},{"line":"31E","type":"bus","directions":["Dobrinja","Vijećnica"]},{"line":"2","type":"tram","directions":["Čengić Vila","Baščaršija"]},{"line":"4","type":"tram","directions":["Ilidža","Željeznička Stanica"]},{"line":"5","type":"tram","directions":["Nedžarići","Baščaršija"]},{"line":"6","type":"tram","directions":["Skenderija","Ilidža"]}]},{"id":17,"name":"Muzeji","connections":[{"line":"200E","type":"bus","directions":["Bentbaša"]},{"line":"31E","type":"bus","directions":["Vijećnica"]},{"line":"1","type":"tram","directions":["Baščaršija"]},{"line":"2","type":"tram","directions":["Baščaršija"]},{"line":"4","type":"tram","directions":["Željeznička Stanica"]},{"line":"5","type":"tram","directions":["Baščaršija"]},{"line":"6","type":"tram","directions":["Skenderija"]}]},{"id":18,"name":"Marijin Dvor","connections":[{"line":"200E","type":"bus","directions":["Međunarodni Aerodrom Sarajevo","Bentbaša"]},{"line":"31E","type":"bus","directions":["Dobrinja","Vijećnica"]},{"line":"1","type":"tram","directions":["Baščaršija","Željeznička Stanica"]},{"line":"2","type":"tram","directions":["Čengić Vila","Baščaršija"]},{"line":"5","type":"tram","directions":["Nedžarići","Baščaršija"]},{"line":"6","type":"tram","directions":["Skenderija","Ilidža"]}]},{"id":19,"name":"Skenderija","connections":[{"line":"200E","type":"bus","directions":["Bentbaša"]},{"line":"31E","type":"bus","directions":["Vijećnica"]},{"line":"1","type":"tram","directions":["Baščaršija"]},{"line":"2","type":"tram","directions":["Baščaršija"]},{"line":"5","type":"tram","directions":["Baščaršija"]},{"line":"6","type":"tram","directions":["Skenderija","Ilidža"]},{"line":"101","type":"trolleybus","directions":["Trg Austrije","Otoka"]},{"line":"102","type":"trolleybus","directions":["Otoka"]},{"line":"103","type":"trolleybus","directions":["Trg Austrije","Dobrinja"]},{"line":"105","type":"trolleybus","directions":["Trg Austrije"]},{"line":"107","type":"trolleybus","directions":["Dobrinja"]}]},{"id":20,"name":"Pošta","connections":[{"line":"200E","type":"bus","directions":["Bentbaša"]},{"line":"31E","type":"bus","directions":["Vijećnica"]},{"line":"1","type":"tram","directions":["Baščaršija"]},{"line":"2","type":"tram","directions":["Baščaršija"]},{"line":"5","type":"tram","directions":["Baščaršija"]}]},{"id":21,"name":"Drvenija","connections":[{"line":"200E","type":"bus","directions":["Bentbaša"]},{"line":"31E","type":"bus","directions":["Vijećnica"]},{"line":"1","type":"tram","directions":["Baščaršija"]},{"line":"2","type":"tram","directions":["Baščaršija"]},{"line":"5","type":"tram","directions":["Baščaršija"]},{"line":"101","type":"trolleybus","directions":["Trg Austrije","Otoka"]},{"line":"103","type":"trolleybus","directions":["Trg Austrije","Dobrinja"]},{"line":"105","type":"trolleybus","directions":["Trg Austrije","Vogošća Terminal"]}]},{"id":22,"name":"Latinska Ćuprija","connections":[{"line":"200E","type":"bus","directions":["Bentbaša"]},{"line":"31E","type":"bus","directions":["Vijećnica"]},{"line":"1","type":"tram","directions":["Baščaršija"]},{"line":"2","type":"tram","directions":["Baščaršija"]},{"line":"5","type":"tram","directions":["Baščaršija"]}]},{"id":23,"name":"Vijećnica","connections":[{"line":"200E","type":"bus","directions":["Bentbaša"]},{"line":"1","type":"tram","directions":["Baščaršija"]},{"line":"2","type":"tram","directions":["Baščaršija"]},{"line":"5","type":"tram","directions":["Baščaršija"]}]},{"id":24,"name":"Baščaršija","connections":[{"line":"200E","type":"bus","directions":["Međunarodni Aerodrom Sarajevo"]},{"line":"1","type":"tram","directions":["Baščaršija","Željeznička Stanica"]},{"line":"2","type":"tram","directions":["Čengić Vila","Baščaršija"]},{"line":"5","type":"tram","directions":["Nedžarići","Baščaršija"]}]}]}]}
The /departures/next endpoint returns the desired number of upcoming departures at a stop, per line and direction, for the remainder of the day. It also offers the possibility to filter by line and direction.
The output includes a list of lines served at the stop, as well as some extra decoration data (line type, directions...) for display purposes.
{"stop":{"id":11,"name":"Otoka","connections":[{"line":"200E","type":"bus","directions":["Bentbaša","Međunarodni Aerodrom Sarajevo"]},{"line":"31E","type":"bus","directions":["Dobrinja","Vijećnica"]},{"line":"3","type":"tram","directions":["Baščaršija","Ilidža"]},{"line":"4","type":"tram","directions":["Ilidža","Željeznička Stanica"]},{"line":"5","type":"tram","directions":["Nedžarići","Baščaršija"]},{"line":"6","type":"tram","directions":["Skenderija","Ilidža"]},{"line":"101","type":"trolleybus","directions":["Trg Austrije","Otoka"]},{"line":"102","type":"trolleybus","directions":["Jezero","Otoka"]},{"line":"108","type":"trolleybus","directions":["Dobrinja","Otoka"]}]},"departures":{"3":{"type":"tram","departures":{"Baščaršija":[{"scheduledAt":"2025-07-28T09:42:00.000Z"},{"scheduledAt":"2025-07-28T09:46:00.000Z"},{"scheduledAt":"2025-07-28T09:50:00.000Z"},{"scheduledAt":"2025-07-28T09:54:00.000Z"},{"scheduledAt":"2025-07-28T09:58:00.000Z"}],"Ilidža":[{"scheduledAt":"2025-07-28T09:39:00.000Z"},{"scheduledAt":"2025-07-28T09:43:00.000Z"},{"scheduledAt":"2025-07-28T09:47:00.000Z"},{"scheduledAt":"2025-07-28T09:51:00.000Z"},{"scheduledAt":"2025-07-28T09:55:00.000Z"}]}},"4":{"type":"tram","departures":{"Ilidža":[{"scheduledAt":"2025-07-28T09:44:00.000Z"},{"scheduledAt":"2025-07-28T10:17:00.000Z"},{"scheduledAt":"2025-07-28T10:50:00.000Z"},{"scheduledAt":"2025-07-28T11:23:00.000Z"},{"scheduledAt":"2025-07-28T11:56:00.000Z"}],"Željeznička Stanica":[{"scheduledAt":"2025-07-28T09:44:00.000Z"},{"scheduledAt":"2025-07-28T10:17:00.000Z"},{"scheduledAt":"2025-07-28T10:50:00.000Z"},{"scheduledAt":"2025-07-28T11:23:00.000Z"},{"scheduledAt":"2025-07-28T11:56:00.000Z"}]}},"5":{"type":"tram","departures":{"Nedžarići":[{"scheduledAt":"2025-07-28T11:09:00.000Z"},{"scheduledAt":"2025-07-28T11:28:00.000Z"},{"scheduledAt":"2025-07-28T11:47:00.000Z"},{"scheduledAt":"2025-07-28T12:06:00.000Z"},{"scheduledAt":"2025-07-28T12:25:00.000Z"}],"Baščaršija":[{"scheduledAt":"2025-07-28T10:50:00.000Z"},{"scheduledAt":"2025-07-28T11:09:00.000Z"},{"scheduledAt":"2025-07-28T11:28:00.000Z"},{"scheduledAt":"2025-07-28T11:47:00.000Z"},{"scheduledAt":"2025-07-28T12:06:00.000Z"}]}},"6":{"type":"tram","departures":{"Skenderija":[{"scheduledAt":"2025-07-28T09:45:00.000Z"},{"scheduledAt":"2025-07-28T10:02:00.000Z"},{"scheduledAt":"2025-07-28T10:20:00.000Z"},{"scheduledAt":"2025-07-28T10:37:00.000Z"},{"scheduledAt":"2025-07-28T10:55:00.000Z"}],"Ilidža":[{"scheduledAt":"2025-07-28T09:45:00.000Z"},{"scheduledAt":"2025-07-28T10:02:00.000Z"},{"scheduledAt":"2025-07-28T10:20:00.000Z"},{"scheduledAt":"2025-07-28T10:37:00.000Z"},{"scheduledAt":"2025-07-28T10:55:00.000Z"}]}},"101":{"type":"trolleybus","departures":{"Trg Austrije":[]}},"102":{"type":"trolleybus","departures":{"Jezero":[{"scheduledAt":"2025-07-28T09:39:00.000Z"},{"scheduledAt":"2025-07-28T09:53:00.000Z"},{"scheduledAt":"2025-07-28T10:08:00.000Z"},{"scheduledAt":"2025-07-28T10:20:00.000Z"},{"scheduledAt":"2025-07-28T10:36:00.000Z"}]}},"108":{"type":"trolleybus","departures":{"Dobrinja":[{"scheduledAt":"2025-07-28T10:15:00.000Z"},{"scheduledAt":"2025-07-28T11:25:00.000Z"},{"scheduledAt":"2025-07-28T12:35:00.000Z"},{"scheduledAt":"2025-07-28T13:45:00.000Z"},{"scheduledAt":"2025-07-28T14:55:00.000Z"}]}},"31E":{"type":"bus","departures":{"Dobrinja":[],"Vijećnica":[]}},"200E":{"type":"bus","departures":{"Bentbaša":[{"scheduledAt":"2025-07-28T10:20:00.000Z"},{"scheduledAt":"2025-07-28T11:40:00.000Z"},{"scheduledAt":"2025-07-28T13:00:00.000Z"},{"scheduledAt":"2025-07-28T14:25:00.000Z"},{"scheduledAt":"2025-07-28T15:00:00.000Z"}],"Međunarodni Aerodrom Sarajevo":[{"scheduledAt":"2025-07-28T09:44:00.000Z"},{"scheduledAt":"2025-07-28T11:04:00.000Z"},{"scheduledAt":"2025-07-28T12:24:00.000Z"},{"scheduledAt":"2025-07-28T13:44:00.000Z"},{"scheduledAt":"2025-07-28T15:09:00.000Z"}]}}}}
No no, you're not seeing double - the /departures/scheduled endpoint looks exactly like its /departures/next neighbour, but this one is meant to expose scheduled departures - while /next would (for an ideal network that publicly exposes real-time data) return live data.
The logo of the 2021 Eurovision Song Contest was algorithmically designed by CLEVER ° FRANKE. It represents the distance between the host city, Rotterdam, and the capital of each participating country.
I found this ridiculously cool and clever, and wanted to see if it could be programatically recreated.
The goal of this project is to generate alternative 2021 logos, using any city as the center.
For that, we first need to compute angles and distances between the selected city and all others - which we can do using GPS coordinates.
We then group cities by angle ranges - or "slices". For example, from Lisbon, Portugal we can group Brussels, Amsterdam, and Oslo within a single slice at ~40°.