Download OpenAPI specification:
⚠️ Beta Notice
This API is currently in beta. Features, endpoints, and schemas are subject to change before the final release. We appreciate your feedback during this period and recommend pinning to specific behavior only after GA.
API documentation for SoilSense soil monitoring services.
To use the SoilSense API, you'll need to create an API key:
curl -H "x-api-key: YOUR_API_KEY_HERE" \
https://api.app.soilsense.io/api/farm
You can try the endpoints out in our Swagger interface at https://api.app.soilsense.io/swagger.
All API endpoints require authentication using an API key in the request header:
x-api-key: your_api_key_here
⚠️ Security: Never expose your API key in client-side code or public repositories.
The API implements universal rate limiting that applies consistently across all endpoints:
All limits are applied per API key (or IP address if no API key is provided):
All three rate limits are enforced simultaneously.
Any single limit can block your request. The rate limit headers in the response show information from the most recently applied rate limiter.
Every response includes rate limit information:
RateLimit-Limit: 600
RateLimit-Remaining: 287
RateLimit-Reset: 2024-06-25T14:30:00.000Z
RateLimit-Policy: 600;w=60
limit;w=window_secondsWhen you exceed any rate limit, you'll receive a 429 status with error message.
The API uses standard HTTP status codes to indicate the success or failure of API calls:
200 OK - Request successful
400 Bad Request - Invalid request parameters
401 Unauthorized - Missing or invalid API key
403 Forbidden - API key valid but lacks permission for this resource
404 Not Found - Resource not found
429 Too Many Requests - Rate limit exceeded
500 Internal Server Error - Server-side error
All error responses follow this JSON format:
{
"error": "Description of what went wrong"
}
{
"error": "Rate limit exceeded. Maximum 300 requests per minute."
}
Returns basic farm information including observation sites that belong to the farm associated with the API key
| includeArchived | boolean Default: false Whether to include archived observation sites in the response |
| id required | string Unique identifier of the farm |
| name required | string Name of the farm |
required | object |
required | Array of objects List of observation sites with active status |
curl -H "x-api-key: YOUR_API_KEY_HERE" \ https://api.app.soilsense.io/api/farm
{- "id": "farm_123456",
- "name": "Green Valley Farm",
- "location": {
- "coordinates": {
- "lat": -1.2921,
- "lng": 36.8219
}, - "country": "Kenya",
- "countryCode": "KE"
}, - "observationSites": [
- {
- "id": "site_789",
- "name": "North Field",
- "coordinates": {
- "lat": -1.2921,
- "lng": 36.8219
}, - "cropType": "Maize",
- "locationType": "Field",
- "active": true,
- "currentDataLogger": {
- "id": 12345,
- "name": "Data Logger 12345"
}, - "lastDataLogger": {
- "id": 12345,
- "name": "Data Logger 12345"
}, - "configuration": {
- "cableTop": {
- "depth": 10,
- "name": "Top sensor",
- "calibration": {
- "fieldCapacity": 30,
- "wiltingPoint": 10
}
}, - "cableMiddle": {
- "depth": 10,
- "name": "Top sensor",
- "calibration": {
- "fieldCapacity": 30,
- "wiltingPoint": 10
}
}, - "cableMiddleBottom": {
- "depth": 10,
- "name": "Top sensor",
- "calibration": {
- "fieldCapacity": 30,
- "wiltingPoint": 10
}
}, - "cableBottom": {
- "depth": 10,
- "name": "Top sensor",
- "calibration": {
- "fieldCapacity": 30,
- "wiltingPoint": 10
}
}
}
}
]
}Returns a simplified list of observation sites with their IDs and basic information
| includeArchived | boolean Default: false Whether to include archived observation sites in the response |
| id required | string Unique identifier of the observation site |
| name required | string Name of the observation site |
| active required | boolean Whether the site is currently active (has a data logger assigned) |
| currentDataLoggerId | number Current data logger device number (only present if active) |
curl -H "x-api-key: YOUR_API_KEY_HERE" \ https://api.app.soilsense.io/api/farm/sites
[- {
- "id": "site_789",
- "name": "North Field",
- "active": true,
- "currentDataLoggerId": 12345
}
]Returns comprehensive information about an observation site including current and historical data source assignments, site metadata, and safe ranges
| siteId required | string ID of the observation site |
| includeHistory | boolean Default: false Whether to include the full observation site history (data source assignments over time) |
| id | string Site ID |
| name | string Site name |
object | |
| cropType | string Type of crop grown at this site |
| locationType | string Type of location |
| frostThreshold | number Temperature threshold for frost alerts (°C) |
| frostAlertEnabled | boolean Whether frost alerts are enabled |
object Safe operating ranges for various measurements | |
object Currently active data logger | |
| status | string Enum: "active" "archived" Current status of the observation site |
Array of objects Historical data source assignments for this observation site (only included if includeHistory=true) |
{- "id": "site_789",
- "name": "North Field",
- "coordinates": {
- "lat": -1.2921,
- "lng": 36.8219
}, - "cropType": "Maize",
- "locationType": "Field",
- "frostThreshold": 2,
- "frostAlertEnabled": true,
- "safeRanges": {
- "plantAvailableWater": [
- 20,
- 80
], - "volumetricWaterContent": [
- 15,
- 40
]
}, - "currentDataLogger": {
- "id": 12345,
- "name": "Data Logger 12345",
- "activeSince": 1640995200000,
- "configuration": {
- "cableTop": {
- "depth": 10,
- "name": "Top sensor",
- "calibration": {
- "fieldCapacity": 30,
- "wiltingPoint": 10
}
}, - "cableMiddle": {
- "depth": 10,
- "name": "Top sensor",
- "calibration": {
- "fieldCapacity": 30,
- "wiltingPoint": 10
}
}, - "cableMiddleBottom": {
- "depth": 10,
- "name": "Top sensor",
- "calibration": {
- "fieldCapacity": 30,
- "wiltingPoint": 10
}
}, - "cableBottom": {
- "depth": 10,
- "name": "Top sensor",
- "calibration": {
- "fieldCapacity": 30,
- "wiltingPoint": 10
}
}
}
}, - "status": "active",
- "observationSiteHistory": [
- {
- "dataSource": {
- "id": 0,
- "name": "string",
- "configuration": {
- "cableTop": {
- "depth": 10,
- "name": "Top sensor",
- "calibration": {
- "fieldCapacity": 30,
- "wiltingPoint": 10
}
}, - "cableMiddle": {
- "depth": 10,
- "name": "Top sensor",
- "calibration": {
- "fieldCapacity": 30,
- "wiltingPoint": 10
}
}, - "cableMiddleBottom": {
- "depth": 10,
- "name": "Top sensor",
- "calibration": {
- "fieldCapacity": 30,
- "wiltingPoint": 10
}
}, - "cableBottom": {
- "depth": 10,
- "name": "Top sensor",
- "calibration": {
- "fieldCapacity": 30,
- "wiltingPoint": 10
}
}
}
}, - "startTimestamp": 0,
- "endTimestamp": 0
}
]
}Returns sensor observations for a specific observation site within the specified timestamps. Can optionally include plant available water calculations.
| siteId required | string ID of the observation site |
| startTime required | integer <int64> Start timestamp in milliseconds since epoch |
| endTime | integer <int64> >= 0 Optional end timestamp in milliseconds since epoch. Leave empty in Swagger UI to use the current server time. |
| timestamp | integer <int64> Timestamp of the observation in milliseconds since epoch |
| rssi | number Signal strength of the device at time of transmission (dBm) |
object |
curl -H "x-api-key: YOUR_API_KEY_HERE" \ "https://api.app.soilsense.io/api/site/SITE_ID/observations?startTime=1715155200000&endTime=1715241600000"
[- {
- "timestamp": 1640995200000,
- "rssi": -75,
- "data": {
- "top": {
- "volumetricWaterContent": 25.5,
- "temperature": 18.5,
- "plantAvailableWater": 75.2,
- "salinity": 0.45
}, - "mid": {
- "volumetricWaterContent": 28.2,
- "temperature": 19.2,
- "plantAvailableWater": 68.7,
- "salinity": 0.52
}, - "midBot": {
- "volumetricWaterContent": 30.1,
- "temperature": 19.8,
- "plantAvailableWater": 72.1,
- "salinity": 0.48
}, - "bot": {
- "volumetricWaterContent": 32.8,
- "temperature": 20.1,
- "plantAvailableWater": 80.3,
- "salinity": 0.55
}, - "box": {
- "temperature": 22.1,
- "batteryVoltage": 3.7
}, - "status": {
- "irrigationStatus": "ok",
- "irrigationStatusColorSuggestion": "green"
}
}
}
]Returns precipitation measurements from rain gauge data for the observation site within the specified timestamps.
| siteId required | string ID of the observation site |
| startTime required | integer <int64> Example: startTime=1715155200000 Start timestamp in milliseconds since epoch |
| endTime | integer <int64> >= 0 Optional end timestamp in milliseconds since epoch. Leave empty in Swagger UI to use the current server time. |
| timestamp | integer <int64> Timestamp in milliseconds since epoch |
| mm | number Precipitation amount in millimeters |
curl -H "x-api-key: YOUR_API_KEY_HERE" \ "https://api.app.soilsense.io/api/site/SITE_ID/precipitation?startTime=1715155200000&endTime=1715241600000"
[- {
- "timestamp": 0,
- "mm": 0
}
]Returns the most recent sensor observation for a specific observation site
| siteId required | string ID of the observation site |
| timestamp | integer <int64> Timestamp of the observation in milliseconds since epoch |
| rssi | number Signal strength of the device at time of transmission (dBm) |
object |
curl -H "x-api-key: YOUR_API_KEY_HERE" \ https://api.app.soilsense.io/api/site/SITE_ID/observations/latest
{- "timestamp": 1640995200000,
- "rssi": -75,
- "data": {
- "top": {
- "volumetricWaterContent": 25.5,
- "temperature": 18.5,
- "plantAvailableWater": 75.2,
- "salinity": 0.45
}, - "mid": {
- "volumetricWaterContent": 28.2,
- "temperature": 19.2,
- "plantAvailableWater": 68.7,
- "salinity": 0.52
}, - "midBot": {
- "volumetricWaterContent": 30.1,
- "temperature": 19.8,
- "plantAvailableWater": 72.1,
- "salinity": 0.48
}, - "bot": {
- "volumetricWaterContent": 32.8,
- "temperature": 20.1,
- "plantAvailableWater": 80.3,
- "salinity": 0.55
}, - "box": {
- "temperature": 22.1,
- "batteryVoltage": 3.7
}, - "status": {
- "irrigationStatus": "ok",
- "irrigationStatusColorSuggestion": "green"
}
}
}const API_KEY = 'YOUR_API_KEY_HERE';
const BASE_URL = 'https://api.app.soilsense.io';
// Get observation sites
const sitesResponse = await fetch(`${BASE_URL}/api/farm/sites`, {
headers: { 'x-api-key': API_KEY }
});
const sites = await sitesResponse.json();
// Calculate last week timestamps (Unix timestamps in milliseconds)
const endTime = Date.now();
const startTime = endTime - (7 * 24 * 60 * 60 * 1000); // 7 days ago
// Loop through sites with rate limiting in mind
for (const site of sites) {
// Get site details
const siteDetailResponse = await fetch(
`${BASE_URL}/api/farm/site/${site.id}`,
{ headers: { 'x-api-key': API_KEY } }
);
const siteDetails = await siteDetailResponse.json();
// Get observations for last week using Unix timestamps
const observationsResponse = await fetch(
`${BASE_URL}/api/site/${site.id}/observations?startTime=${startTime}&endTime=${endTime}`,
{ headers: { 'x-api-key': API_KEY } }
);
const observations = await observationsResponse.json();
console.log(`Site: ${siteDetails.name}, Observations:`, observations);
// Optional: Add small delay between requests to stay well within rate limits
await new Promise(resolve => setTimeout(resolve, 100)); // 100ms delay
}
Configure webhook URLs in the SoilSense interface under device settings.
SoilSense will automatically send data to your configured endpoints:
POST {your_webhook_url}/telemetryPOST {your_webhook_url}/attributesThis endpoint should be implemented by you (the API user).
SoilSense will automatically send sensor telemetry data to {your_webhook_url}/telemetry when new observations are received from your devices.
When you configure a secret key for your webhook, SoilSense will include a signature in the request header:
SoilSense-Signature: Contains timestamp and HMAC signature in format t=<unix_ms>,v1=<hex_hmac>Verify the signature to ensure the request came from SoilSense and wasn't tampered with.
Configure your webhook URL and optional secret key in the SoilSense interface under device settings.
| SoilSense-Signature | string Example: t=1640995200000,v1=a1b2c3d4e5f6789... Signature for verification (only present if secret is configured).
Format:
|
| ts required | integer <int64> Unix timestamp in milliseconds |
required | object Sensor measurement values (undefined values are omitted) |
{- "ts": 1640995200000,
- "values": {
- "depths": {
- "top": 10,
- "middle": 30,
- "middleBottom": 50,
- "bottom": 70
}, - "volumetricWaterContent_top": 25.5,
- "volumetricWaterContent_middle": 28.2,
- "volumetricWaterContent_middleBottom": 30.1,
- "volumetricWaterContent_bottom": 32.8,
- "soilTemperature_top": 18.5,
- "soilTemperature_middle": 19.2,
- "soilTemperature_middleBottom": 19.8,
- "soilTemperature_bottom": 20.1,
- "salinity_top": 0.45,
- "salinity_middle": 0.52,
- "salinity_middleBottom": 0.48,
- "salinity_bottom": 0.55,
- "plantAvailableWater_top": 75.2,
- "plantAvailableWater_middle": 68.7,
- "plantAvailableWater_middleBottom": 72.1,
- "plantAvailableWater_bottom": 80.3,
- "temperature_box": 22.1,
- "batteryVoltage_box": 3.7,
- "irrigationStatus": "ok",
- "irrigationStatusColorSuggestion": "green"
}
}This endpoint should be implemented by you (the API user).
SoilSense will automatically send observation site attributes to {your_webhook_url}/attributes when sites are created or modified.
Uses the same security protocol as the telemetry webhook. See the telemetry endpoint documentation for verification examples.
Configure your webhook URL and optional secret key in the SoilSense interface under device settings.
| SoilSense-Signature | string Example: t=1640995200000,v1=a1b2c3d4e5f6789... Signature for verification (only present if secret is configured).
Format: |
| siteId required | string Unique identifier of the observation site |
| siteName required | string Name of the observation site |
required | object |
| cropType | string Type of crop at this observation site |
| locationType | string Type of location |
| frostThreshold | number Temperature threshold for frost alerts (°C) |
| frostAlertEnabled | boolean Whether frost alerts are enabled for this site |
object Sensor configuration | |
| deviceNumber required | string Data logger device number assigned to this site |
| deviceName | string Display name of the data logger device |
| farmId required | string Unique identifier of the farm |
| updatedAt required | string <date-time> ISO timestamp when the site was last updated |
{- "siteId": "site_123456",
- "siteName": "North Field",
- "coordinates": {
- "latitude": -1.2925,
- "longitude": 36.822
}, - "cropType": "Maize",
- "locationType": "Field",
- "frostThreshold": 2,
- "frostAlertEnabled": true,
- "configuration": {
- "cableTop": {
- "depth": 10,
- "name": "Top sensor",
- "calibration": {
- "fieldCapacity": 30,
- "wiltingPoint": 10
}
}, - "cableMiddle": {
- "depth": 10,
- "name": "Top sensor",
- "calibration": {
- "fieldCapacity": 30,
- "wiltingPoint": 10
}
}, - "cableMiddleBottom": {
- "depth": 10,
- "name": "Top sensor",
- "calibration": {
- "fieldCapacity": 30,
- "wiltingPoint": 10
}
}, - "cableBottom": {
- "depth": 10,
- "name": "Top sensor",
- "calibration": {
- "fieldCapacity": 30,
- "wiltingPoint": 10
}
}
}, - "deviceNumber": "12345",
- "deviceName": "Data Logger 12345",
- "farmId": "farm_123456",
- "updatedAt": "2023-12-01T10:30:00.000Z"
}