ProPlanta Recommendation API
A single HTTPS endpoint that turns plot & soil data into a fertilizer recommendation. This guide covers everything you need to integrate.
Overview
You send one or more plots (soil analysis, crop, history) in a JSON request; the service runs the agronomic model and returns a recommendation report for each plot. You authenticate with a token we issue to you — there is no sign-up or login flow on your side.
The integration is a single call:
- POST your JSON to
/pp - Put your token in the
Authorizationheader - Read the recommendation text back from the response body
Authentication
Every request must include the token we provide, sent verbatim in the
Authorization header. Do not add a Bearer prefix.
Authorization: eyJhbGciOiJIUzI1NiIs...your-token...
The endpoint
| Header | Value |
|---|---|
Authorization | Your token (required) |
Content-Type | application/json |
Request body
The body has two parts: userinfo (the grower/client this recommendation is for)
and plotinfo (an array — one object per plot). You may submit several plots in
a single request; each is processed independently.
{
"userinfo": {
"username": "Példa Gazda Kft.",
"regid": "1002456789",
"address": "7400 Kaposvár, Fő utca 1.",
"phone": "+36 30 123 4567",
"email": "iroda@pelda-gazda.hu"
},
"plotinfo": [
{
"plotname": "Kaszásmajor",
"mepar": "ABCD1-K-12",
"area": 12.4,
"soil": 1,
"samplingdate": "2024-09-23",
"humus": 3.1,
"phkcl": 7.2,
"alp2o5": 120,
"alk2o": 145,
"harvestyear": 2025,
"plant": "kukorica",
"plantid": 1111,
"planttype": 1,
"plannedyield": 7.3
}
]
}
Client fields (userinfo)
| Field | Type | Description |
|---|---|---|
username | string | Grower / client name |
regid | string | Registration number |
address | string | Postal address |
phone | string | Contact phone |
email | string | Contact email |
Plot fields (each item in plotinfo)
Grouped by topic. Types: string, number (decimals allowed), integer (whole numbers / codes).
Plot identity
| Field | Type | Description |
|---|---|---|
plotname | string | Plot name |
mepar | string | MePAR block identifier |
serialnr1, serialnr2 | string | Plot serial numbers |
area | number | Plot area |
soil | integer | Soil type code |
no3sensitive | integer | Nitrate-sensitive flag (0/1) |
akgid | integer | Agri-environment scheme id |
Soil sample results
| Field | Type | Description |
|---|---|---|
samplingdate | string | Sampling date (YYYY-MM-DD) |
bondindex | number | Soil bond (plasticity) index |
humus | number | Humus content (%) |
alp2o5 | number | AL-soluble P₂O₅ |
alk2o | number | AL-soluble K₂O |
caco3 | number | Calcium carbonate content |
phkcl | number | pH (KCl) |
mgkcl | number | KCl-soluble Mg (-1 if unknown) |
edtazn, edtacu, edtamn | number | EDTA-soluble Zn / Cu / Mn (-1 if unknown) |
Crop & production
| Field | Type | Description |
|---|---|---|
harvestyear | integer | Harvest year |
harvestseason | integer | Harvest season code |
plant | string | Crop name |
plantid | integer | Crop id |
planttype | integer | Crop type code |
plannedyield | number | Planned yield |
plannedyears, actualyear, plantingyear | integer | Perennial planning / planting years |
Leaf analysis & orchard/vineyard (perennials)
| Field | Type | Description |
|---|---|---|
leafn, leafp, leafk | number | Leaf N / P / K content |
treegrid | string | Planting grid (e.g. 4x5) |
trunkdiameter | number | Trunk diameter |
grasscover | integer | Grass cover flag (0/1) |
Pre-crop
| Field | Type | Description |
|---|---|---|
precrop | string | Pre-crop name |
precropid | integer | Pre-crop id |
harvestbefore0915 | integer | Harvested before 15 Sep (0/1) |
yield | number | Pre-crop yield |
byproduct | number | By-product amount |
fateofbyproduct | integer | By-product fate code |
legumepreprecrop | string | Legume in the crop before the pre-crop |
legumepreprecropid | integer | …its id |
Nutrients applied this year
| Field | Type | Description |
|---|---|---|
nthisyear, pthisyear, kthisyear | number | N / P / K already applied this year |
Organic matter & amendment history
| Field | Type | Description |
|---|---|---|
fym1year … fym4year | integer | Farmyard-manure application year (up to 4 entries) |
fym1type … fym4type | string | Manure type (e.g. cattle) |
fym1id … fym4id | integer | Manure type id |
fym1amount … fym4amount | number | Amount applied |
lm1year, lm2year | integer | Other organic material application year (up to 2) |
lm1type, lm2type | string | Material type |
lm1id, lm2id | integer | Material type id |
lm1amount, lm2amount | number | Amount applied |
greenm | string | Green manure |
greenmamount | number | Green-manure amount |
znfertyear | integer | Zinc fertilizer year |
znfertamount | number | Zinc fertilizer amount |
limeyear | integer | Liming year |
limeamount | number | Lime amount |
soil, harvestseason, plantid,
planttype, fym*id, …) follow ProPlanta conventions. Ask us for the
current code tables and unit reference so your values map correctly.
Success response
200 OK — the body is a plain-text report
(not JSON), with one section per plot, in the order you sent them. Content type is
text/plain.
error: recommendation failed: … instead of a result — the other plots are
unaffected. Always scan each section for that marker.
Ask us for a sample report for your crop set so you can build your parser against the exact output layout.
Status codes
| Status | Meaning | What to do |
|---|---|---|
| 200 | Processed. Body holds the report(s) — check each section for an error: line. | Parse the body. |
| 400 | Request body is not valid JSON. | Fix the payload; do not retry unchanged. |
| 403 | Token missing, invalid, or expired. | Obtain a new token, then retry. |
| 404 | Wrong path or method (only POST /pp is supported). | Check URL/method. |
| 500 | Server-side error. | Retry with backoff; contact us if it persists. |
Errors & retries
- 403 → your token is expired/invalid. Request a fresh one and retry. Do not retry with the same token in a tight loop.
- 400 → a payload problem (malformed JSON). Retrying unchanged will fail again — fix and resend.
- 500 → transient server issue. Retry a few times with exponential backoff (e.g. 1s, 4s, 10s). If it keeps failing, contact us.
- 200 with
error:in a section → that one plot couldn't be computed (often bad/contradictory inputs for that plot). Check the plot's data; other plots in the same request still succeeded.
Example — cURL
curl -X POST https://company.proplanta.atk.hu/pp \
-H "Authorization: $PROPLANTA_TOKEN" \
-H "Content-Type: application/json" \
--data-binary @request.json
Example — Python
import requests
resp = requests.post(
"https://company.proplanta.atk.hu/pp",
headers={"Authorization": TOKEN, "Content-Type": "application/json"},
json={
"userinfo": {"username": "Példa Gazda Kft.", "regid": "1002456789"},
"plotinfo": [{
"plotname": "Kaszásmajor", "area": 12.4, "soil": 1,
"humus": 3.1, "phkcl": 7.2, "alp2o5": 120, "alk2o": 145,
"harvestyear": 2025, "plant": "kukorica", "plantid": 1111,
"planttype": 1, "plannedyield": 7.3,
}],
},
timeout=60,
)
if resp.status_code == 403:
raise SystemExit("Token expired or invalid — request a new one.")
resp.raise_for_status() # raises on 400/404/500
report = resp.text # plain-text report, one section per plot
if "error: recommendation failed" in report:
print("Warning: at least one plot failed — inspect the report.")
print(report)
Example — Node.js
const res = await fetch("https://company.proplanta.atk.hu/pp", {
method: "POST",
headers: { "Authorization": process.env.PROPLANTA_TOKEN,
"Content-Type": "application/json" },
body: JSON.stringify({
userinfo: { username: "Példa Gazda Kft.", regid: "1002456789" },
plotinfo: [{
plotname: "Kaszásmajor", area: 12.4, soil: 1,
humus: 3.1, phkcl: 7.2, alp2o5: 120, alk2o: 145,
harvestyear: 2025, plant: "kukorica", plantid: 1111,
planttype: 1, plannedyield: 7.3,
}],
}),
});
if (res.status === 403) throw new Error("Token expired or invalid.");
if (!res.ok) throw new Error(`Request failed: ${res.status}`);
const report = await res.text(); // plain-text report
console.log(report);