Skip to main content
Upload soil sample .zip files, track processing status, and download normalized results in GeoJSON and canonical JSON formats. For output format details, see Soil Sampling Overview.
The Soil Sampling service is currently available by invitation. Contact your account team to request access.

Base URL

https://api.withleaf.io/services/soil/api

Endpoints

MethodPathDescription
POST/soil/batchUpload soil files
GET/soil/batch/{id}Get batch status
GET/soil/batch/{id}/resultsGet batch results
GET/soil/batchesList all batches

Upload soil files

POST /soil/batch Upload one or more .zip soil sample files for processing. Each file becomes an entry within the batch. Processing is asynchronous; poll the batch status endpoint to track progress.

Parameters

NameTypeInRequiredDescription
filesfile(s)body (multipart)YesOne or more .zip soil sample files
leafUserIdUUIDqueryYesThe Leaf user ID associated with the upload

Headers

HeaderValue
AuthorizationBearer YOUR_TOKEN
Content-Typemultipart/form-data

Request

curl -X POST \
  -H 'Authorization: Bearer YOUR_TOKEN' \
  -F 'files=@soil_samples.zip' \
  'https://api.withleaf.io/services/soil/api/soil/batch?leafUserId=YOUR_LEAF_USER_ID'
To upload multiple files in a single batch, repeat the files field for each file. Each file becomes a separate entry within the batch.
curl -X POST \
  -H 'Authorization: Bearer YOUR_TOKEN' \
  -F 'files=@field_north.zip' \
  -F 'files=@field_south.zip' \
  'https://api.withleaf.io/services/soil/api/soil/batch?leafUserId=YOUR_LEAF_USER_ID'
In Python, pass a list of tuples:
files = [
    ("files", open("field_north.zip", "rb")),
    ("files", open("field_south.zip", "rb")),
]
response = requests.post(url, headers=headers, files=files)

Response

201 Created
{
  "id": "fd22d4bb-e0c3-45a1-8d70-c5cc886088e4",
  "status": "PROCESSING",
  "fileCount": 1,
  "entries": [
    {
      "id": "aea0b567-8279-48a3-a226-7c57529a79b3",
      "fileName": "soil_samples.zip",
      "status": "PROCESSING",
      "downloadRawFile": "https://api.withleaf.io/services/files/soil/raw/.../file.zip",
      "downloadStandardGeojson": null,
      "downloadCanonicalJson": null,
      "errorMessage": null,
      "createdAt": "2026-04-10T19:21:46.358Z"
    }
  ],
  "createdAt": "2026-04-10T19:21:46.358Z",
  "updatedAt": "2026-04-10T19:21:46.900Z"
}

Get batch status

GET /soil/batch/{id} Retrieve the current status of a batch and all its entries. When an entry reaches COMPLETED, downloadStandardGeojson contains the flat result URL. downloadCanonicalJson contains the hierarchical result URL when that output is available.

Parameters

NameTypeInRequiredDescription
idUUIDpathYesBatch ID returned from the upload
leafUserIdUUIDqueryNoFilter by Leaf user ID. Omit to return results for all Leaf users under the API owner.

Request

curl -H 'Authorization: Bearer YOUR_TOKEN' \
  'https://api.withleaf.io/services/soil/api/soil/batch/fd22d4bb-e0c3-45a1-8d70-c5cc886088e4'

Response

200 OK
{
  "id": "fd22d4bb-e0c3-45a1-8d70-c5cc886088e4",
  "status": "COMPLETED",
  "fileCount": 1,
  "entries": [
    {
      "id": "aea0b567-8279-48a3-a226-7c57529a79b3",
      "fileName": "soil_samples.zip",
      "status": "COMPLETED",
      "downloadRawFile": "https://api.withleaf.io/services/files/soil/raw/.../file.zip",
      "downloadStandardGeojson": "https://api.withleaf.io/services/files/soil/results/.../result.geojson",
      "downloadCanonicalJson": "https://api.withleaf.io/services/files/soil/results/.../canonical.json",
      "errorMessage": null,
      "createdAt": "2026-04-10T19:21:46.358Z"
    }
  ],
  "createdAt": "2026-04-10T19:21:46.358Z",
  "updatedAt": "2026-04-10T19:21:49.344Z"
}

