API v1 stable
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
Layers
GET /api/v1/layers
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
GET /api/v1/layers/:id
Single layer detail with download URLs, attribution, and column schema.
curl https://bharatlas.com/api/v1/layers/lgd_states
GET /api/v1/layers/:id/schema
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
GET /api/v1/layers/:id/query
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).
Spatial
GET /api/v1/locate
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
}
GET /api/v1/nearby
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"
Categories and Levels
GET /api/v1/categories
List all categories with layer counts.
curl https://bharatlas.com/api/v1/categories
GET /api/v1/levels
List admin hierarchy levels in order (country, state, district, subdistrict, block, village, ...).
curl https://bharatlas.com/api/v1/levels
Counts and Stats
GET /api/v1/layers/:id/counts
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.
Downloads
GET /api/v1/downloads/counts
Download counts per layer and format, plus a grand total.
curl https://bharatlas.com/api/v1/downloads/counts
Community Submissions
GET /api/v1/submissions
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 |
GET /api/v1/submissions/:id
Single submission detail.
Response Format
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 }
Rate Limits
| 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.
Versioning
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.
Download Formats
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 |
collect API (write) stable
collect is the write side: design a map form, collect points from contributors, and publish approved points into this catalog. It runs as a separate service with its own origin and auth, so it has its own section here rather than sitting under /api/v1 above.
Base URL: https://collect.bharatlas.com/api/collect/v1
Auth is a bearer token in the URL fragment of each share link (kept out of server logs); the field-definition contract is the public meta-schema at https://collect.bharatlas.com/schema/v1.json. Creating a map programmatically needs an API key (the anti-abuse gate) via the X-API-Key header, since there is no browser Turnstile.
| Method | Endpoint | What |
|---|---|---|
| POST | /collections | create a map (X-API-Key, or Turnstile in the browser); returns admin / collect / view links |
| GET | /collections/:id | map metadata + counts |
| PATCH | /collections/:id | edit settings (admin) |
| POST | /collections/:id/records | add one point (contributor) |
| GET | /collections/:id/records | records as GeoJSON, scoped by token |
| POST | /collections/:id/import | bulk import with a source + rights attestation (admin) |
| POST | /records/:id/moderate | approve / reject one point (admin) |
| POST | /collections/:id/publish | submit approved points to the catalog for review (admin) |
# 1. Create a map (needs an API key; body follows the meta-schema)
curl -X POST https://collect.bharatlas.com/api/collect/v1/collections \
-H "X-API-Key: cak_..." -H "content-type: application/json" \
-d '{ ...map definition per /schema/v1.json... }'
# returns admin / collect / view links; each link's token lives in its URL fragment
# 2. Review incoming points (admin token from the returned admin link)
curl https://collect.bharatlas.com/api/collect/v1/collections/ID/records \
-H "authorization: Bearer adm_..."
# 3. Submit approved points to the catalog for review (admin token)
# a maintainer approves the submission before it appears in the catalog
curl -X POST https://collect.bharatlas.com/api/collect/v1/collections/ID/publish \
-H "authorization: Bearer adm_..."
Field contributors add points from the browser via the shared collect link (Turnstile-gated), so that step is a tap, not a curl. Or drive the whole flow from an LLM: the bharatlas MCP (npx bharatlas-mcp) authors collect maps too. Register a share link with register_map and its token scope (view / collect / admin) sets what you can do.
Common questions
Do I need an API key to use the bharatlas API?
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.
How do I list all geo layers via the API?
GET https://bharatlas.com/api/v1/layers returns every curated layer with pagination. Optional filters: ?category=, ?level=, ?source=, ?q= for full-text search.
How do I find what state or district a coordinate is in?
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.
How do I find features near a point?
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.
How do I query a layer with column filters?
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.
What download formats does bharatlas support?
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.
How do I connect an LLM to the bharatlas API?
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 to query the catalog (schema-first querying, spatial joins) and, with a collect share link, to author your own maps.
How do I collect map data from the field with a shared form?
Use collect (collect.bharatlas.com), the write side. Design a small form, share a link, and anyone who has it adds points from their phone with no account. You review incoming points, then publish the approved ones to this catalog, where a curator approves the submission before it appears. API base: https://collect.bharatlas.com/api/collect/v1.
How do I manage or publish a collect map from an LLM?
Create the map online at collect.bharatlas.com (no account, no key), then use the bharatlas MCP (npx bharatlas-mcp): register_map with a share link. The link scope sets what you can do: view reads and exports published points, collect adds in the browser, admin can edit, moderate, import and publish. The link is the credential, so no API key is needed for maps you made online.
How do I create a collect map programmatically?
POST https://collect.bharatlas.com/api/collect/v1/collections with an X-API-Key header (the anti-abuse gate; the browser flow uses Turnstile instead). It returns admin, collect and view links whose tokens live in the URL fragment. Then GET /collections/:id/records to review and POST /collections/:id/publish to submit approved points to this catalog for review (a maintainer approves before it appears). Or from an LLM: the bharatlas MCP (npx bharatlas-mcp) authors collect maps too, register your share link with register_map then moderate and publish.
Where do published collect maps show up?
As community submissions, once a curator approves them. GET https://bharatlas.com/api/v1/submissions lists approved submissions, and the read MCP tool list_submissions surfaces them to an LLM. An approved collect map is queryable via /api/v1/layers/:id/query like any other layer.
For LLMs
Use the bharatlas MCP server (npx bharatlas-mcp) to connect Claude, GPT, Gemini, or any MCP-compatible LLM directly to this API. 17 tools with built-in instructions: 8 for reading (schema-first querying, spatial joins, multi-source awareness) plus 9 for authoring collect maps.