Leaf’s API outputs GeoJSON, GeoTIFF, and PNG data that maps directly into ArcGIS workflows. This tutorial covers two integration patterns: consuming data in ArcGIS Pro with Python toolboxes, and automating data ingest in ArcGIS Enterprise with geoprocessing services.
Before you start
- A Leaf account with API credentials and at least one connected provider or sample data.
- ArcGIS Pro 3.x or ArcGIS Enterprise 11.x (or later).
- Python 3.9+ with
requests and arcpy available (bundled with ArcGIS Pro).
- The sample toolboxes from Leaf’s ArcGIS GitHub repo.
ArcGIS Pro
Step 1: Set up authentication
Communication with the Leaf API requires a bearer token. The sample toolbox includes an authentication tool that stores the token in a temporary table for use by other tools.
import requests
response = requests.post(
"https://api.withleaf.io/api/authenticate",
json={"username": "your-email@example.com", "password": "your-password"}
)
token = response.json()["id_token"]
Run the Leaf Authentication tool in the sample toolbox, or store the token programmatically. Other toolbox tools check for a valid token before executing.
Step 2: Fetch field boundaries
The Get Field Boundaries tool combines data from multiple Leaf endpoints (growers, farms, fields, and boundaries) and converts them into a feature layer in your ArcGIS Pro map.
The Leaf API returns boundaries as GeoJSON. The toolbox converts them to features using arcpy.JSONToFeatures_conversion. Fields from different providers appear in a single layer with attributes like provider, farm name, and field name.
import requests, json
headers = {"Authorization": f"Bearer {token}"}
fields = requests.get(
"https://api.withleaf.io/services/fields/api/fields",
headers=headers,
params={"leafUserId": leaf_user_id, "size": 100}
).json()
for field in fields:
boundary = requests.get(
f"https://api.withleaf.io/services/fields/api/users/{leaf_user_id}/fields/{field['id']}/boundary",
headers=headers
).json()
# Convert to feature using arcpy
Step 3: Display satellite imagery
Leaf’s crop monitoring endpoints return GeoTIFF and PNG images for NDVI, NDRE, and RGB. You can download these and add them as raster layers.
images = requests.get(
f"https://api.withleaf.io/services/satellite/api/fields/{satellite_field_id}/processes",
headers=headers,
params={"startDate": "2025-01-01", "endDate": "2025-12-31"}
).json()
for image in images:
ndvi_image = next(
(img for img in image.get("images", []) if img.get("type") == "tif_colorized"),
None
)
if ndvi_image:
ndvi_url = ndvi_image["downloadUrl"]
response = requests.get(ndvi_url, headers=headers)
with open(f"/tmp/ndvi_{image['date']}.tif", "wb") as f:
f.write(response.content)
Add the downloaded GeoTIFFs to your map as raster layers. Leaf images are already georeferenced and clipped to the field boundary.
Step 4: Load field operations
Field operations expose a standard GeoJSON download URL. Fetch the download URL first, then download the actual GeoJSON and convert it to a feature layer:
op_id = "your-operation-id"
geojson_ref = requests.get(
f"https://api.withleaf.io/services/operations/api/operations/{op_id}/standardGeojson",
headers=headers
).json()
geojson = requests.get(
geojson_ref["downloadStandardGeojson"],
headers=headers
).json()
with open("/tmp/operation.geojson", "w") as f:
json.dump(geojson, f)
arcpy.conversion.JSONToFeatures("/tmp/operation.geojson", "operation_layer")
The GeoJSON features have standardized properties like yieldVolume, seedRate, and appliedRate that you can use for symbology and analysis.
ArcGIS Enterprise
For automated workflows, publish a geoprocessing service that acts as a webhook for Leaf alerts. When new data arrives, Leaf sends an alert to your geoprocessing service, which downloads and processes the data automatically.
Step 5: Create a geoprocessing webhook
Write a Python toolbox that accepts the parameters for the specific Leaf event you subscribe to. For newSatelliteImage, the payload includes:
| Parameter | Type | Description |
|---|
externalId | String | Your satellite field external ID |
processId | String | The satellite process ID |
type | String | Alert type, for example newSatelliteImage |
timestamp | String | When the alert fired |
X-Leaf-Signature | String | HMAC signature for validation |
Step 6: Validate the signature
Every alert from Leaf includes an X-Leaf-Signature header. Validate it against your secret to confirm the request came from Leaf:
import base64
import hmac
import hashlib
def validate_signature(payload: bytes, signature: str, secret: str) -> bool:
expected = hmac.new(
secret.encode(), payload, hashlib.sha256
).digest()
provided = base64.b64decode(signature)
return hmac.compare_digest(expected, provided)
Step 7: Publish and register
- Publish the geoprocessing service following ArcGIS Pro’s publishing guide.
- The service must be publicly accessible (Leaf needs to reach it).
- Register the service URL as a Leaf alert endpoint:
curl -X POST "https://api.withleaf.io/services/alerts/api/alerts/arcgis" \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"events": ["newSatelliteImage"],
"name": "Satellite images listener",
"url": "https://your-arcgis-server.com/arcgis/rest/services/LeafWebhook/GPServer/LeafWebhook/submitJob?f=json",
"secret": "your-random-secret"
}'
Use /submitJob?f=json for asynchronous geoprocessing or /execute?f=json for synchronous.
What you built
You connected Leaf’s API to ArcGIS for two workflows: interactive data exploration in ArcGIS Pro, and automated data ingest in ArcGIS Enterprise via webhook-driven geoprocessing.
The sample toolboxes on GitHub provide working implementations of these patterns. Adapt them to your specific requirements. Last modified on March 19, 2026