‹ ZestLens

ZestLens API

Connect your ERP, packhouse, sales or BI system to your orchard measurements. Read-only REST, JSON, version 1.

Authentication

Company administrators create keys in the app: Settings → API keys. Each key belongs to one company and only sees that company's data. The key is shown once; revoke it at any time from the same screen.

Authorization: Bearer zlk_…

Base URL: https://api.zestlens.app · Limit: 120 requests per minute per key (HTTP 429 when exceeded) · Text fields follow Accept-Language (en, es, pt, fr, it, de, el, tr, ar, ru, zh).

Endpoints

EndpointReturns
GET/v1/blocksBlocks: id, name, cultivar, trees, area (ha), corner coordinates.
GET/v1/blocks/{id}/summaryLatest week: fruit measured, diameter (mean, median, SD, 95% CI), 5 mm size distribution, EU 543/2011 size codes, mean weight, peel defect severity, export-grade share, colour stages; yield estimate, growth vs. reference curve, bin-scan stem quality, weekly history and analysis notes. ?mode=bin for bin scans.
GET/v1/measurementsFruit-level data: time, block, sampling point, GPS, diameter, size code, weight, colour, defect, caliper value. Filters block, from, to, mode; paging with limit (≤1000 photos) and after.
GET/v1/photos/{id}/controlMeasurement image (JPEG) with the fruit outline and diameter.
GET/v1/blocks/{id}/documentFull report document (A4 HTML with photos, tables, charts and literature).
GET/v1/openapi.jsonOpenAPI 3.1 description (no key needed) — open.

Examples

curl -H "Authorization: Bearer $ZESTLENS_KEY" https://api.zestlens.app/v1/blocks

curl -H "Authorization: Bearer $ZESTLENS_KEY" -H "Accept-Language: de" \
     https://api.zestlens.app/v1/blocks/2/summary
import requests
H = {"Authorization": "Bearer " + KEY}
after = None
while True:
    p = {"block": 2, "from": "2026-10-01", "limit": 500, **({"after": after} if after else {})}
    r = requests.get("https://api.zestlens.app/v1/measurements", headers=H, params=p).json()
    for f in r["fruit"]:
        print(f["captured_at"], f["diameter_mm"], f["size_code"], f["weight_g"])
    after = r["next_after"]
    if not after: break

Sample summary (shortened):

{
  "block": { "id": 2, "name": "Block A", "cultivar": "Valencia Late", "area_ha": 3.52 },
  "latest_week": {
    "week": "2026-40", "fruit_measured": 64,
    "diameter_mm": { "mean": 73.5, "median": 73.3, "sd": 3.8, "ci95_half_width": 0.9 },
    "eu_size_codes": [ { "code": "6", "from_mm": 70, "to_mm": 80, "count": 26, "share_pct": 40.6 }, … ],
    "weight_g": { "mean": 205 },
    "defects": [ { "severity": "none", "share_pct": 79.3 }, { "severity": "slight", "share_pct": 15.5 }, … ],
    "export_grade_pct": 94.8
  },
  "yield_t": { "today": …, "at_harvest": … },
  "weeks": [ … ]
}

Notes

Privacy policy · Support