Browse all documentation

Historical Palmoil physical API

Get oldest-first Palmoil observations

Endpoint and request

REST endpoint
GET /v1/commodities/palmoil/physical/{series_id}/observations
Contract request
curl --request GET \
  --url "https://api.narwhalapi.com/v1/commodities/palmoil/physical/id-ri-oil-palm-ffb-smallholder-purchase-agreement/observations?start=2026-08-01&end=2026-09-02&limit=100" \
  --header "Authorization: Bearer $NARWHAL_API_KEY"

MCP uses commodities_get_palmoil_physical_observations. REST and MCP share the same schema, account quota, rights check, and problem shape.

Parameters

NameRequiredTypeMeaning
series_idYespath · stable Narwhal IDSeries returned by discovery.
startNoquery · YYYY-MM-DDInclusive lower bound. It must be supplied together with end.
endNoquery · YYYY-MM-DDExclusive upper bound. It must be later than start.
limitNoquery · integer, 1–500Maximum complete schedules to return. Defaults to 100.
cursorNoquery · opaque stringContinuation token bound to the same series and date range.

Response

Contract example
{
  "series_id": "id-ri-oil-palm-ffb-smallholder-purchase-agreement",
  "commodity": "OIL-PALM-FFB",
  "country": "IDN",
  "area": "ID-RI",
  "producer_type": "smallholder",
  "producer_arrangement": "purchase_agreement",
  "price_type": "official_purchase_schedule",
  "observation_type": "age_schedule",
  "period_type": "effective",
  "currency": "IDR",
  "unit": "kilogram",
  "observations": [
    {
      "period_start": "2026-08-26",
      "period_end": "2026-09-01",
      "published_on": "2026-08-26",
      "revision": 1,
      "prices": [
        {
          "tree_age_min_years": 3,
          "tree_age_max_years": 3,
          "price": "2998.31"
        },
        {
          "tree_age_min_years": 4,
          "tree_age_max_years": 4,
          "price": "3347.20"
        },
        {
          "tree_age_min_years": 5,
          "tree_age_max_years": 5,
          "price": "3595.38"
        },
        {
          "tree_age_min_years": 6,
          "tree_age_max_years": 6,
          "price": "3734.94"
        },
        {
          "tree_age_min_years": 7,
          "tree_age_max_years": 7,
          "price": "3818.86"
        },
        {
          "tree_age_min_years": 8,
          "tree_age_max_years": 8,
          "price": "3865.46"
        },
        {
          "tree_age_min_years": 9,
          "tree_age_max_years": 9,
          "price": "3878.72"
        },
        {
          "tree_age_min_years": 10,
          "tree_age_max_years": 20,
          "price": "3836.93"
        },
        {
          "tree_age_min_years": 21,
          "tree_age_max_years": 21,
          "price": "3770.94"
        },
        {
          "tree_age_min_years": 22,
          "tree_age_max_years": 22,
          "price": "3695.31"
        },
        {
          "tree_age_min_years": 23,
          "tree_age_max_years": 23,
          "price": "3609.33"
        },
        {
          "tree_age_min_years": 24,
          "tree_age_max_years": 24,
          "price": "3544.29"
        },
        {
          "tree_age_min_years": 25,
          "tree_age_max_years": 25,
          "price": "3490.84"
        },
        {
          "tree_age_min_years": 26,
          "tree_age_max_years": 26,
          "price": "3471.62"
        },
        {
          "tree_age_min_years": 27,
          "tree_age_max_years": 27,
          "price": "3441.82"
        },
        {
          "tree_age_min_years": 28,
          "tree_age_max_years": 28,
          "price": "3385.41"
        },
        {
          "tree_age_min_years": 29,
          "tree_age_max_years": 29,
          "price": "3344.02"
        },
        {
          "tree_age_min_years": 30,
          "tree_age_max_years": 30,
          "price": "3249.41"
        }
      ]
    }
  ],
  "next_cursor": null
}
FieldTypeRequiredMeaning
series_id / commodity / country / areastringsYesStable identity and physical-market scope.
producer_type / producer_arrangementstrings or nullYesProducer scope shared by every returned schedule.
price_type / observation_type / period_typestringsYesShared typed value meaning and period convention.
observationsarrayYesScalar or complete schedules ordered oldest first.
next_cursorstring or nullYesOpaque continuation token, or null at the end.

Errors and quota

  • 400 invalid_query — a code, area, date range, limit, or selector is invalid.
  • 400 invalid_cursor — the cursor is invalid or does not match the series and range.
  • 401 invalid_api_key — the Bearer key is missing or rejected.
  • 404 palmoil_series_not_found — no complete approved data matches the request.
  • 429 rate_limit_exceeded or quota_exhausted — the shared allowance is exhausted.
  • 503 physical_price_unavailable — a complete publication cannot safely satisfy the Interface.

Rejected requests and server errors do not consume monthly quota.

Coverage

History includes accepted revisions only and makes no promise for Malaysia or every Indonesian area.

Available with a Narwhal API key.