What Shipped This Summer: A PlantLab Progress Report

Three antique botanical study plates side by side - two vigorous cannabis specimens and one with drooping, ochre-faded lower leaves - above a fine ink rule punctuated with engraved tick marks like a season of logged entries

The Short Version

Between June and August 2026, PlantLab learned to diagnose several plants in one photo, started naming the family of a condition when it isn't sure of the specific name, made model upgrades visible to your code, and fixed an upload failure that took three tries to actually kill.

The middle two change what comes back in your JSON.

I don't write these often. Most of what happens on a project like this isn't worth a post, and I'd rather publish something useful about spider mites than a changelog with a bow on it. But enough landed this summer to be worth one place to see it, and the failures are more interesting than the features.


One Photo, Several Plants

The API used to assume your photo held one plant. Shoot a whole tent and it averaged everything into one answer for the room, which is the wrong answer for every plant in it.

It now finds each plant separately and returns a results array – one entry per plant, each with its own bounding box, health call, growth stage and conditions. Three plants, two fine and one yellowing, gets you exactly that, and the box tells you which pot to walk to.

This was a breaking change: the per-plant fields moved off the top level into results[]. A single-plant photo returns an array of one, so it's the same code path either way. There's a longer write-up if you're wiring it up.


It Admits When It's Unsure

The change I'm happiest with, and the least flashy.

Some plant problems look nearly identical in a photograph. Calcium and magnesium deficiencies need different fixes, and there are images where nothing in the RGB data cleanly separates them. The honest move there isn't to pick one and sound confident.

So every condition and pest now carries a coarse_group – the clinical family it belongs to, one of six. The family is often right when the specific name is shaky, so you can alert on “something in the mobile nutrient family is happening” and stand on much firmer ground than “it's definitely magnesium.”

A secondary finding can also come back marked suspected: true. That flags something for your attention without claiming it. Show those to a human. Don't dose on them.

Both fields are additive, so nothing broke when they appeared.


Model Upgrades Are Visible Now

Responses carry an engine_version naming the build and model iteration that served your call.

That matters because the models behind a diagnosis were replaced over the summer, and they'll be replaced again. If you cache results or tune thresholds against particular behavior, watch that field and invalidate when it moves. It used to be that a model upgrade quietly changed things underneath you. Now you can branch on it.

The honest numbers haven't moved much and I won't inflate them: cannabis verification sits at 99.96% balanced accuracy, health screening at 98.4%, both on plants held out from training. Naming the exact condition is still harder than noticing something's wrong. That gap is why the hedging fields exist.


The Upload Bug That Took Three Tries

My favorite failure of the summer.

Uploads from phones started failing. Not all of them, not reproducibly, and never on my machine. The pattern turned out to be full-resolution photos over slow upstream connections – which describes a lot of growers and almost no developers.

The server was hanging up while the phone was still sending. I raised the read timeout from 30 seconds to 120 and shipped it. Reports kept coming. Raised it to 300 and shipped that. Reports kept coming.

There were three timeouts in that path, not one, and they were all different: the API's own, the reverse proxy in front of it, and the client's. Fixing one just moved the failure to whichever was now shortest. The connection died at the tightest link no matter what I did to the others. Aligned all three at 300 seconds and the reports stopped.

I shouldn't have needed this lesson twice: when a timeout fix doesn't work, the timeout you fixed isn't the one that fired. Find every layer that can hang up before you touch any of them.

The mobile app shipped with the aligned values in July.


Quieter Things


What's Next

More work on naming the specific condition rather than the family, since that's the honest weak point and everything else is downstream of it. And better handling of the photos growers actually take, which are lit by purple LEDs at midnight rather than by a photographer.

Free tier is 3 diagnoses a day, no card. The full contract is at plantlab.ai/openapi.json, with a field-by-field walkthrough if you want it, plus guides for Home Assistant and Node-RED.