Firmowy arkusz z kosztami w EUR i USD. Ktoś z zespołu codziennie rano otwiera stronę NBP, przepisuje kurs do komórki B2 i dopiero wtedy reszta formuł zaczyna działać. Brzmi znajomo? A może dostajesz fakturę od kontrahenta z Norwegii w NOK i musisz znaleźć kurs z konkretnego dnia roboczego, żeby prawidłowo ją zaksięgować.
Oba te problemy rozwiązuje jeden skrypt – albo nawet jedna formuła. Narodowy Bank Polski udostępnia darmowe API z kursami walut (tabela A, B i C), które można odpytywać bez klucza, bez rejestracji i bez żadnych limitów płatności. Wystarczy wysłać zapytanie HTTP na właściwy adres i odczytać odpowiedź w formacie JSON lub CSV.
W tym artykule pokazuję dwie metody: szybką formułę IMPORTDATA dla tych, którzy nie chcą pisać kodu, oraz pełny skrypt Apps Script z automatycznym triggerem dla tych, którzy potrzebują historii kursów i pełnej kontroli nad procesem.
Metoda 1: IMPORTDATA – jedna formuła, zero kodu
Jeśli potrzebujesz tylko bieżącego kursu jednej waluty i nie zależy Ci na automatycznym zbieraniu historii – wystarczy jedna formuła. API NBP obsługuje format CSV, a Google Sheets potrafi go wciągnąć przez wbudowaną funkcję IMPORTDATA.
Podstawowa formuła wygląda tak:
=IMPORTDATA("https://api.nbp.pl/api/exchangerates/rates/a/eur/today/?format=csv")
Po wklejeniu tej formuły do komórki arkusz pobierze trzy kolumny danych: kod waluty, nazwę i kurs średni. Problem w tym, że IMPORTDATA wstawia też nagłówki – więc interesująca Cię wartość kursu ląduje w innej komórce niż ta, w której wpisałeś formułę.
Rozwiązanie: owiń IMPORTDATA w INDEX, żeby wyciągnąć dokładnie tę komórkę, która Cię interesuje – wiersz 2 (pierwszy wiersz danych po nagłówku), kolumna 3 (kurs średni):
=INDEX(IMPORTDATA("https://api.nbp.pl/api/exchangerates/rates/a/eur/today/?format=csv"),2,3)
Możesz też pobrać kurs z konkretnej daty – na przykład z 15 stycznia 2026:
=INDEX(IMPORTDATA("https://api.nbp.pl/api/exchangerates/rates/a/usd/2026-01-15/?format=csv"),2,3)
Ograniczenia tej metody są jednak istotne. Formuła odświeża się dopiero przy otwarciu arkusza – nie na bieżąco. Jeśli arkusz jest zamknięty cały dzień, kurs nie zaktualizuje się sam. Poza tym IMPORTDATA bywa niestabilna – Google co jakiś czas zwraca błąd #N/A bez wyraźnego powodu, szczególnie przy większej liczbie wywołań.
Metoda 2: Apps Script – pełna kontrola i automatyzacja
Formuły IMPORTDATA wystarczają do jednorazowego sprawdzenia kursu. Ale jeśli potrzebujesz codziennego zapisu historii kursów, obsługi wielu walut naraz i odporności na błędy API – jedynym sensownym rozwiązaniem jest Apps Script.
Apps Script to środowisko JavaScript wbudowane w Google Sheets. Masz dostęp do UrlFetchApp (zapytania HTTP), SpreadsheetApp (odczyt/zapis komórek) i wyzwalaczy czasowych (triggery). Wszystko za darmo, bez instalacji, bez serwera.
Poniżej kompletny skrypt z komentarzami. Skopiuj go do edytora Apps Script (Rozszerzenia > Apps Script) i zapisz:
/**
* Pobiera kurs średni waluty z API NBP (tabela A).
* @param {string} waluta – kod ISO, np. "EUR", "USD", "NOK"
* @param {string} data – opcjonalnie, format "YYYY-MM-DD"; domyślnie dziś
* @returns {number|null} kurs średni lub null przy błędzie
*/
function pobierzKursNBP(waluta, data) {
var url = 'https://api.nbp.pl/api/exchangerates/rates/a/'
+ waluta.toLowerCase() + '/';
if (data) {
url += data + '/';
} else {
url += 'today/';
}
url += '?format=json';
try {
var odpowiedz = UrlFetchApp.fetch(url, {muteHttpExceptions: true});
if (odpowiedz.getResponseCode() !== 200) {
Logger.log('Brak kursu dla ' + waluta + ' (' + data + '): kod ' + odpowiedz.getResponseCode());
return null;
}
var dane = JSON.parse(odpowiedz.getContentText());
return dane.rates[0].mid;
} catch (e) {
Logger.log('Błąd pobierania kursu: ' + e.message);
return null;
}
}
/**
* Zapisuje kursy wielu walut do arkusza "Kursy".
* Kolumna A = data, B = EUR, C = USD, D = GBP, E = NOK, F = CHF.
*/
function zapiszKursyDoArkusza() {
var arkusz = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Kursy');
if (!arkusz) {
arkusz = SpreadsheetApp.getActiveSpreadsheet().insertSheet('Kursy');
arkusz.appendRow(['Data', 'EUR', 'USD', 'GBP', 'NOK', 'CHF']);
}
var waluty = ['EUR', 'USD', 'GBP', 'NOK', 'CHF'];
var dzis = Utilities.formatDate(new Date(), 'Europe/Warsaw', 'yyyy-MM-dd');
var wiersz = [dzis];
for (var i = 0; i < waluty.length; i++) {
var kurs = pobierzKursNBP(waluty[i]);
wiersz.push(kurs !== null ? kurs : 'brak');
Utilities.sleep(200); // limit API NBP
}
arkusz.appendRow(wiersz);
Logger.log('Zapisano kursy z dnia ' + dzis);
}
Jak to działa krok po kroku:
Funkcja pobierzKursNBP(waluta, data) – buduje URL do API NBP na podstawie kodu waluty (np. "EUR") i opcjonalnej daty. Jeśli nie podasz daty, użyje endpointu /today/. Wysyła zapytanie GET przez UrlFetchApp.fetch() z flagą muteHttpExceptions: true, dzięki czemu błąd HTTP (np. 404 w weekend) nie przerywa całego skryptu. Odpowiedź JSON zawiera tablicę rates, z której wyciągamy pole mid – kurs średni.
Funkcja zapiszKursyDoArkusza() – szuka zakładki o nazwie "Kursy". Jeśli nie istnieje, tworzy ją i wstawia nagłówki. Następnie iteruje po tablicy walut, pobiera kurs każdej z nich i buduje wiersz. Metoda appendRow() dopisuje wiersz na końcu arkusza – dzięki temu z każdym uruchomieniem rośnie historia kursów.
Zwróć uwagę na linię Utilities.sleep(200) – to 200-milisekundowa pauza między zapytaniami. API NBP nie dokumentuje limitu rate, ale przy zbyt szybkich zapytaniach potrafi zwracać błędy. 200 ms to bezpieczny margines.
Konfiguracja triggera – codziennie o 12:00
Sam skrypt nic nie zrobi, dopóki go nie uruchomisz. Możesz kliknąć przycisk "Uruchom" ręcznie, ale cały sens automatyzacji polega na tym, żeby skrypt odpalał się sam każdego dnia. Do tego służą wyzwalacze (triggers) w Apps Script.
Krok po kroku:
- W edytorze Apps Script kliknij ikonę zegara po lewej stronie (Wyzwalacze) lub wybierz z menu Edytuj > Wyzwalacze bieżącego projektu.
- Kliknij + Dodaj wyzwalacz w prawym dolnym rogu.
- Ustaw parametry:
- Funkcja do uruchomienia:
zapiszKursyDoArkusza - Źródło zdarzenia: Czasowy
- Typ wyzwalacza czasowego: Wyzwalacz dzienny
- Pora dnia: 12:00 – 13:00
- Funkcja do uruchomienia:
- Kliknij Zapisz.
Przy pierwszym zapisaniu wyzwalacza Google poprosi Cię o autoryzację – skrypt potrzebuje dostępu do arkusza i do sieci (UrlFetchApp). Zaakceptuj uprawnienia – to Twój własny skrypt, więc nie ma ryzyka bezpieczeństwa.
Wariant: kurs historyczny z konkretnego dnia
Często potrzebujesz kursu nie z dziś, ale z konkretnego dnia – np. z daty wystawienia faktury. Możesz oczywiście użyć funkcji pobierzKursNBP("EUR", "2026-01-15") bezpośrednio w kodzie, ale wygodniej będzie stworzyć formułę arkuszową, która działa jak natywna funkcja Sheets.
Dodaj do swojego projektu Apps Script poniższy kod:
/**
* Formuła arkuszowa: =KURS_NBP("EUR", "2026-01-15")
* @customfunction
*/
function KURS_NBP(waluta, data) {
if (!waluta) return 'Podaj walutę (np. EUR)';
var dataStr = '';
if (data) {
if (data instanceof Date) {
dataStr = Utilities.formatDate(data, 'Europe/Warsaw', 'yyyy-MM-dd');
} else {
dataStr = data.toString();
}
}
return pobierzKursNBP(waluta, dataStr || null);
}
Dekorator @customfunction sprawia, że Apps Script rejestruje tę funkcję jako formułę arkuszową. Po zapisaniu skryptu możesz wrócić do arkusza i wpisać:
=KURS_NBP("EUR", "2026-01-15")
=KURS_NBP("NOK", A2) // data z komórki A2
=KURS_NBP("USD") // kurs z dziś
Funkcja obsługuje zarówno daty wpisane jako tekst ("2026-01-15"), jak i referencje do komórek z formatem daty – sprawdza, czy argument to obiekt Date, i odpowiednio go konwertuje.
API ECB i Norges Bank – kursy EUR/USD i NOK
API NBP to najlepsze źródło kursów dla polskich potrzeb księgowych – bo to oficjalny kurs NBP używany do rozliczeń podatkowych. Ale zdarzają się sytuacje, kiedy potrzebujesz kursów z innego banku centralnego.
Europejski Bank Centralny (ECB) – udostępnia kursy referencyjne EUR względem ok. 30 walut. Endpoint:
https://data-api.ecb.europa.eu/service/data/EXR/D.USD.EUR.SP00.A?format=csvdata&startPeriod=2026-01-01&endPeriod=2026-01-31
ECB publikuje kursy raz dziennie ok. 16:00 CET. Przydatne, gdy potrzebujesz kursu EUR/USD do porównań międzynarodowych – a nie kursu PLN/USD z NBP.
Norges Bank – bank centralny Norwegii. Udostępnia kursy NOK przez REST API:
https://data.norges-bank.no/api/data/EXR/B.EUR.NOK.SP?format=csv&startPeriod=2026-01-01&endPeriod=2026-01-31
Kiedy NBP nie wystarczy? Gdy potrzebujesz bezpośredniego kursu NOK/EUR (NBP podaje tylko NOK/PLN i EUR/PLN – musisz sam przeliczyć), kursów intraday (NBP daje jeden kurs na dzień), albo kursów walut, których NBP nie notuje w tabeli A (np. waluty egzotyczne – te są w tabeli C, publikowanej w środy).
Porównanie metod
| IMPORTDATA | Apps Script | Wtyczka (np. CurrencyFreaks) | |
|---|---|---|---|
| Koszt | $0 | $0 | $5-50/mies. |
| Automatyzacja | ❌ odświeża przy otwarciu | ✅ trigger dzienny | ✅ auto |
| Historia kursów | ❌ tylko bieżący | ✅ append do arkusza | ✅ |
| Limit | 50 formuł/arkusz | ~20k wywołań/dzień | zależny od planu |
| Niezawodność | ⚠️ IMPORTDATA bywa niestabilny | ✅ z obsługą błędów | ✅ |
Jeśli potrzebujesz kursu raz na jakiś czas i masz jeden arkusz – IMPORTDATA z INDEX w zupełności wystarczy. Jeśli prowadzisz rozliczenia walutowe na co dzień, masz wiele walut i chcesz mieć historię – Apps Script to jedyna sensowna droga. Wtyczki płatne warto rozważyć tylko wtedy, gdy potrzebujesz kursów intraday lub walut egzotycznych, których NBP nie obsługuje.
waluty w funkcji zapiszKursyDoArkusza() – kody walut znajdziesz na stronie NBP w tabeli kursów.