Get batch results

GET /soil/batch/{id}/results Returns a flat list of entries with their status and output URLs. Lighter than the full batch status when you only need the download links.

Parameters

NameTypeInRequiredDescription
idUUIDpathYesBatch ID
leafUserIdUUIDqueryNoFilter by Leaf user ID. Omit to return results for all Leaf users under the API owner.

Request

curl -H 'Authorization: Bearer YOUR_TOKEN' \
  'https://api.withleaf.io/services/soil/api/soil/batch/fd22d4bb-e0c3-45a1-8d70-c5cc886088e4/results'

Response

200 OK
[
  {
    "entryId": "aea0b567-8279-48a3-a226-7c57529a79b3",
    "fileName": "soil_samples.zip",
    "status": "COMPLETED",
    "downloadStandardGeojson": "https://api.withleaf.io/services/files/soil/results/.../result.geojson",
    "downloadCanonicalJson": "https://api.withleaf.io/services/files/soil/results/.../canonical.json",
    "downloadRawFile": "https://api.withleaf.io/services/files/soil/raw/.../file.zip",
    "errorMessage": null
  }
]

List all batches

GET /soil/batches List all batches for the authenticated API owner. Results are paginated.

Parameters

NameTypeInRequiredDescription
leafUserIdUUIDqueryNoFilter batches by Leaf user ID. Omit to return batches for all Leaf users under the API owner.
pageintegerqueryNoPage number (default: 0)
sizeintegerqueryNoPage size (default: 20)

Request

curl -H 'Authorization: Bearer YOUR_TOKEN' \
  'https://api.withleaf.io/services/soil/api/soil/batches?leafUserId=YOUR_LEAF_USER_ID&page=0&size=10'

Response

200 OK The response body is a JSON array of batch objects. Pagination metadata is returned in headers.
HeaderDescription
X-Total-CountTotal number of batches matching the query
LinkPagination links (first, prev, next, last)
[
  {
    "id": "fd22d4bb-e0c3-45a1-8d70-c5cc886088e4",
    "status": "COMPLETED",
    "fileCount": 1,
    "entries": [
      {
        "id": "aea0b567-8279-48a3-a226-7c57529a79b3",
        "fileName": "soil_samples.zip",
        "status": "COMPLETED",
        "downloadRawFile": "https://api.withleaf.io/services/files/soil/raw/.../file.zip",
        "downloadStandardGeojson": "https://api.withleaf.io/services/files/soil/results/.../result.geojson",
        "downloadCanonicalJson": "https://api.withleaf.io/services/files/soil/results/.../canonical.json",
        "errorMessage": null,
        "createdAt": "2026-04-10T19:21:46.358Z"
      }
    ],
    "createdAt": "2026-04-10T19:21:46.358Z",
    "updatedAt": "2026-04-10T19:21:49.344Z"
  }
]

Status values

StatusLevelDescription
PROCESSINGEntry / BatchFile uploaded, conversion in progress
COMPLETEDEntry / BatchConversion finished, result URLs available
PARTIALLY_COMPLETEDBatch onlySome entries completed, some failed
FAILEDEntry / BatchConversion failed; check errorMessage for details

Error responses

CodeReason
400API owner not enabled, missing files, or invalid parameters
401Missing or invalid JWT token
404Batch not found or does not belong to the authenticated user

Accessing result files

The downloadStandardGeojson, downloadCanonicalJson, and downloadRawFile fields contain URLs for the converted results. These URLs require the same Authorization: Bearer header used for all other API calls. Requests without a valid token return 401. downloadStandardGeojson is a flat GeoJSON FeatureCollection suited for mapping and GIS tools. downloadCanonicalJson is the hierarchical data model with lab info, provenance, and analyte categories when that output is available. downloadCanonicalJson may be null for some formats. Both output formats are described in Soil Sampling Overview: Output Formats.
curl -H 'Authorization: Bearer YOUR_TOKEN' \
  -o result.geojson \
  'https://api.withleaf.io/services/files/soil/results/.../result.geojson'

What to do next

Last modified on April 17, 2026