Find the cheapest hours
With an hourly power fee, the grid fee depends on when you use electricity. If you can move consumption, such as EV charging, a heat pump or a water heater, you can save money by running it in the hours with the lowest price.
This guide fetches the hourly prices for a tariff and picks the cheapest upcoming hours.
How it works
- Fetch the prices with
GET /prices?product={product}. - Keep the hours in
actualthat haven't ended yet. These prices are set and won't change. - Sort them by price and take as many hours as you need.
Prices for the next day are set every day by 15:00 at the latest. If you run the code after that, the result includes tomorrow's hours.
Code
- Python
- JavaScript
- C#
- curl
cheapest_hours.py
"""Find the cheapest upcoming hours for a tariff with an hourly power fee.
Requires Python 3.9 or later. Run with: python cheapest_hours.py
"""
import json
import urllib.parse
import urllib.request
from datetime import datetime, timezone
BASE_URL = "https://api.uat.goteborgenergi.cloud/gridtariff/v0"
PRODUCT = "GN6KWST"
HOURS_NEEDED = 4
# Name your application, so it can be told apart from other clients.
USER_AGENT = "grid-tariff-example/1.0"
url = f"{BASE_URL}/prices?product={urllib.parse.quote(PRODUCT)}"
request = urllib.request.Request(url, headers={"User-Agent": USER_AGENT})
with urllib.request.urlopen(request) as response:
data = json.load(response)
# Only hours that haven't ended yet
now = datetime.now(timezone.utc)
upcoming = [h for h in data["actual"] if datetime.fromisoformat(h["end"]) > now]
cheapest = sorted(upcoming, key=lambda h: h["priceIncVat"])[:HOURS_NEEDED]
cheapest.sort(key=lambda h: datetime.fromisoformat(h["start"]))
print(f"The {len(cheapest)} cheapest upcoming hours:")
for hour in cheapest:
start = hour["start"][:16].replace("T", " ")
end = hour["end"][11:16]
# On the night the clocks change, an hour starts and ends with different
# UTC offsets. Show them, or October's extra hour would read 02:00–02:00.
start_offset = hour["start"][19:]
end_offset = hour["end"][19:]
if start_offset != end_offset:
start += f" ({start_offset})"
end += f" ({end_offset})"
print(f"{start}–{end} {hour['priceIncVat']} {data['currency']}/{data['unit']} incl. VAT")
cheapest-hours.mjs
// Find the cheapest upcoming hours for a tariff with an hourly power fee.
// Requires Node.js 20 or later. Run with: node cheapest-hours.mjs
const BASE_URL = 'https://api.uat.goteborgenergi.cloud/gridtariff/v0';
const PRODUCT = 'GN6KWST';
const HOURS_NEEDED = 4;
const response = await fetch(
`${BASE_URL}/prices?product=${encodeURIComponent(PRODUCT)}`,
);
if (!response.ok) {
throw new Error(`Request failed: ${response.status} ${await response.text()}`);
}
const {currency, unit, actual} = await response.json();
// Only hours that haven't ended yet
const now = new Date();
const upcoming = actual.filter((hour) => new Date(hour.end) > now);
const cheapest = upcoming
.toSorted((a, b) => a.priceIncVat - b.priceIncVat)
.slice(0, HOURS_NEEDED)
.toSorted((a, b) => new Date(a.start) - new Date(b.start));
console.log(`The ${cheapest.length} cheapest upcoming hours:`);
for (const hour of cheapest) {
let from = hour.start.slice(0, 16).replace('T', ' ');
let to = hour.end.slice(11, 16);
// On the night the clocks change, an hour starts and ends with different
// UTC offsets. Show them, or October's extra hour would read 02:00–02:00.
const startOffset = hour.start.slice(19);
const endOffset = hour.end.slice(19);
if (startOffset !== endOffset) {
from += ` (${startOffset})`;
to += ` (${endOffset})`;
}
console.log(`${from}–${to} ${hour.priceIncVat} ${currency}/${unit} incl. VAT`);
}
Program.cs
// Find the cheapest upcoming hours for a tariff with an hourly power fee.
// Requires .NET 8 or later. Put this in Program.cs of a console project
// (dotnet new console) and run with: dotnet run
using System.Net.Http.Json;
const string BaseUrl = "https://api.uat.goteborgenergi.cloud/gridtariff/v0";
const string Product = "GN6KWST";
const int HoursNeeded = 4;
using var http = new HttpClient();
var prices = await http.GetFromJsonAsync<PricesResponse>(
$"{BaseUrl}/prices?product={Uri.EscapeDataString(Product)}")
?? throw new InvalidOperationException("Empty response");
// Only hours that haven't ended yet
var now = DateTimeOffset.Now;
var cheapest = prices.Actual
.Where(hour => hour.End > now)
.OrderBy(hour => hour.PriceIncVat)
.Take(HoursNeeded)
.OrderBy(hour => hour.Start)
.ToList();
Console.WriteLine($"The {cheapest.Count} cheapest upcoming hours:");
foreach (var hour in cheapest)
{
var from = $"{hour.Start:yyyy-MM-dd HH:mm}";
var to = $"{hour.End:HH:mm}";
// On the night the clocks change, an hour starts and ends with different
// UTC offsets. Show them, or October's extra hour would read 02:00–02:00.
if (hour.Start.Offset != hour.End.Offset)
{
from += $" ({hour.Start:zzz})";
to += $" ({hour.End:zzz})";
}
Console.WriteLine(
$"{from}–{to} " +
$"{hour.PriceIncVat} {prices.Currency}/{prices.Unit} incl. VAT");
}
record PricesResponse(string Currency, string Unit, List<HourlyPrice> Actual, List<HourlyPrice> Preview);
record HourlyPrice(DateTimeOffset Start, DateTimeOffset End, decimal PriceExVat, decimal PriceIncVat);
The prices for a single day, for example tomorrow:
curl "https://api.uat.goteborgenergi.cloud/gridtariff/v0/prices?product=GN6KWST&date=2026-10-15"
Sorting and picking hours is easier in a programming language, see the other tabs.
Example output:
The 4 cheapest upcoming hours:
2026-10-14 22:00–23:00 0 SEK/kW incl. VAT
2026-10-14 23:00–00:00 0 SEK/kW incl. VAT
2026-10-15 00:00–01:00 0 SEK/kW incl. VAT
2026-10-15 01:00–02:00 0 SEK/kW incl. VAT
Taking it further
- Use the preview prices (
preview) to plan several days ahead, for example when a battery needs to be full on a certain day. Remember that they are estimates: they are recalculated every day, and the set prices can turn out differently. - Need a continuous block? Some devices, such as a dishwasher, need several hours in a row. Instead of picking single hours, compare the total price of every block of the right length and pick the cheapest.
- Run it daily after 15:00, when the next day's prices are set.
- Using Home Assistant? See Use hourly prices in Home Assistant.