Open API layer on Rwanda's national soil information system. Query soil carbon, pH, erosion risk, and fertilizer recommendations for every district in Rwanda — programmatically.
Parameters
| Name | Type | Description |
|---|---|---|
| — | — | No parameters required. Returns the full crop list. |
Response
[ { "id": 1, "name": "Bean" }, { "id": 2, "name": "Cassava" }, { "id": 3, "name": "Rice" }, { "id": 4, "name": "Potato" }, { "id": 5, "name": "Maize" }, { "id": 6, "name": "Wheat" } ]
Authentication
| Endpoint | Header |
|---|---|
| GET /crops | Authorization: Bearer <key> |
| GET /boundaries | X-Api-Key: <key> |
| GET /boundaries/location/ | X-Api-Key: <key> |
Using Bearer on /boundaries returns 401. This is a wrong-header issue, not an access restriction.
Note: Only Potato and Rice have fertilizer recommendations populated in the current RwaSIS release. Maize, Bean, Cassava, and Wheat recommendations are in development.
?name= parameter exists but returns empty soil arrays.
Always use province + district for real data.
Auth: X-Api-Key header (not Bearer — Bearer returns 401 on this endpoint).
Parameters
| Name | Type | Description |
|---|---|---|
| province | string | Province name. Example: Kigali, Northern, Southern |
| district | string | District name within the province. Example: Gasabo, Musanze |
| sectoroptional | string | Sector within the district for more granular data. Example: Rusororo |
Response fields
Example: GET /boundaries?province=Kigali&district=Gasabo
{ "name": "Gasabo", "carbon": 2.34, "calcium": 1820, "potassium": 187, "phValues": 5.8, "erosionRisk": "Moderate", "erosionRecommendation": "Maintain ground cover year-round. Use contour ridges on slopes >15%. Incorporate crop residues into soil.", "potatoFertilizerRecommendation": "Apply 60 kg/ha DAP at planting. Top-dress with 50 kg/ha urea at 6 weeks. Supplement with 100 kg/ha potassium sulphate.", "riceFertilizerRecommendation": "Apply 45 kg/ha DAP pre-transplant. Top-dress with 60 kg/ha urea in two equal splits at 3 and 6 weeks.", "boundary": { "type": "Polygon", "coordinates": [[ 30.12, -1.86 ], [ 30.18, -1.86 ], ...] } }
X-Api-Key authentication (not Bearer).
The API uses two different auth schemes: /crops uses
Authorization: Bearer <key>, while
/boundaries and /boundaries/location/
use X-Api-Key: <key>. UPI parcel queries return real fertilizer data.
Note: /boundaries?name= currently returns empty soil arrays — data not yet populated by RAB.
Two modes
| Mode | keyword value | Returns |
|---|---|---|
| Name lookup | Gasabo |
Matching districts, sectors, cells, villages grouped by type. Use this to resolve a name to province/district/sector before calling /boundaries. |
| UPI lookup | 1/03/08/04/2148 |
Resolved location hierarchy (province, district, sector, cell, village) + fertilizer recommendations. |
Two-step usage pattern
/boundaries/location/?keyword=<name> to resolve the name to province/district/sector/boundaries?province=X&district=Y§or=Z with the resolved values to get soil dataParameters
| Name | Type | Description |
|---|---|---|
| keyword | string |
Location name (leaf name only — e.g. Rusororo, not "Kigali Gasabo Rusororo")or UPI parcel ID (e.g. 1/03/08/04/2148)
|
UPI format decoded
Example: GET /boundaries/location/?keyword=1/03/08/04/2148
Kigali City
Kicukiro
Masaka
Gitaraga
Kabeza
Soil · Erosion · Fertilizer
Name search — confirmed behavior
keyword=Rusororo
Leaf name only — village, cell, sector, or district. Returns full location hierarchy + soil data.
keyword=Kigali+Gasabo+Rusororo
Full path returns "No Location Found". The portal dropdown label is display-only — never send it as-is.
The portal autocomplete shows the full path Kigali Gasabo Rusororo
to help disambiguate between multiple locations with the same name (there are 8 villages
named Rusororo across Rwanda). When a result is clicked, the portal extracts only the
last term and sends keyword=Rusororo to the API.
The response then tells you which province, district, sector, cell, and village it matched.
Select any of Rwanda's 30 districts to query soil data directly from the RwaSIS API. Results reflect live data from Rwanda Agriculture Board.
Query the RwaSIS API directly from your application. The API key is publicly readable from the RwaSIS frontend bundle.
import requests
BASE_URL = "https://rwasis.rab.gov.rw/api/v1"
API_KEY = "95856365-2a3f-4faa-82bc-ab9aca985b4a"
headers = {"Authorization": f"Bearer {API_KEY}"}
# --- Fetch all crops ---
crops = requests.get(f"{BASE_URL}/crops", headers=headers)
print(crops.json())
# [{'id': 1, 'name': 'Bean'}, {'id': 2, 'name': 'Cassava'}, ...]
# --- Fetch soil data by district name ---
def get_soil(district: str) -> dict:
resp = requests.get(
f"{BASE_URL}/boundaries",
params={"name": district},
headers=headers,
)
resp.raise_for_status()
return resp.json()
data = get_soil("Gasabo")
print(f"pH: {data['phValues']}, Carbon: {data['carbon']}%")
print(data['potatoFertilizerRecommendation'])
# --- Fetch soil data by UPI (land parcel ID) ---
# NOTE: /boundaries/location/ uses X-Api-Key auth (confirmed working externally)
# from the RwaSIS portal login flow — not the public Bearer key.
# This is a known limitation; RAB has been asked to unify auth.
# The endpoint URL for reference:
#
# GET /boundaries/location/?keyword=1/03/08/04/2148
#
# UPI format: Province/District/Sector/Cell/ParcelID
# Returns: Province, District, Sector, Cell, Village
# + soil data, erosion risk, fertilizer recommendations
const BASE_URL = "https://rwasis.rab.gov.rw/api/v1";
const API_KEY = "95856365-2a3f-4faa-82bc-ab9aca985b4a";
const headers = { "Authorization": `Bearer ${API_KEY}` };
// --- Fetch all crops ---
const cropsRes = await fetch(`${BASE_URL}/crops`, { headers });
const crops = await cropsRes.json();
console.log(crops);
// [{ id: 1, name: 'Bean' }, { id: 2, name: 'Cassava' }, ...]
// --- Fetch soil data by district name ---
async function getSoil(district) {
const url = new URL(`${BASE_URL}/boundaries`);
url.searchParams.set("name", district);
const res = await fetch(url, { headers });
if (!res.ok) throw new Error(`HTTP ${res.status}`);
return res.json();
}
const data = await getSoil("Gasabo");
console.log(`pH: ${data.phValues}, Carbon: ${data.carbon}%`);
console.log(data.potatoFertilizerRecommendation);
// --- Fetch by UPI (land parcel ID) ---
// NOTE: /boundaries/location/ uses X-Api-Key auth (confirmed working externally)
// from the RwaSIS portal — not the public Bearer key.
// Endpoint for reference (pending auth fix from RAB):
//
// GET /boundaries/location/?keyword=1/03/08/04/2148
//
// UPI format: Province/District/Sector/Cell/ParcelID
// Returns parcel location + full soil + fertilizer data
# All crops curl https://rwasis.rab.gov.rw/api/v1/crops \ -H "Authorization: Bearer 95856365-2a3f-4faa-82bc-ab9aca985b4a" # Soil data — Gasabo district (works with Bearer key) curl "https://rwasis.rab.gov.rw/api/v1/boundaries?name=Gasabo" \ -H "Authorization: Bearer 95856365-2a3f-4faa-82bc-ab9aca985b4a" # Soil data — Musanze district curl "https://rwasis.rab.gov.rw/api/v1/boundaries?name=Musanze" \ -H "Authorization: Bearer 95856365-2a3f-4faa-82bc-ab9aca985b4a" # UPI search — parcel-level query (X-Api-Key auth — fully working externally) # Format: Province/District/Sector/Cell/ParcelID # Format: Province/District/Sector/Cell/ParcelID curl "https://rwasis.rab.gov.rw/api/v1/boundaries/location/?keyword=1/03/08/04/2148" \ -H "X-Api-Key: 95856365-2a3f-4faa-82bc-ab9aca985b4a" # Name-based location search (leaf name only — e.g. Rusororo not "Kigali Gasabo Rusororo") curl "https://rwasis.rab.gov.rw/api/v1/boundaries/location/?keyword=Rusororo" \ -H "X-Api-Key: 95856365-2a3f-4faa-82bc-ab9aca985b4a"
rwasis-mcp is a Model Context Protocol server that wraps this API, letting AI assistants like Claude query Rwanda soil data through natural conversation — in English, French, or Kinyarwanda.
pip install rwasis-mcp
Three tools
| Tool | Description |
|---|---|
get_crops |
Returns all 6 crops in the RwaSIS system |
get_soil_by_location(name, province?, district?, sector?) |
Two-step lookup: first resolves the name via /boundaries/location/, then fetches soil data via /boundaries. Returns disambiguation options when a name matches multiple locations. Optional province, district, and sector parameters narrow down ambiguous results on the second call. |
get_soil_by_upi(upi) |
Resolves a UPI parcel ID to location hierarchy + fertilizer recommendations |
Handling Ambiguous Location Names
Many location names in Rwanda exist at multiple administrative levels or in multiple provinces. "Gasabo" is both a district in Kigali and a cell in Rutunga sector, and a village in 8 different locations. "Rusororo" is a sector in Gasabo, a cell in Ngororero, and a village in 7 different places.
When get_soil_by_location is called with an ambiguous name,
it returns all matches grouped by type instead of guessing. Claude presents the options and asks the user to pick.
Example conversation
get_soil_by_location(name="Gasabo", province="Kigali", district="Gasabo")
→ returns real soil data for Gasabo district.
Disambiguation parameters
| Parameter | Type | Example | Purpose |
|---|---|---|---|
| provinceoptional | string | "Kigali" |
Narrow results by province |
| districtoptional | string | "Gasabo" |
Resolve directly to district level |
| sectoroptional | string | "Rusororo" |
Resolve to sector level |
| all three | — | province + district + sector |
Pinpoint a specific sector directly |
Resolution priority:
Sector beats district beats auto-resolve. Passing district="Gasabo" and
sector="Rusororo" together resolves to sector level, not district level.
Village and cell picks automatically resolve to their parent sector, since the API supports data at sector level and above.
Claude Desktop setup
{
"mcpServers": {
"rwasis": {
"command": "rwasis-mcp"
}
}
}