API Overview¶
The LEAF DSS API provides RESTful endpoints for geospatial data (blocks, districts, villages), per-commodity village clustering, feasibility analysis, infrastructure (POI) queries, and AI recommendations. All API routes are prefixed with /api/ except the health check at /health.
The backend is a Flask app (leaf_flask/app.py) running under gunicorn on Render, backed by a Supabase Postgres database (cluster data) plus committed CSV/shapefile geodata. Routes are organised into 13 blueprint modules under leaf_flask/blueprints/.
Base URL¶
Custom domain
Production is served at https://leaf-asrlm.in (custom domain configured in the Render dashboard/DNS). The underlying Render service URL (iwmi-leaf.onrender.com) may still respond, but leaf-asrlm.in is the canonical address for all clients and examples.
Authentication¶
The API is open and unauthenticated — there are no API keys, tokens, sessions, or login. Every endpoint is publicly reachable.
A handful of destructive / ops endpoints are gated behind a soft admin flag rather than real authentication:
- Add
?admin=1to the query string, or send the headerX-Admin: 1.
This is not a security boundary — it only guards the UI's "danger" actions (whole-state refresh, AI-doc upload). Affected endpoints include POST /api/clusters/refresh-all and POST /api/config/upload-ai-doc.
No security boundary
Because there is no real authentication, do not expose the destructive endpoints (refresh-all, import, regenerate, upload-ai-doc, infrastructure/import) to untrusted networks. The AI recommendation endpoints additionally require an OPENAI_API_KEY on the server (never passed by the client).
Response Format¶
All endpoints return JSON (Content-Type: application/json) except CSV endpoints (/api/export/csv, /api/clusters/export.csv, /api/clusters/unassigned.csv, /api/livestock-subfilter.csv) which return text/csv, and the doc/asset routes (/ai-docs/..., /documentation/...) which stream files.
Successful responses return data directly. Errors return:
CORS¶
Cross-Origin Resource Sharing is enabled for all origins via flask-cors. You can call the API from any frontend application, Jupyter notebook, or script without CORS issues.
Endpoint Categories¶
Counts below are Flask URL rules read directly from the route handlers in leaf_flask/blueprints/. A few rules alias the same handler (e.g. /api/blocks and /api/blocks/geojson), and some endpoints live in a blueprint file named for a different area (the Variables endpoints live in interventions.py; the AI endpoints in feasibility.py; finalize in production_tool.py).
| Category | Prefix | Endpoints | Description |
|---|---|---|---|
| Blocks | /api/blocks, /api/statistics |
8 | Block GeoJSON (+ /geojson alias), by-id/by-name lookup, SHG summary, Biophysical/Infrastructure convergence, statistics (+ alias) |
| Locations | /api/locations, /api/districts |
5 | Hierarchical location tree, district list + GeoJSON, protected-areas GeoJSON, blocks-in-district |
| Villages | /api/villages |
4 | Village points/GeoJSON, state/district aggregates, blocks-with-village-data |
| Clusters | /api/clusters |
11 | Per-commodity village clusters: params, list/get, report card, regenerate, CSV export/import edit cycle, whole-state coverage refresh + rename reconciliation, unassigned export |
| Infrastructure | /api/infrastructure |
3 | POI database (vet centres, pharmacies, input shops): list, CSV import, nearest-to-cluster/point query |
| Production Tool | /api/production-tool, /api/clusters/<id>/finalize |
3 | Finalise a cluster, outbound finalised-cluster feed, inbound aggregated dashboard exchange |
| Interventions | /api/interventions, /api/intervention, /api/livestock-subfilter |
4 | Intervention definitions, per-intervention config, livestock sub-filter CSV download/upload |
| Variables | /api/variables, /api/variable-groups, /api/variable-stats |
3 | Block-level variable metadata, groups, and per-variable statistics |
| Feasibility | /api/calculate-feasibility |
1 | Weighted, multi-criteria block feasibility scoring |
| AI | /api/ai-recommendation |
2 | RAG-powered recommendation + vector-store (re)initialisation |
| Export | /api/export/csv |
1 | Generic filtered CSV export (POST) |
| Config / Health | /api/config, /api/levels, /api, /health |
8 | App config, sheet status/validation, Google-Sheet refresh, AI-doc upload, data levels, API index, health check |
Total: 53 API endpoints across 12 categories.
In addition, the app serves 15 non-API routes — 12 server-rendered HTML pages (/, /clustering, /update, /about, and the /<district>/<block> drill-down family, in pages.py) and 3 file-serving routes (/ai-docs/<path>, /documentation/, /documentation/<path>, in levels.py) — for ~68 Flask route rules total.
Assam coverage
The village master (data/villages.csv) currently covers 35 districts and 220 blocks across Assam, aggregated into 6 livestock commodities (Dairy, Goatery, Piggery, Backyard Poultry, Duckery, Fishery Activity).
Common Patterns¶
GeoJSON Responses¶
Geospatial endpoints return standard GeoJSON FeatureCollections. Each feature includes a properties object with all variable data and a geometry object with polygon (blocks/districts) or point (villages) coordinates in WGS84 (EPSG:4326).
{
"type": "FeatureCollection",
"features": [
{
"type": "Feature",
"properties": {
"Block_name": "Digboi",
"Dist_Name": "Tinsukia",
"BLOCK_ID": "1234",
"AD": 45.2,
"WA": 30.1
},
"geometry": {
"type": "Polygon",
"coordinates": [[[95.1, 27.3], [95.2, 27.3], [95.2, 27.4], [95.1, 27.3]]]
}
}
]
}
Working with GeoJSON
GeoJSON responses can be loaded directly into mapping libraries like Leaflet, Mapbox, or QGIS. In Python, use geopandas.GeoDataFrame.from_features(response['features']) to convert to a GeoDataFrame.
Filter Criteria¶
POST /api/calculate-feasibility accepts an array of filter criteria (one per variable). Either pass explicit filters, or pass an intervention name to load that intervention's default config:
| Field | Type | Required | Description |
|---|---|---|---|
column |
string | Yes | Variable/column name from the block dataset |
min_val |
number | Yes | Minimum acceptable threshold |
max_val |
number | Yes | Maximum acceptable threshold |
weight |
number | No | Importance weight (0–1), default 1.0 |
Error Handling¶
| HTTP Code | Meaning | When |
|---|---|---|
200 |
Success | Normal response |
400 |
Bad request | Malformed body, missing required field, or invalid CSV on import endpoints |
404 |
Resource not found | Invalid block/cluster identifier, or missing data file |
500 |
Server error | Unexpected processing error (returned as {"error": "..."}) |
503 |
Service unavailable | AI/RAG dependencies or OPENAI_API_KEY not configured |
Rate Limiting¶
There are no rate limits on the API. Some endpoints (particularly GET /api/blocks and POST /api/calculate-feasibility) return large GeoJSON payloads (1–5 MB). Consider caching responses client-side when building frontend applications.
Interactive Docs¶
Two documentation interfaces are available:
| Interface | URL | Description |
|---|---|---|
| Swagger UI | /docs |
Interactive API explorer — try endpoints live with "Try it out" (spec at /apispec.json) |
| MkDocs | /documentation/ |
This written documentation with guides and examples |
Quick Examples¶
Get all blocks and count them (Python)¶
Run feasibility analysis (JavaScript)¶
The response is { "geojson": FeatureCollection, "statistics": { ... } }. Summary stats live under statistics (mean, median, high_feasibility, low_feasibility, distribution, variable_stats).
const response = await fetch('https://leaf-asrlm.in/api/calculate-feasibility', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
filters: [
{ column: 'AD', min_val: 20, max_val: 60, weight: 1.0 }
]
})
});
const result = await response.json();
console.log(`Blocks scored: ${result.geojson.features.length}`);
console.log(`Mean score: ${result.statistics.mean}`);
console.log(`High-feasibility blocks (>=75): ${result.statistics.high_feasibility}`);