Skip to content

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

Production: https://leaf-asrlm.in
Local:      http://localhost:5000

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=1 to the query string, or send the header X-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:

{
  "error": "Description of what went wrong"
}

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:

{
  "column": "AD",
  "min_val": 20,
  "max_val": 60,
  "weight": 1.0
}
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)

import requests

r = requests.get("https://leaf-asrlm.in/api/blocks")
geojson = r.json()
print(f"Total blocks: {len(geojson['features'])}")

# Get unique districts
districts = {f['properties']['Dist_Name'] for f in geojson['features']}
print(f"Districts: {len(districts)}")
curl -s https://leaf-asrlm.in/api/blocks \
  | python -c "import sys,json; d=json.load(sys.stdin); print(len(d['features']),'blocks')"

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}`);

Load blocks into GeoPandas (Python)

import requests
import geopandas as gpd

r = requests.get("https://leaf-asrlm.in/api/blocks")
gdf = gpd.GeoDataFrame.from_features(r.json()['features'])
print(gdf[['Block_name', 'Dist_Name', 'AD', 'WA']].head())