Public, read-only JSON API for India's open geo layers. No auth, no API key. All endpoints return JSON with CORS enabled.
Base URL: https://bharatlas.com/api/v1
List all curated layers with pagination and filters.
| Param | Type | Description |
|---|---|---|
category | string | Filter by category (e.g. boundaries, city-wards, environment) |
level | string | Filter by admin level (e.g. state, district) |
source | string | Filter by data source (e.g. LGD, OpenCity) |
q | string | Text search across id, source, notes |
limit | int | Results per page (default 100, max 500) |
offset | int | Skip N results (default 0) |
curl https://bharatlas.com/api/v1/layers?category=city-wards&limit=5
Single layer detail with download URLs, attribution, and column schema.
curl https://bharatlas.com/api/v1/layers/lgd_states
Column names, types, and sample values. Reads parquet metadata from R2 at runtime. Use this to discover what columns a layer has before querying.
curl https://bharatlas.com/api/v1/layers/airports/schema
Query layer data with filters and grouping. Reads the parquet from R2 at runtime, no pre-computation needed. Works for curated and community layers.
| Param | Type | Description |
|---|---|---|
select | string | Comma-separated column names to return |
where | string | Filters: col1=val1,col2=val2. Or pass columns directly as params: ?state=KA&type=INTERNATIONAL |
group_by | string | Group by column, returns value counts |
limit | int | Max rows (default 100, max 1000) |
# How many airports in Bengaluru district?
curl "https://bharatlas.com/api/v1/layers/airports/query?where=district=Bengaluru Urban&select=name,type"
# How many reserve forests in Karnataka?
curl "https://bharatlas.com/api/v1/layers/soi_forests/query?where=State_LGD=29&group_by=type"
# National parks vs wildlife sanctuaries
curl "https://bharatlas.com/api/v1/layers/gs_wildlife/query?group_by=type"
# Which blocks are in district 571?
curl "https://bharatlas.com/api/v1/layers/lgd_blocks/query?where=Dist_LGD=571&select=BNAME,Block_LGD"
Columns with geometry data (geom, wkb_geometry) are excluded from results. For large layers, queries may take 1-5 seconds on first call (cached afterward).
Given a lat/lng, returns all polygon layers that contain that point: state, district, ward, pincode, forest, eco-zone, seismic zone, and more. The "where am I?" endpoint.
| Param | Type | Description |
|---|---|---|
lat | float * | Latitude (6 to 38, India bounding box) |
lng | float * | Longitude (68 to 98, India bounding box) |
layers | string | Comma-separated layer IDs (default: 12 essential layers) |
zoom | int | Tile resolution 4-16 (default 14, auto-capped to layer max zoom) |
curl "https://bharatlas.com/api/v1/locate?lat=12.97&lng=77.59"
Response groups results by category:
{
"point": { "lat": 12.97, "lng": 77.59 },
"results": {
"boundaries": [{
"layer_id": "lgd_states",
"level": "state",
"feature": {
"properties": { "STNAME": "KARNATAKA", "State_LGD": 29 }
}
}]
},
"queried_layers": ["lgd_states", "lgd_districts", ...],
"timing_ms": 450
}
Find features from any layer near a point. Reads PMTiles tiles in a radius, extracts features with computed centroids, filters by Haversine distance. Works for points, polygons, and lines.
| Param | Type | Description |
|---|---|---|
lat | float * | Center latitude (6-38) |
lng | float * | Center longitude (68-98) |
layer | string * | Layer ID to search |
radius_km | float | Search radius in km (default 25, max 200) |
limit | int | Max results (default 20, max 100) |
# Reservoirs within 50km of Bengaluru
curl "https://bharatlas.com/api/v1/nearby?lat=12.97&lng=77.59&layer=wris_reservoirs&radius_km=50"
# Wildlife sanctuaries within 100km of Mysuru
curl "https://bharatlas.com/api/v1/nearby?lat=12.30&lng=76.65&layer=gs_wildlife&radius_km=100"
List all categories with layer counts.
curl https://bharatlas.com/api/v1/categories
List admin hierarchy levels in order (country, state, district, subdistrict, block, village, ...).
curl https://bharatlas.com/api/v1/levels
Feature counts with optional column grouping. Without group_by, returns the total and lists all groupable columns. With group_by, returns value-level counts for that column.
| Param | Type | Description |
|---|---|---|
group_by | string | Column name to group by (e.g. statename, wardname, zone) |
# List groupable columns
curl https://bharatlas.com/api/v1/layers/soi_forests/counts
# Count forests per state
curl https://bharatlas.com/api/v1/layers/soi_forests/counts?group_by=statename
# Count wards per zone in PCMC
curl https://bharatlas.com/api/v1/layers/wards_pcmc/counts?group_by=zone
Columns with fewer than 200 distinct values are pre-computed and available for grouping. The column list is auto-detected from the parquet schema, no hardcoding.
Download counts per layer and format, plus a grand total.
curl https://bharatlas.com/api/v1/downloads/counts
List accepted community submissions.
| Param | Type | Description |
|---|---|---|
sort | string | recent (default) or useful |
category | string | Filter by category |
q | string | Search name and description |
limit | int | Results per page (default 20, max 100) |
offset | int | Skip N results |
Single submission detail.
All endpoints return JSON. List endpoints wrap results in:
{ "data": [...], "total": N, "limit": 100, "offset": 0 }
Detail endpoints wrap in:
{ "data": { ... } }
Errors return:
{ "error": "Not found", "status": 404 }
| Endpoint | Limit |
|---|---|
| All read endpoints | 120 requests/minute per IP |
/locate, /nearby | 60 requests/minute per IP |
| Submissions (write) | 5/hour, 20/day per IP |
Exceeded limits return 429 with a retry-after header. Cloudflare DDoS protection applies to all endpoints.
All v1 endpoints follow additive-only evolution: new fields may appear, but existing fields will not be renamed or removed. If a breaking change is needed, it ships as v2. All responses include an x-api-version: v1 header.
Each layer may offer up to 5 formats. Use the downloads object from /api/v1/layers/:id to get direct URLs:
| Format | Best for |
|---|---|
parquet | DuckDB, Pandas, R, Spark |
pmtiles | MapLibre, Leaflet, web maps |
geojson | QGIS, D3, Mapbox, general GIS |
kml | Google Earth |
shapefile | ArcGIS, legacy GIS tools |
No. The API is public, read-only and CORS-enabled. No auth, no signup, no API key. Rate limit is 120 requests per minute per IP for most endpoints, 60/min for /locate and /nearby.
GET https://bharatlas.com/api/v1/layers returns every curated layer with pagination. Optional filters: ?category=, ?level=, ?source=, ?q= for full-text search.
GET https://bharatlas.com/api/v1/locate?lat=12.97&lng=77.59 runs point-in-polygon across 12 admin and zone layers in a single call. Returns state, district, ward, pincode, seismic zone, eco-zone and more.
GET https://bharatlas.com/api/v1/nearby?lat=12.97&lng=77.59&layer=wris_reservoirs&radius_km=50 finds features from any layer within a radius. Works for points, polygons and lines via Haversine distance to computed centroids.
GET https://bharatlas.com/api/v1/layers/:id/query?where=col=val&select=col1,col2&group_by=col. Reads parquet directly from R2 at runtime. Call /schema first to discover column names.
Up to 5 formats per layer: parquet (DuckDB, Pandas, R, Spark), pmtiles (web maps), geojson (QGIS, D3), kml (Google Earth) and shapefile (ArcGIS). Use /api/v1/layers/:id to get direct download URLs.
Use the bharatlas MCP server: add bharatlas-mcp to your MCP client config and run npx -y bharatlas-mcp. Connects Claude, GPT, Gemini or any MCP-compatible LLM with 8 tools and built-in instructions for spatial joins and schema-first querying.
Use the bharatlas MCP server (npx bharatlas-mcp) to connect Claude, GPT, Gemini, or any MCP-compatible LLM directly to this API. 8 tools with built-in instructions for schema-first querying, spatial joins, and multi-source awareness.