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.

Run the image
docker run -d --name bmkg-api -p 8099:8099 ghcr.io/dhanyyudi/bmkg-api:latest

curl http://localhost:8099/health

Docker 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.

Clone and start
git clone https://github.com/dhanyyudi/bmkg-api.git
cd bmkg-api
cp .env.example .env
docker compose up -d

Local development

Python 3.12 or newer for the API, Node 22 or newer for the website.

From source
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 web

Configuration

All settings are environment variables, read from .env when present.

VariableDefaultPurpose
REDIS_URLredis://localhost:6379Cache backend. Without Redis the API uses an in-process cache.
RATE_LIMIT_ANONYMOUS30/minuteRequests 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_MINUTE50Most requests per minute sent to each BMKG host. 0 removes the cap.
CACHE_TTL_EARTHQUAKE_LATEST60Seconds the latest earthquake is cached.
CACHE_TTL_EARTHQUAKE_LIST300Seconds earthquake lists are cached.
CACHE_TTL_NOWCAST120Seconds weather warnings are cached.
CACHE_TTL_WEATHER900Seconds a forecast is cached.
CACHE_TTL_WILAYAH86400Cache 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.

MCP setup guide