A small Prometheus exporter that pulls fiat exchange rates, cryptocurrency prices, and stock quotes from free public APIs and exposes them as metrics. Prometheus scrapes the exporter; Grafana graphs the result.
It is meant to be run as a Docker container on the same Docker network as Prometheus (typically a Linux VM).
Market data from Frankfurter, CoinGecko, and Yahoo Finance (unofficial). Free tiers and attribution rules apply. See Attribution and licenses.
On separate fixed intervals the exporter:
- Calls Frankfurter for fiat rates (default: every 6 hours)
- Calls CoinGecko for crypto prices (default: every 6 minutes)
- Calls Yahoo Finance chart endpoints for stock quotes (default: every 15 minutes)
- Publishes all values as Prometheus gauges
- Tracks API success/failure counts per source and persists them under
/data
Default scrape target: http://market-exporter:9100/metrics on the shared Docker network (no host port is published).
Default base currency is ILS with USD and EUR quotes. Change symbols and currencies in src/fiat.py, src/crypto.py, and src/stocks.py before you build the image.
No .env file or API keys are required.
| Piece | Role |
|---|---|
| Python 3.13 | Exporter |
| Frankfurter | Fiat exchange rates (ECB reference data; no API key) |
| CoinGecko | Cryptocurrency prices (Demo API; no API key) |
| Yahoo Finance | Stock quotes via unofficial chart endpoint (no API key) |
| Prometheus | Scrapes /metrics |
| Grafana | Dashboards on top of Prometheus |
| Docker | How this service is run |
Frankfurter / CoinGecko / Yahoo --> market-exporter (:9100/metrics) --> Prometheus --> Grafana
The exporter does not talk to Grafana. It joins the same Docker network as Prometheus (monitoring in the example below), then Prometheus scrapes it by container name.
- Docker
- A running Prometheus instance (Grafana is optional, but that is the usual setup)
- Outbound HTTPS to
api.frankfurter.dev,api.coingecko.com, andquery1.finance.yahoo.com
No API keys are required for the endpoints used in this repo.
Clone this repo into your monitoring stack as exporters/market-exporter (next to the Compose file that already runs Prometheus), then add:
# --- Market Exporter --- #
market-exporter:
build: ./exporters/market-exporter
container_name: market-exporter
restart: unless-stopped
volumes:
- ./exporters/market-exporter/data:/data
networks:
- monitoringUse the same networks name as Prometheus. The exporter does not publish 9100 on the host; Prometheus reaches it as market-exporter:9100 on monitoring.
Bring it up with the rest of the stack, for example:
docker compose up -d market-exporterConfirm metrics from another container on monitoring (Prometheus itself works):
docker compose exec prometheus wget -qO- http://market-exporter:9100/metricsIn prometheus.yml:
global:
scrape_interval: 10s
scrape_configs:
- job_name: "prometheus"
static_configs:
- targets:
- localhost:9090
- job_name: "market"
static_configs:
- targets:
- market-exporter:9100Reload Prometheus after the change.
Prometheus scrapes every 10 seconds; the exporter refreshes each data source on its own schedule (FIAT_INTERVAL, CRYPTO_INTERVAL, STOCKS_INTERVAL in src/main.py). Between polls, scraped values stay the same.
Edit the lists at the top of each module, then rebuild and recreate the container:
Fiat — src/fiat.py:
BASE_CURRENCY = "ILS"
CURRENCIES = ["USD", "EUR"]Crypto — src/crypto.py:
CRYPTO_CURRENCIES = [
"bitcoin",
"ethereum",
"tether",
"binancecoin",
"solana",
"dogecoin",
]Stocks — src/stocks.py:
STOCKS = [
"AAPL",
"MSFT",
"NVDA",
"GOOGL",
"AMZN",
"TSLA",
]You can also change PORT and the poll intervals at the top of src/main.py.
Stay within each provider's free-tier limits. With the defaults above, CoinGecko usage is well under the Demo plan cap (10,000 calls/month).
Point Grafana at Prometheus as usual, then graph the metrics below. Import the bundled dashboard from grafana/dashboard.json.
On the dashboard, include provider attribution, for example:
- Powered by CoinGecko
- Fiat rates via Frankfurter (ECB reference data)
Labels: base, quote.
| Metric | Meaning |
|---|---|
currency_exchange_rate |
Exchange rate (inverted so quote/base is shown) |
Labels: coin, currency.
| Metric | Meaning |
|---|---|
crypto_price |
Current price |
crypto_24h_change_percent |
24-hour price change (%) |
Labels: symbol, currency on price; symbol on change.
| Metric | Meaning |
|---|---|
stock_price |
Latest regular-market price |
stock_24h_change_percent |
Change vs previous close (%) |
| Metric | Meaning |
|---|---|
fiat_api_calls |
Total Frankfurter requests |
fiat_api_success |
Successful Frankfurter requests |
fiat_api_failures |
Failed Frankfurter requests |
crypto_api_calls |
Total CoinGecko requests |
crypto_api_success |
Successful CoinGecko requests |
crypto_api_failures |
Failed CoinGecko requests |
stocks_api_calls |
Total stock update cycles (one per interval, not per symbol) |
stocks_api_success |
Successful stock update cycles |
stocks_api_failures |
Failed stock update cycles |
- This repository's source code is under the MIT License.
- Fiat data comes from Frankfurter (ECB reference rates). See NOTICE.
- Crypto data comes from CoinGecko and requires attribution when displayed. See NOTICE and CoinGecko API terms.
- Stock data uses an unofficial Yahoo Finance endpoint. It is for personal monitoring only; use a licensed provider for production or commercial use. See NOTICE.
This project is a personal, non-commercial portfolio example. It does not come with legal advice, uptime guarantees, or licenses to third-party market data.
Dockerfile Container image
src/ Exporter modules (main, fiat, crypto, stocks)
requirements.txt Python dependencies
data/ Runtime API counters (volume; not committed)
grafana/ Dashboard JSON and screenshot
NOTICE Third-party data attribution and terms
LICENSE MIT license for this code
