Running a separate instance
The public instance is shared and rate limited. A separate instance has limits of its own, and the whole API runs in one container.
Docker
The image is built for amd64 and arm64 from the main branch, and includes this website.
docker run -d --name bmkg-api -p 8099:8099 ghcr.io/dhanyyudi/bmkg-api:latest
curl http://localhost:8099/healthDocker Compose with Redis
The repository's docker-compose.yml adds Redis so the cache survives a container restart. The stack joins an external network named private_network and opens no host port, which suits a reverse proxy or a tunnel. A ports entry gives direct access.
git clone https://github.com/dhanyyudi/bmkg-api.git
cd bmkg-api
cp .env.example .env
docker compose up -dLocal development
Python 3.12 or newer for the API, Node 22 or newer for the website.
git clone https://github.com/dhanyyudi/bmkg-api.git
cd bmkg-api
make setup
source venv/bin/activate
make dev # API on http://localhost:8099
# The website is a separate build, written to ./landing
make webConfiguration
All settings are environment variables, read from .env when present.
| Variable | Default | Purpose |
|---|---|---|
REDIS_URL | redis://localhost:6379 | Cache backend. Without Redis the API uses an in-process cache. |
RATE_LIMIT_ANONYMOUS | 30/minute | Requests allowed per client, per endpoint. |
CLIENT_IP_HEADER | (empty) | Header carrying the real client IP behind a proxy, for example CF-Connecting-IP. Left empty unless every request arrives through that proxy. |
UPSTREAM_MAX_PER_MINUTE | 50 | Most requests per minute sent to each BMKG host. 0 removes the cap. |
CACHE_TTL_EARTHQUAKE_LATEST | 60 | Seconds the latest earthquake is cached. |
CACHE_TTL_EARTHQUAKE_LIST | 300 | Seconds earthquake lists are cached. |
CACHE_TTL_NOWCAST | 120 | Seconds weather warnings are cached. |
CACHE_TTL_WEATHER | 900 | Seconds a forecast is cached. |
CACHE_TTL_WILAYAH | 86400 | Cache lifetime sent for region data. |
Putting it on the internet
A public instance gets flooded with requests sooner or later. Three things keep it up:
- Caching at a CDN. Every /v1 response carries Cache-Control matching its data's lifetime. With a CDN in front that honours the header, repeat requests never reach the server.
- Rate limiting by the real client. Behind a proxy, CLIENT_IP_HEADER needs a value. Without it every visitor counts as the proxy.
- A cap towards BMKG. UPSTREAM_MAX_PER_MINUTE keeps abuse of an instance from getting the server's IP blocked by BMKG.
MCP server
The same image runs the MCP endpoint: a second container with the command python -m mcp_server.http_server, and BMKG_API_BASE_URL pointing at the API.