Checking API...

BMKG API

Free REST API featuring interactive weather warning maps from BMKG nowcast. Also includes earthquake data, forecasts & region lookup.

curl https://bmkg-restapi.vercel.app/v1/nowcast

API Features

Earthquake

Real-time seismic data from BMKG's nationwide sensor network.

GET /earthquake/latest
GET /earthquake/recent
GET /earthquake/nearby

Weather

3-day weather forecasts for any location in Indonesia.

GET /weather/{adm4}
GET /weather/{adm4}/current

Nowcast

Real-time weather warnings with map visualization & infographic.

GET /nowcast
GET /nowcast/{alert_code}
Try in Explorer

Wilayah

Indonesian region lookup: provinces, districts, villages.

GET /wilayah/provinces
GET /wilayah/search

API Explorer

Try the API with live visualization for geo data

New: AI Assistant Integration

Use with AI Assistants

Connect BMKG data directly to Claude, Cursor, VS Code, and other MCP-compatible AI assistants

Two Ways to Connect

Option 1: Streamable HTTP (No Install)

Connect instantly via Smithery or HTTP-compatible clients. No local installation required!

https://mcp-bmkg.dhanypedia.it.com/mcp
Add via Smithery

Option 2: Local Install (stdio)

Install locally for offline use and full control. Best for Claude Desktop, Cursor.

pipx install bmkg-api-mcp

HTTP Transport Setup

Claude Code:

claude mcp add --transport http bmkg-api https://mcp-bmkg.dhanypedia.it.com/mcp

Claude Desktop config:

{
  "mcpServers": {
    "bmkg-api": {
      "url": "https://mcp-bmkg.dhanypedia.it.com/mcp"
    }
  }
}
No installation needed!

Quick Setup

1

Claude Desktop

Add to claude_desktop_config.json

2

Cursor

Settings → MCP Servers → Add

3

VS Code

Use with Cline/Roo Code extension

View Full Setup Guide

15 Tools Available

4 Earthquake

Latest, recent, felt, nearby

2 Weather

3-day & current

2 Nowcast

Weather warnings

5 Region

Provinces to villages

2 Utility

Cache & debug

Try these natural language queries:

"Gempa terbaru di Indonesia?" "Cuaca 3 hari ke depan di Jakarta?" "Cari kode wilayah untuk Tebet"

Quick Start

1

Pick Endpoint

Choose from earthquake, weather, nowcast, or wilayah

2

Make Request

Simple HTTP GET request, no authentication required

3

Get JSON

Clean, structured JSON response with attribution

curl https://bmkg-restapi.vercel.app/v1/earthquake/latest
fetch('https://bmkg-restapi.vercel.app/v1/earthquake/latest')
  .then(r => r.json())
  .then(data => console.log(data));
import requests

response = requests.get(
    'https://bmkg-restapi.vercel.app/v1/earthquake/latest'
)
data = response.json()
print(data)
package main

import (
    "encoding/json"
    "fmt"
    "net/http"
)

func main() {
    resp, err := http.Get(
        "https://bmkg-restapi.vercel.app/v1/earthquake/latest",
    )
    if err != nil {
        panic(err)
    }
    defer resp.Body.Close()

    var data map[string]interface{}
    json.NewDecoder(resp.Body).Decode(&data)
    fmt.Println(data)
}
$response = file_get_contents(
    'https://bmkg-restapi.vercel.app/v1/earthquake/latest'
);
$data = json_decode($response, true);
print_r($data);
require 'net/http'
require 'json'

uri = URI('https://bmkg-restapi.vercel.app/v1/earthquake/latest')
response = Net::HTTP.get(uri)
data = JSON.parse(response)
puts data
import 'dart:convert';
import 'package:http/http.dart' as http;

void main() async {
  final response = await http.get(
    Uri.parse('https://bmkg-restapi.vercel.app/v1/earthquake/latest'),
  );
  final data = jsonDecode(response.body);
  print(data);
}

Working with Warning Polygons

The nowcast warning endpoint returns polygon coordinates for affected areas. Here's how to extract and render them on a map using Turf.js.

1

Data Structure

Each area has a flat [lat, lon] array with multiple closed rings concatenated together. Swap to [lon, lat] for map libraries.

Response
{ "areas": [{ "polygon": [
    [-7.434, 110.216],  // Ring 1 start
    [-7.444, 110.215], ...
    [-7.434, 110.216],  // Ring 1 close (matches start)
    [-7.565, 110.72],   // Ring 2 start (50km+ jump)
    ...
    [-7.565, 110.72]    // Ring 2 close
]}]}
2

Split into Rings

Detect ring boundaries with coordinate matching (point equals start) and distance jump (>10km gap = new polygon).

splitRings.js
function splitRings(coords) {
  const rings = [], eps = 0.0001;
  let start = coords[0];
  let ring = [[start[1], start[0]]];

  for (let i = 1; i < coords.length; i++) {
    const c = coords[i], pt = [c[1], c[0]];
    // Split on >10km gap (new polygon group)
    if (ring.length >= 4) {
      const prev = ring[ring.length - 1];
      if (turf.distance(prev, pt,
          { units: 'kilometers' }) > 10) {
        ring.push(ring[0]);
        rings.push(ring);
        start = c; ring = [pt]; continue;
      }
    }
    ring.push(pt);
    // Split on coordinate match (ring closed)
    if (Math.abs(c[0]-start[0]) < eps
     && Math.abs(c[1]-start[1]) < eps
     && ring.length >= 4) {
      rings.push(ring); ring = [];
      if (i+1 < coords.length) {
        start = coords[i+1];
        ring = [[start[1], start[0]]]; i++;
      }
    }
  }
  if (ring.length >= 4) {
    ring.push(ring[0]); rings.push(ring);
  }
  return rings;
}
3

Filter Artifacts & Render

Reject strip artifacts by max edge length — real boundaries have short edges (<5km), artifacts have long diagonal edges (50km+).

render.js (with Turf.js + MapLibre GL)
const rings = splitRings(area.polygon);

rings.forEach((ring, i) => {
  try {
    const poly = turf.polygon([ring]);
    if (turf.area(poly) <= 0) return;

    // Reject artifacts: max edge > 20km
    let maxEdge = 0;
    for (let j = 0; j < ring.length - 1; j++) {
      const d = turf.distance(ring[j],
        ring[j+1], { units: 'kilometers' });
      if (d > maxEdge) maxEdge = d;
    }
    if (maxEdge > 20) return;

    // Add to map
    map.addSource(`area-${i}`, {
      type: 'geojson', data: poly
    });
    map.addLayer({
      id: `area-${i}-fill`,
      type: 'fill',
      source: `area-${i}`,
      paint: {
        'fill-color': '#f59e0b',
        'fill-opacity': 0.35
      }
    });
  } catch (e) { /* skip invalid */ }
});

Coordinate order: API returns [lat, lon], map libraries expect [lon, lat]. Always swap before rendering.

Why filter? The flat array format can produce thin strip artifacts connecting distant polygons. The 20km edge + 10km gap thresholds eliminate these while preserving real boundaries.

Demo Instance

This public demo has rate limits (30 requests/minute) for fair usage. For unlimited access and production use, please self-host your own instance.

BMKG Logo Data Source

All data provided by BMKG (Badan Meteorologi, Klimatologi, dan Geofisika)

This API is not affiliated with BMKG. All data belongs to BMKG.