{"openapi":"3.1.0","info":{"title":"Diligo Intelligence Hub API","description":"Independent daily price intelligence for the UK fertiliser market: a modelled estimate and confidence band for every tracked grade, the published price series behind it, and the public record of how the model scored against the prints that followed. Read-only and keyless. Prices are estimates in GBP per tonne, not offers and not transaction prices.\n\nNo licence is asserted by this contract. Source licence metadata is informational and does not gate API availability.","contact":{"name":"Diligo Ltd","url":"https://www.diligo.uk/intelligence/","email":"hello@diligo.uk"},"version":"1.142.0"},"servers":[{"url":"https://intelligence.tilth.uk","description":"Diligo Intelligence Hub public API"}],"paths":{"/api/v1/fertiliser":{"get":{"tags":["Fertiliser"],"summary":"`GET /api/v1/fertiliser` — latest estimate per product with track record.\nMounted by [`crate::gb`].","operationId":"list_latest_estimates","responses":{"200":{"description":"The most recent daily estimate for every tracked grade, with its confidence band and, once the model has been scored against at least one realised weekly print, its track record. Read `hit_rate_n_of_10` as a ratio over `scored_prints`, never over ten: early in a grade's life fewer than ten prints have been scored, so dividing by ten understates the model. All three track-record fields are null until the first print is scored. Prices are estimates in GBP per tonne, not offers and not transaction prices.","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/FertiliserEstimate"}}}}},"500":{"description":"The estimate store could not be read"}}}},"/api/v1/fertiliser/divergence":{"get":{"tags":["Fertiliser"],"summary":"`GET /api/v1/fertiliser/divergence`: per-day forward-error time series for\none or all grades. Shows how far the daily estimate was from the next\nweekly market print.","description":"Part of the published public contract: it is the scoring record behind the\naccuracy claim the product makes. An earlier comment here called it\ninternal diagnostics, which was true before the public launch spec named it\none of the seven published operations.","operationId":"get_divergence","parameters":[{"name":"product","in":"query","description":"Product code to filter by: AN_UK, AN_IMPORT, UREA_GRANULAR, UAN, NITRATE_SULPHUR, DAP, TSP, MOP, POLYSULPHATE. Omit for every grade.","required":false,"schema":{"type":"string"}},{"name":"days","in":"query","description":"Lookback window in days, default 90. Clamped to the range 1 to 365; a larger value returns 365 days rather than an error.","required":false,"schema":{"type":"integer","format":"int32"}}],"responses":{"200":{"description":"One point per day in the window, oldest first, showing the estimate, the realised market print it is compared against, and the signed percentage difference. This is the public scoring record behind the accuracy claim. Capped at 4000 points per call, which covers a full year across every tracked grade.","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/DivergencePoint"}}}}},"400":{"description":"`days` was not an integer, or was outside the range of a 32-bit signed integer"},"500":{"description":"The divergence store could not be read"}}}},"/api/v1/fertiliser/estimates/history":{"get":{"tags":["Fertiliser"],"summary":"`GET /api/v1/fertiliser/estimates/history?product=X` — recent estimate history\nfor one product (the \"nowcast line\"), newest-first, capped at 400 rows\n(covers 13+ months at daily cadence; was 70, sized for the old\nweekly-only cadence). Mounted by [`crate::gb`].","operationId":"get_estimate_history","parameters":[{"name":"product","in":"query","description":"Product code: AN_UK, AN_IMPORT, UREA_GRANULAR, UAN, NITRATE_SULPHUR, DAP, TSP, MOP, POLYSULPHATE","required":true,"schema":{"type":"string"}},{"name":"cadence","in":"query","description":"Accepted for call compatibility with /series and ignored by this operation, which always returns the daily estimate line. Defaults to `weekly`.","required":false,"schema":{"type":"string"}}],"responses":{"200":{"description":"That product's daily estimates, newest first, capped at 400 points. An unknown product code returns an empty array rather than an error.","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/FertiliserEstimatePoint"}}}}},"400":{"description":"The `product` query parameter is missing"},"500":{"description":"The estimate store could not be read"}}}},"/api/v1/fertiliser/narrative":{"get":{"tags":["Fertiliser"],"summary":"`GET /api/v1/fertiliser/narrative?product=AN_UK` - ranked market facts for the\nproduct's SEO narrative. The facts are COMPUTED here (`fertiliser_facts`); the\nmarketplace renders them. Empty `facts` for an unknown/data-less product.","operationId":"get_narrative_facts","parameters":[{"name":"product","in":"query","description":"Product code: AN_UK, AN_IMPORT, UREA_GRANULAR, UAN, NITRATE_SULPHUR, DAP, TSP, MOP, POLYSULPHATE","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Ranked, computed market facts for that product, most salient first. Each fact is tagged by `kind`: trend, range_position, volatility, track_record, peer_comparison or seasonality. An unknown or data-less product returns an empty `facts` array.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FertiliserNarrative"}}}},"400":{"description":"The `product` query parameter is missing"},"500":{"description":"The price or estimate store could not be read"}}}},"/api/v1/fertiliser/official-index-comparison":{"get":{"tags":["Fertiliser"],"summary":"`GET /api/v1/fertiliser/official-index-comparison?product=AN_UK`: rebased\noverlay of the daily forecast vs the official DEFRA index. Consistency +\nlead-time only; no accuracy percentage (spec R1). Mounted by [`crate::gb`].","description":"Part of the published public contract, served by this Hub at\n`GET /openapi.json`. The marketplace proxy publishes a separate, narrower\ncontract for its own site.","operationId":"compare_to_official_index","parameters":[{"name":"product","in":"query","description":"Product code: AN_UK, AN_IMPORT, UREA_GRANULAR, UAN, NITRATE_SULPHUR, DAP, TSP, MOP, POLYSULPHATE","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"The daily forecast and the official DEFRA Agricultural Price Index for the same product, both rebased to 100 at the first shared month. Gate rendering on `eligible`, never on `overlay_points` being non-empty: when the two series are not structurally distinct, or when the forecast has not yet reached modelled stage, `eligible` is false and the overlay is still fully populated. Rendering it anyway shows exactly the self-comparison the distinctness gate exists to suppress. `eligible` is also false with an empty overlay when the product has no DEFRA-mapped nutrient series, when fewer than three months are shared between the two series, or when either series is non-positive at the first shared month. `official_lag_days` states how far behind the official index publishes. This is a consistency and lead-time comparison, not an accuracy claim.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OfficialIndexResponse"}}}},"400":{"description":"The `product` query parameter is missing"},"500":{"description":"The estimate or official-index store could not be read"}}}},"/api/v1/fertiliser/overview":{"get":{"tags":["Fertiliser"],"summary":"`GET /api/v1/fertiliser/overview` - the market-wide weekly aggregate for the\nhub page (how many grades rose, the biggest mover). Aggregate market claim,\ncomputed here.","operationId":"get_market_overview","responses":{"200":{"description":"Aggregate across every tracked grade, derived from the daily forecast series: the latest estimate against the newest estimate at least seven days older, so the span is a week or more when ingestion has gaps. Reports how many of `tracked` grades rose and the grade with the largest absolute move, sign retained.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FertiliserMarketOverview"}}}},"500":{"description":"The estimate store could not be read"}}}},"/api/v1/fertiliser/series":{"get":{"tags":["Fertiliser"],"summary":"`GET /api/v1/fertiliser/series?product=AN_UK&cadence=weekly` — that product's\ncurated AHDB price series (date + price + cadence), newest-first, capped at\n70 prints (≈16 months of weekly — enough for a 1-year window plus a\n1-year-ago comparison). `cadence` defaults to `weekly`; only `weekly` and\n`monthly` are accepted (any other value is a 400). Mounted by [`crate::gb`].","operationId":"get_price_series","parameters":[{"name":"product","in":"query","description":"Product code: AN_UK, AN_IMPORT, UREA_GRANULAR, UAN, NITRATE_SULPHUR, DAP, TSP, MOP, POLYSULPHATE","required":true,"schema":{"type":"string"}},{"name":"cadence","in":"query","description":"Print cadence, `weekly` (default) or `monthly`. Any other value returns 400.","required":false,"schema":{"type":"string"}}],"responses":{"200":{"description":"The curated published price series for that product and cadence, newest first, capped at 70 prints. A date, price and cadence projection only; no source or ingest metadata is published.","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/FertiliserPricePoint"}}}}},"400":{"description":"`cadence` was neither `weekly` nor `monthly`, or `product` was missing"},"500":{"description":"The price store could not be read"}}}}},"components":{"schemas":{"DivergencePoint":{"type":"object","description":"One day of forward error: how far the daily estimate sat from the weekly\nmarket print that followed it. A positive `divergence_pct` means the\nestimate was above the print.","required":["product","anchor_week","divergence_date","estimate_gbp_t","actual_gbp_t","divergence_pct"],"properties":{"actual_gbp_t":{"type":"number","format":"double","description":"The realised market print for `anchor_week`, GBP per tonne."},"anchor_week":{"type":"string","format":"date","description":"The week the compared market print belongs to."},"divergence_date":{"type":"string","format":"date","description":"The day the estimate was made."},"divergence_pct":{"type":"number","format":"double","description":"Signed percentage difference between the two."},"estimate_gbp_t":{"type":"number","format":"double","description":"The estimate on `divergence_date`, GBP per tonne."},"product":{"type":"string","description":"Product code, one of the nine published grades."}}},"FertiliserEstimate":{"type":"object","description":"Hub→marketplace DTO for a single fertiliser estimate row.\n\nUsed by `GET /api/v1/fertiliser` (latest modelled estimate per product).\nWire shape is stable — no wire-format change when re-pointing the hub.","required":["product","estimate_gbp_t","band_low_gbp_t","band_high_gbp_t","as_of","method"],"properties":{"as_of":{"type":"string","format":"date"},"band_high_gbp_t":{"type":"number","format":"double"},"band_low_gbp_t":{"type":"number","format":"double"},"estimate_gbp_t":{"type":"number","format":"double"},"hit_rate_n_of_10":{"type":["integer","null"],"format":"int32","description":"How many of the last scored prints landed inside the band. Read it as a\nratio over [`FertiliserEstimate::scored_prints`], never over ten: early\nin a grade's life the sample is smaller than ten, and dividing by ten\nunderstates the model by up to an order of magnitude. `None` until the\nfirst weekly print has been scored."},"method":{"type":"string"},"product":{"type":"string"},"scored_prints":{"type":["integer","null"],"format":"int32","description":"The denominator for [`FertiliserEstimate::hit_rate_n_of_10`]: how many\nrealised prints the hit count was measured over, at most 10. `None`\nuntil the first weekly print has been scored, and always `Some` with a\nvalue of at least 1 whenever `hit_rate_n_of_10` is `Some`.\n\nA Hub that predates this field omits it, so `None` also means \"the Hub\ndid not say\". Render the count alone in that case rather than assuming\nten, which is the assumption this field exists to retire."},"signal_stage":{"type":["string","null"],"description":"`\"early\"` (founder-approved, unvalidated stock signal) | `\"validated\"`\n(cleared the real walk-forward Gate 2) | `None` (flat-carry or WB-only,\nno stock term applied)."},"within_gbp_t":{"type":["number","null"],"format":"double","description":"Median absolute error against realised AHDB weekly prints (last 10).\n`None` until the first weekly print has been scored."}}},"FertiliserEstimatePoint":{"type":"object","description":"Hub→marketplace DTO for one historical estimate point (the \"nowcast line\").\n\nUsed by `GET /api/v1/fertiliser/estimates/history?product=X`.","required":["product","date","estimate_gbp_t","band_low_gbp_t","band_high_gbp_t","method"],"properties":{"band_high_gbp_t":{"type":"number","format":"double"},"band_low_gbp_t":{"type":"number","format":"double","description":"Confidence range bounds for this day's estimate — drawn as the chart's\nshaded band (the single-series presentation's uncertainty display)."},"date":{"type":"string","format":"date"},"estimate_gbp_t":{"type":"number","format":"double"},"method":{"type":"string"},"product":{"type":"string"},"signal_stage":{"type":["string","null"],"description":"See [`FertiliserEstimate::signal_stage`]."}}},"FertiliserFact":{"oneOf":[{"type":"object","description":"Price move over `window_days` (30 / 90 / 365): signed delta and percent.","required":["window_days","delta_gbp_t","pct","kind"],"properties":{"delta_gbp_t":{"type":"number","format":"double"},"kind":{"type":"string","enum":["trend"]},"pct":{"type":"number","format":"double"},"window_days":{"type":"integer","format":"int64"}}},{"type":"object","description":"Where the latest print sits in the trailing 52-week range.","required":["position","low_gbp_t","high_gbp_t","kind"],"properties":{"high_gbp_t":{"type":"number","format":"double"},"kind":{"type":"string","enum":["range_position"]},"low_gbp_t":{"type":"number","format":"double"},"position":{"$ref":"#/components/schemas/RangeBand"}}},{"type":"object","description":"Modelled uncertainty band width as a percent of the estimate.","required":["class","band_pct","kind"],"properties":{"band_pct":{"type":"number","format":"double"},"class":{"$ref":"#/components/schemas/VolatilityClass"},"kind":{"type":"string","enum":["volatility"]}}},{"type":"object","description":"Model track record over the recently scored prints. `hit_rate_n_of_10`\nis a count, not a rate: divide it by `scored_prints`, never by ten.","required":["within_gbp_t","hit_rate_n_of_10","kind"],"properties":{"hit_rate_n_of_10":{"type":"integer","format":"int32"},"kind":{"type":"string","enum":["track_record"]},"scored_prints":{"type":"integer","format":"int32"},"within_gbp_t":{"type":"number","format":"double"}}},{"type":"object","description":"Latest price against the mean of the other tracked grades.","required":["vs_peer_mean_gbp_t","kind"],"properties":{"kind":{"type":"string","enum":["peer_comparison"]},"vs_peer_mean_gbp_t":{"type":"number","format":"double"}}},{"type":"object","description":"Latest price against the same month one year ago.","required":["vs_same_month_last_year_gbp_t","kind"],"properties":{"kind":{"type":"string","enum":["seasonality"]},"vs_same_month_last_year_gbp_t":{"type":"number","format":"double"}}}],"description":"One computed, ranked market fact about a fertiliser product. COMPUTED in the\nhub (`fertiliser_facts`); the marketplace only renders it. Serde-tagged so the\nrenderer can `match` on the variant. Every number is hub-derived; no field is\nrecomputed downstream."},"FertiliserMarketOverview":{"type":"object","description":"Hub->marketplace DTO: the market-wide week-over-week aggregate for the\noverview page, computed from the DAILY FORECAST series (latest estimate vs\nthe estimate at least 7 days older, per grade -- single-series presentation,\n2026-07-10). `risers` of `tracked` grades rose this week; `top_mover_*` is\nthe largest absolute move. Aggregate market claim, so hub-computed.","required":["risers","tracked","top_mover_product","top_mover_delta_gbp_t"],"properties":{"risers":{"type":"integer","format":"int32","minimum":0},"top_mover_delta_gbp_t":{"type":"number","format":"double"},"top_mover_product":{"type":"string"},"tracked":{"type":"integer","format":"int32","minimum":0}}},"FertiliserNarrative":{"type":"object","description":"Hub->marketplace DTO: the ranked narrative facts for one product (most salient\nfirst). Empty `facts` means no narrative (data-less or too-short series). The\nmarketplace renders these into prose; it never derives them.","required":["product","facts"],"properties":{"facts":{"type":"array","items":{"$ref":"#/components/schemas/FertiliserFact"}},"product":{"type":"string"}}},"FertiliserPricePoint":{"type":"object","description":"Hub→marketplace DTO for a single published AHDB price print.\n\nA **curated** projection of `intelligence.fertiliser_prices` (date + price +\ncadence only — never the raw row's source/ingest metadata). Used by\n`GET /api/v1/fertiliser/series`. Founder-authorised public republication of\nthe AHDB GB Fertiliser Price Series (supersedes the earlier estimates-only\nrule). Wire shape is stable across hub re-pointing.","required":["product","week_ending","price_gbp_t","cadence"],"properties":{"cadence":{"type":"string","description":"`\"weekly\"` or `\"monthly\"` — the AHDB print cadence."},"price_gbp_t":{"type":"number","format":"double"},"product":{"type":"string"},"week_ending":{"type":"string","format":"date"}}},"OfficialIndexResponse":{"type":"object","description":"The forecast against official-index comparison for one product.","required":["eligible","overlay_points","n_months","official_lag_days"],"properties":{"eligible":{"type":"boolean","description":"Whether the overlay is safe to render. **Not** a summary of whether\n`overlay_points` is empty: the distinctness and modelled-stage gates\nboth return `false` alongside a fully populated overlay, and rendering\nthat overlay defeats the suppression those gates exist for. Consumers\ngate on this field alone.\n\n`false` with an **empty** overlay: no DEFRA-mapped nutrient series for\nthe product, fewer than three shared months, or a non-positive value in\neither series at the first shared month.\n\n`false` with a **populated** overlay: the two series are not\nstructurally distinct, or the forecast has not reached modelled stage."},"n_months":{"type":"integer","format":"int32","description":"How many months the overlay covers."},"official_lag_days":{"type":"integer","format":"int32","description":"How many days behind the forecast the official index publishes."},"overlay_points":{"type":"array","items":{"$ref":"#/components/schemas/OverlayPointDto"},"description":"The rebased overlay, oldest month first."}}},"OverlayPointDto":{"type":"object","description":"One month of the forecast against official-index overlay. Both series are\nrebased to 100 at the first shared month, so they share one axis.","required":["month","forecast_rebased","official_rebased","official_carried_forward"],"properties":{"forecast_rebased":{"type":"number","format":"double","description":"The Hub forecast level for that month, rebased."},"month":{"type":"string","format":"date","description":"First day of the calendar month this point covers."},"official_carried_forward":{"type":"boolean","description":"`true` when DEFRA had not yet published that month and the previous\nofficial value was carried forward."},"official_rebased":{"type":"number","format":"double","description":"The official DEFRA index for that month, rebased on the same base."}}},"RangeBand":{"type":"string","enum":["near_high","near_low","mid"]},"VolatilityClass":{"type":"string","enum":["tight","wide"]}}},"tags":[{"name":"Fertiliser","description":"Daily UK fertiliser price estimates, confidence bands, published price series, and the public record of how each estimate scored against the print that followed. Read-only and keyless."}],"externalDocs":{"url":"https://www.diligo.uk/intelligence/","description":"Diligo Intelligence Hub product documentation"}}