Exchange API — darmowe kursy walut bez limitów zapytań
Kursy dla trzystu czterdziestu walut, kryptowalut i metali, bez klucza API i bez limitów zapytań — sprawdziliśmy na żywo i dane są z dzisiaj. Kluczowe jest jednak zrozumienie, że to nie jest API: to paczka npm publikowana raz na dobę i serwowana z publicznego CDN-u. Wyjaśniamy, co z tego wynika, dlaczego nie wiadomo, skąd pochodzą kursy, i czego nie wolno na tym oprzeć.
Kursy walut są w projektach potrzebne częściej, niż się planuje. Sklep pokazujący ceny w euro i złotych. Faktura wystawiona w euro, zapłacona przelewem w złotych. Raport sprzedaży z trzech rynków. Panel, w którym klient chce widzieć wartość zamówień w jednej walucie.
Płatne serwisy rozwiązują to za abonament rozliczany od liczby zapytań, z historią za wyższą stawkę. Exchange API (github.com/fawazahmed0/exchange-api) obiecuje to samo za darmo, bez klucza i bez limitów, dla ponad dwustu walut wraz z kryptowalutami i metalami.
Przy darmowych usługach danych finansowych obietnica jest jednak najmniej interesującą częścią. Dlatego ten wpis zaczynam od sprawdzenia, czy to w ogóle działa — a potem wyjaśniam, dlaczego działa, bo z tej odpowiedzi wynika wszystko, co trzeba wiedzieć przed użyciem.
Sprawdzone na żywo
Odpytałem usługę w dniu pisania tego tekstu, 11 października 2026. Wynik dla euro jako waluty bazowej:
- data w odpowiedzi: dzisiejsza — dane nie są zaległe,
- 340 walut w jednym pliku, w tym
plnrówne 4,3133,usd1,1631,gbp0,8586 ichf0,9409, - obok walut narodowych są kryptowaluty (
btc) i metale (xau, czyli złoto), - adres zapasowy na Cloudflare zwrócił te same dzisiejsze dane, co sprawdziłem osobno dla złotego jako bazy,
- archiwum działa: plik dla 6 marca 2024 odpowiedział poprawnie, z kursem euro do złotego 4,3121.
Usługa jest więc żywa, aktualna i ma sprawdzalną historię. To rozstrzyga pytanie podstawowe, ale otwiera ważniejsze.
To nie jest API — i w tym cała rzecz
Nazwa myli, a mechanizm jest zupełnie inny, niż sugeruje. Nie ma tu żadnego serwera API. Przepływ wygląda tak:
- raz na dobę, o północy czasu uniwersalnego, uruchamia się zadanie w CI,
- zadanie zbiera kursy i generuje pliki JSON dla każdej waluty bazowej,
- całość jest publikowana jako paczka npm, której numerem wersji jest data,
- a użytkownicy pobierają pliki z publicznego CDN-u jsDelivr, który serwuje zawartość paczek npm.
Stąd bierze się „brak limitów zapytań" i jest to prawda dosłowna: nie ma czego limitować, bo nie ma serwera, który by liczył zapytania. Odpytujesz sieć dostarczania treści, tak samo jak pobierając bibliotekę JavaScriptu.
Z tej jednej decyzji architektonicznej wynikają wszystkie zalety i wszystkie wady tej usługi:
- Jest szybka i tania w skali, bo odpowiada CDN z pamięcią podręczną blisko użytkownika,
- Historia jest niezmienna i dostępna bez dopłat — każdy dzień to osobna wersja paczki, która nigdy się nie zmienia,
- Nie ma uwierzytelniania ani kluczy, więc integracja to jedno pobranie,
- ale rozdzielczość jest dobowa. Nie ma kursów śróddziennych i nie będzie,
- i limit nie zniknął, tylko się przesunął — obowiązują warunki uczciwego użycia sieci dostarczania treści, a nie warunki usługi, której autor nie ma.
Jak z tego korzystać
Adres składa się z trzech ruchomych części — daty, wersji API i punktu końcowego:
https://cdn.jsdelivr.net/npm/@fawazahmed0/currency-api@{data}/v1/{punkt}Data to albo latest, albo konkretny dzień w formacie RRRR-MM-DD. Punkty końcowe są dwa: /currencies zwraca listę dostępnych walut, a /currencies/{kod} — kursy dla wskazanej waluty bazowej. Każdy występuje w wariancie czytelnym i zminifikowanym:
# wszystkie kursy z euro jako bazą, wersja zminifikowana
https://cdn.jsdelivr.net/npm/@fawazahmed0/currency-api@latest/v1/currencies/eur.min.json
# to samo dla złotego
https://cdn.jsdelivr.net/npm/@fawazahmed0/currency-api@latest/v1/currencies/pln.min.json
# stan na konkretny dzień
https://cdn.jsdelivr.net/npm/@fawazahmed0/currency-api@2024-03-06/v1/currencies/eur.min.jsonOdpowiedź ma dwa pola: datę oraz obiekt o nazwie waluty bazowej, w którym kluczami są kody pozostałych walut pisane małymi literami. Kurs złotego do euro to więc data["pln"]["eur"].
Jest też adres zapasowy na Cloudflare Pages, w którym data trafia do nazwy poddomeny:
https://latest.currency-api.pages.dev/v1/currencies/eur.jsonI tu uwaga najważniejsza z całej dokumentacji, postawiona przez autora jako ostrzeżenie: należy zaimplementować mechanizm zapasowy — jeśli pobranie z jsDelivr się nie uda, kod ma sięgnąć na Cloudflare. Nie jest to sugestia stylistyczna, a wymóg wynikający z tego, że oba adresy są cudzą infrastrukturą, na którą autor usługi nie ma wpływu. Traktowałbym to jako część minimalnej implementacji, nie jako ulepszenie na później.
Co jest w środku
Skrypt generujący dane ma wypisane wprost dwie listy. Pierwsza to kody ISO walut narodowych — jest tam złoty, wszystkie waluty europejskie, egzotyki w rodzaju wanuackiego vatu, a także pozycje, które walutami nie są: metale (złoto, srebro, platyna, pallad) oraz specjalne prawa ciągnienia Międzynarodowego Funduszu Walutowego. Druga to lista kryptowalut ułożona według kapitalizacji, wpisana do kodu na stałe, z komentarzem odsyłającym do serwisu, z którego pochodzi ranking.
Przy okazji sprawdzania archiwum natrafiłem na rzecz, którą warto odnotować, bo mówi coś o stabilności zakresu danych: plik z 6 marca 2024 zawiera 701 walut, a dzisiejszy 340. Pokrycie zmniejszyło się w tym czasie o ponad połowę. Nie wiem, co dokładnie zostało usunięte — najbardziej prawdopodobne jest ograniczenie liczby kryptowalut, bo to ich lista jest ręcznie utrzymywana — ale wniosek praktyczny jest niezależny od przyczyny: jeśli Twoja aplikacja opiera się na konkretnym kodzie waluty, sprawdź jego obecność w kodzie, a nie zakładaj.
Punkt końcowy z listą walut zwraca przy tym nazwy czytelne dla człowieka, co przy budowaniu listy rozwijanej oszczędza własny słownik: pln to „Polish Zloty", xau — „Gold Ounce", a xdr — „IMF Special Drawing Rights".
Punkt końcowy, którego nie ma w dokumentacji
Przy przeglądaniu zawartości paczki natrafiłem na plik, o którym README nie wspomina ani słowem, a który bywa dokładnie tym, czego się szuka: /v1/country.json zawiera 245 krajów z przypisaną walutą.
Każdy wpis ma nazwę kraju, kod trzyliterowy, numer statystyczny, a przede wszystkim nazwę, kod i numer waluty. Czyli mapowanie z kraju na walutę, gotowe do użycia:
{
"pl": {
"country_name": "poland",
"country_iso3": "pol",
"currency_name": "zloty",
"currency_code": "pln",
...
}
}Zastosowanie jest oczywiste: ustalenie domyślnej waluty na podstawie kraju użytkownika — z ustawień regionalnych przeglądarki albo z adresu dostawy — bez utrzymywania własnej tablicy, która i tak byłaby niepełna. Warto jednak pamiętać, skąd ta wygoda pochodzi: plik nie jest udokumentowany, więc jest tym bardziej narażony na zniknięcie przy zmianie struktury paczki. Kopiowałbym go do siebie raz i trzymał lokalnie, zamiast pobierać na bieżąco.
Skąd pochodzą te kursy — i dlaczego tego nie wiadomo
To jest sekcja, dla której warto było przeczytać kod, i najważniejsza rzecz w tym wpisie.
Skrypt zbierający dane pobiera adresy źródeł z pięciu zmiennych środowiskowych, ustawianych w CI z sekretów repozytorium. Cztery dotyczą kursów walut (jeden z nich wraz z kluczem dostępu), jeden kryptowalut. Poza CI skrypt czyta te same wartości z pliku, którego w repozytorium nie ma.
Znaczy to jedną rzecz: nie da się sprawdzić, skąd pochodzą kursy. Nie wiadomo, czy źródłem jest bank centralny, agregator komercyjny, czy strona internetowa. Nie wiadomo, jaka jest metodologia — kurs średni, kurs kupna, kurs zamknięcia. Nie wiadomo, czy redystrybucja tych danych jest zgodna z warunkami ich dostawcy.
Drugi szczegół z kodu potwierdza obraz: pobieranie odbywa się przy użyciu Playwrighta z Firefoksem, a przepływ CI instaluje przeglądarkę wraz z zależnościami systemowymi. To nie jest odpytywanie API — to automatyzacja przeglądarki, czyli w praktyce zbieranie danych ze stron.
Nie stawiam tego jako oskarżenia — autor nie ukrywa mechanizmu, kod jest publiczny i każdy może go przeczytać, a usługa jest darmowa i nie obiecuje gwarancji. Stawiam to jako fakt do świadomego przyjęcia: korzystasz z danych o nieznanym pochodzeniu, zbieranych metodą, która zależy od układu cudzych stron.
Ciągłość: to już raz się zepsuło
Historia tej usługi jest jej najlepszą oceną ryzyka i jest udokumentowana w repozytorium.
Poprzednia wersja działała pod innym adresem, opartym na plikach wprost z repozytorium na GitHubie. GitHub poprosił autora o usunięcie tamtego repozytorium z powodu jego rozmiaru, co wymusiło migrację na npm i Cloudflare. Sprawdziłem stary adres z przewodnika migracji: zwraca dziś kod 404.
Zmienił się przy tym nie tylko adres, ale i kształt odpowiedzi — dawniej pobierało się kurs jednej pary walut i czytało wartość wprost, dziś pobiera się wszystkie kursy dla waluty bazowej i wchodzi o jeden poziom głębiej. Czyli był to pełny rozjazd kontraktu, a nie zmiana domeny.
Do tego dochodzi profil ryzyka, który trzeba nazwać: jeden autor, jedno repozytorium, a ciągłość usługi zależy od trzech cudzych infrastruktur — rejestru npm, sieci jsDelivr i Cloudflare Pages. Każda z nich może zmienić warunki, a wtedy scenariusz z 2024 roku powtórzy się w innej formie.
Licencja: CC0 na kodzie, ale nie na kursach
Repozytorium jest oznaczone jako CC0-1.0, czyli zrzeczenie się praw w maksymalnym zakresie — najbardziej liberalna forma udostępnienia, jaka istnieje, bez wymogu podawania autorstwa.
I tu potrzebne jest rozróżnienie, które przy zbiorach danych bywa pomijane: CC0 obejmuje to, do czego autor ma prawa — czyli skrypt, konfigurację i sposób publikacji. Nie może natomiast przenieść praw do danych, których autor nie wytworzył. Skoro źródła kursów są nieujawnione, to nie da się ustalić, na jakich warunkach te konkretne liczby wolno redystrybuować, a deklaracja licencyjna w repozytorium tego nie rozstrzyga.
Praktycznie: do wyświetlenia przelicznika w sklepie nikt nie będzie tego badał. Do systemu, w którym kursy trafiają na dokumenty finansowe klienta, jest to pytanie, którego wolałbym nie mieć bez odpowiedzi.
Czego na tym nie należy oprzeć
Najważniejsze rozgraniczenie dla polskich projektów i rzecz, którą trzeba powiedzieć klientowi, zanim zapyta.
Przeliczenia na potrzeby podatkowe i księgowe opierają się w Polsce na kursach Narodowego Banku Polskiego, a nie na dowolnym źródle rynkowym — z regułą wskazującą konkretną tabelę i konkretny dzień względem daty zdarzenia. Kurs z darmowego serwisu, choćby był dokładny, nie jest właściwym źródłem dla faktury, deklaracji ani rozliczenia różnic kursowych. Do tego celu jest publiczne API NBP, które udostępnia tabele wraz z ich numerami i datami — czyli dokładnie to, czego wymaga dokumentacja księgowa. Szczegóły reguły warto potwierdzić z księgowością klienta, ale kierunek jest jednoznaczny.
Podobnie nie opierałbym na tym rozliczeń między systemami ani niczego, co dotyczy realnego przepływu pieniędzy. Rozdzielczość dobowa, nieznane pochodzenie i brak jakiegokolwiek zobowiązania co do dostępności wykluczają zastosowania, w których liczba musi być właściwa, a nie tylko przybliżona.
Gdzie to ma sens
- Ceny wyświetlane orientacyjnie. „Około 249 euro" obok ceny w złotych, z adnotacją o charakterze poglądowym. Najczęstsze i najbezpieczniejsze zastosowanie,
- Przeliczniki i kalkulatory w serwisach informacyjnych oraz narzędziach pomocniczych,
- Pulpity i raporty wewnętrzne — sprowadzenie sprzedaży z kilku rynków do jednej waluty na potrzeby oglądania trendu, nie rozliczenia,
- Środowiska deweloperskie i testowe. Zamiast płacić za klucz do płatnego serwisu w środowisku, które i tak generuje dane próbne,
- Dane historyczne do analiz — niezmienne pliki dzienne z archiwum są do tego wygodne i darmowe,
- Kryptowaluty i metale poglądowo — tu w ogóle nie ma dobrego darmowego odpowiednika o takim zasięgu.
W każdym z tych przypadków wdrożyłbym jednak dwie rzeczy niezależnie od skali: własną pamięć podręczną po stronie serwera, żeby nie odpytywać CDN-u przy każdym żądaniu użytkownika i mieć ostatnią znaną wartość przy awarii, oraz mechanizm zapasowy na drugi adres, tak jak zaleca dokumentacja.
Jak to wpiąć w Laravelu
Cała integracja mieści się w jednej metodzie i warto ją napisać od razu w wersji odpornej, bo dopisanie zabezpieczeń później zwykle nie następuje:
public function rates(string $base = 'pln'): array
{
return Cache::remember("rates.{$base}", now()->addHours(6), function () use ($base) {
foreach ([
"https://cdn.jsdelivr.net/npm/@fawazahmed0/currency-api@latest/v1/currencies/{$base}.min.json",
"https://latest.currency-api.pages.dev/v1/currencies/{$base}.min.json",
] as $url) {
$response = Http::timeout(5)->get($url);
if ($response->successful()) {
Cache::forever("rates.{$base}.last", $response->json());
return $response->json();
}
}
return Cache::get("rates.{$base}.last", []);
});
}Trzy decyzje w tych kilkunastu linijkach są celowe. Pamięć podręczna na sześć godzin przy danych aktualizowanych raz na dobę jest z zapasem, a jednocześnie ogranicza ruch do CDN-u do kilku żądań dziennie. Pętla po dwóch adresach realizuje mechanizm zapasowy wymagany przez dokumentację. A osobny wpis trzymany bezterminowo przechowuje ostatnią udaną odpowiedź — więc gdy oba adresy zawiodą, aplikacja pokaże kurs wczorajszy zamiast się wywrócić. Krótki limit czasu domyka całość: przy niedostępnym CDN-ie strona nie czeka pięciu sekund na każdy z adresów po kolei w trakcie żądania użytkownika, bo odpowiedź i tak przychodzi z pamięci podręcznej.
Przy cenach wyświetlanych klientowi dodałbym do tego jeszcze jedno: datę kursu obok przeliczonej kwoty. Pole z datą jest w odpowiedzi, a informacja „kurs z 11 października" zamienia liczbę orientacyjną w liczbę uczciwie opisaną.
Pułapki
- Źródła kursów są nieujawnione — pięć adresów w sekretach repozytorium,
- Dane zbierane automatyzacją przeglądarki, czyli metodą zależną od układu cudzych stron,
- CC0 dotyczy kodu, nie rozstrzyga o prawach do samych kursów,
- Rozdzielczość dobowa, aktualizacja raz na dobę o północy czasu uniwersalnego,
- Usługa już raz zmieniła adresy i kształt odpowiedzi po tym, jak GitHub poprosił o usunięcie poprzedniego repozytorium; stare adresy zwracają 404,
- Ciągłość zależy od jednej osoby i trzech cudzych infrastruktur,
- Brak limitów nie znaczy braku warunków — obowiązują zasady uczciwego użycia sieci dostarczania treści,
- Zakres walut się zmienia: 701 pozycji w archiwum z 2024 roku wobec 340 dzisiaj,
- Nie do księgowości. Do przeliczeń podatkowych właściwym źródłem są tabele NBP,
- Brak jakiegokolwiek zobowiązania co do dostępności — to darmowa usługa jednej osoby, nie umowa.
Podsumowanie
- Działa i zwraca dzisiejsze dane — sprawdzone na obu adresach w dniu pisania,
- 340 walut, kryptowalut i metali, ze złotym i złotem w zestawie,
- To nie API, a paczka npm serwowana z CDN-u — stąd brak limitów i stąd dobowa rozdzielczość,
- Historia jest niezmienna i darmowa, każdy dzień jako osobna wersja,
- Zaimplementuj adres zapasowy — dokumentacja traktuje to jako wymóg, nie opcję,
- Dodaj własną pamięć podręczną i przechowuj ostatnią znaną wartość,
- Nie wiadomo, skąd pochodzą kursy, a zbierane są automatyzacją przeglądarki,
- Do faktur i podatków użyj API NBP, nie tego serwisu,
- Usługa już raz zerwała kontrakt adresowy — przy wdrożeniu założ, że może to zrobić ponownie,
- W paczce jest nieudokumentowany plik z 245 krajami i ich walutami — przydatny, ale skopiuj go do siebie,
- Pokaż datę kursu obok przeliczonej kwoty,
- Do cen orientacyjnych, przeliczników, pulpitów i analiz historycznych jest to bardzo dobre narzędzie — i za darmo.
Licencja: repozytorium jest udostępnione na CC0-1.0, czyli w formie zrzeczenia się praw autorskich w najszerszym dopuszczalnym zakresie — bez wymogu podawania autorstwa, bez warunków, także w użyciu komercyjnym. Dla kodu i sposobu publikacji jest to sytuacja najprostsza z możliwych. Trzeba jednak rozdzielić dwie rzeczy, które przy zbiorach danych zlewają się najczęściej i najkosztowniej: zrzeczenie się praw działa w zakresie, w jakim autor te prawa miał. Obejmuje więc skrypt, konfigurację i pliki, które on generuje — ale nie przenosi uprawnień do danych źródłowych, których autor nie wytworzył. Ponieważ adresy pięciu źródeł są przechowywane jako sekrety repozytorium, pochodzenia kursów nie da się ustalić z zewnątrz, a więc nie da się też ustalić, czy i na jakich warunkach ich dalsza redystrybucja jest dopuszczalna. Przy przeliczniku poglądowym w sklepie nikt tego nie zbada i praktyczne ryzyko jest znikome. Przy systemie, w którym kursy trafiają na dokumenty finansowe albo do rozliczeń z klientem, jest to brak, którego nie zasypie żadna deklaracja licencyjna — i dodatkowy argument za tym, żeby w takich zastosowaniach sięgać po źródło o jawnej metodologii i jawnym wydawcy, czyli po bank centralny.