Table of contents

Intro

On our mission to give everyone interested the knowledge on how to grow useful plants for a food garden, medicinal or regenerative uses, we want the database to be used wherever it can help. This is why we provide an API for developers to add plant data to their applications and services.

The API lets you do everything you can do with plants on the website: list and page through the whole database, full-text search with faceted filtering, read individual plants and their varieties and companions, and create and update plants.

Getting access

API access is fully self-service: sign up for an account (or log in) and create your API keys under My API keys.

If you have any questions or feedback, don’t hesitate to reach out to us.

Licensing

Please note that all data available through this API is licensed under CC BY-SA 4.0. That means you must provide appropriate credit (Permapeople.org), and any work built upon this dataset must be distributed under the same license (CC BY-SA 4.0). We do our best to include data sources via links, descriptions and sources within plant profiles, and encourage our contributors to do the same.

Since this is a free service, we ask that anyone using the API give attribution to plant data sources by linking back to Permapeople.org, either in a specific call-out or in plant profiles where Permapeople plant data is used.

We do not currently allow commercial projects free access to the API. If you have a commercial project and you’d like to gain access to our API, please get in touch and we can work out the details.

Please also read our data acknowledgement.

Endpoint

All following requests must be sent to and will return valid JSON (content-type: application/json; charset=utf-8).

https://permapeople.org/api

Request bodies (for POST/PUT) must be sent as a JSON object with content-type: application/json.

Authentication

To access all API endpoints header-based authentication must be provided for every request. To authenticate, add the following HTTP headers to your request:

x-permapeople-key-id: YOUR_KEY_ID
x-permapeople-key-secret: YOUR_KEY_SECRET

Requests without valid headers return 401 Unauthorized:

{
  "error": "not authenticated",
  "msg": "Please check your x-permapeople-key-id and x-permapeople-key-secret http headers."
}

The plant object

Every endpoint that returns a plant returns the same shape. Endpoints returning many plants wrap them in a plants array (see Pagination).

{
   "id" : 101,
   "type" : "Plant",
   "scientific_name" : "Morus alba",
   "name" : "White mulberry",
   "version" : 10,
   "description" : "Young leaves are edible.",
   "link" : "/plants/morus-alba-white-mulberry",
   "slug" : "morus-alba-white-mulberry",
   "parent_id" : null,
   "updated_at" : "2022-07-30T08:17:26.658Z",
   "created_at" : "2020-08-11T16:09:47.485Z",
   "images" : {
      "thumb" : "https://...",
      "title" : "https://..."
   },
   "data" : [
      { "key" : "Edible", "value" : "true" },
      { "key" : "Growth", "value" : "Medium" },
      { "key" : "Water requirement", "value" : "Moist" },
      { "key" : "Light requirement", "value" : "Full sun, partial sun/shade" },
      { "key" : "USDA Hardiness zone", "value" : "3-9" },
      { "key" : "Layer", "value" : "Trees" },
      { "key" : "Soil type", "value" : "Light (sandy), medium, heavy (clay)" },
      { "key" : "Family", "value" : "Moraceae" },
      { "key" : "Edible parts", "value" : "Fruit, inner bark, leaves" }
   ]
}

Notable fields:

Pagination

All list endpoints (/plants, /search, /plants/:id/varieties, /plants/:id/companions) return a plants array and an additive pagination object describing the current slice.

{
  "plants" : [ {<Plant>}, {<Plant>}, ... ],
  "pagination" : { ... }
}

There are two paging modes. /plants defaults to keyset and additionally supports offset; /search, /varieties and /companions use offset.

per_page<int> controls the page size on every list endpoint. The default is 100 and the maximum is 100 (larger values are clamped down).

Keyset pagination (cursor)

The default for List plants. Walk the whole dataset by following the last_id cursor — efficient and stable even while data changes underneath you. The pagination object looks like:

"pagination" : {
  "per_page" : 100,
  "total" : 8421,
  "last_id" : 1340,
  "has_more" : true
}

To get the next page, pass last_id back in: GET /api/plants?last_id=1340. Stop when has_more is false.

Offset pagination (pages)

Used when you pass page, and the default for the other list endpoints. Jump to any page directly. The pagination object looks like:

"pagination" : {
  "page" : 2,
  "per_page" : 100,
  "total" : 8421,
  "total_pages" : 85
}

List plants

List plants ordered by their internal ID, wrapped in a plants array with a pagination object.

GET https://permapeople.org/api/plants
# Response

{
  "plants" : [ {<Plant>}, {<Plant>}, ... ],
  "pagination" : {
    "per_page" : 100,
    "total" : 8421,
    "last_id" : 1340,
    "has_more" : true
  }
}

Params

Provide as GET params in the URL, for example ?last_id=100&per_page=50.

(optional) last_id<int>: Cursor — returns only plants after this id. Use this with has_more to page through the whole list (keyset mode, the default).

(optional) page<int>: Switches to offset mode and returns that page. Use with per_page and total_pages.

(optional) per_page<int>: Page size, default 100, max 100.

(optional) updated_since<iso8601 string>: Only show plants last updated on or after the datetime provided. Works in both paging modes.

Search plants

Returns plants matching a full-text search. This means it will find similarly named plants too, so check that the returned plant is the one you are looking for. Results are paginated (offset mode).

POST https://permapeople.org/api/search
GET  https://permapeople.org/api/search?q=mulberry
# Response

{
  "plants" : [ {<Plant>}, {<Plant>}, ... ],
  "pagination" : {
    "page" : 1,
    "per_page" : 100,
    "total" : 7,
    "total_pages" : 1
  }
}

Params

For POST, provide as a JSON object with content-type: application/json. For GET, provide as URL query params.

(required) q<string>: Search the whole database for this term. Much like the search on the website.

(optional) type<enum>: Restrict results to Plant/Species or Variety.

(optional) page<int> / per_page<int>: Paging, as described in Pagination.

(optional) <facet>: Any filterable attribute can be passed to narrow results, with a single value or an array of values (matched as OR), e.g. {"q": "mint", "Layer": "Herbs", "Light requirement": ["Full sun", "Partial sun/shade"]}.

Get a single plant

Returns a single plant object for an exact ID.

GET https://permapeople.org/api/plants/<id>
# Response

{<Plant>}

List varieties of a plant

Returns the varieties belonging to a plant, paginated (offset mode).

GET https://permapeople.org/api/plants/<id>/varieties
# Response

{
  "plants" : [ {<Plant with type "Variety">}, ... ],
  "pagination" : { "page" : 1, "per_page" : 100, "total" : 3, "total_pages" : 1 }
}

Accepts page and per_page params.

List companion plants

Returns the companion plants linked to a plant (link type “companion to”), paginated (offset mode). Antagonists and other link types are not included.

GET https://permapeople.org/api/plants/<id>/companions
# Response

{
  "plants" : [ {<Plant>}, ... ],
  "pagination" : { "page" : 1, "per_page" : 100, "total" : 5, "total_pages" : 1 }
}

Accepts page and per_page params.

Create a plant

To create a plant, do a POST request with a valid plant object. A valid plant object has at minimum a scientific_name, name and note. On success it returns the whole plant object with status 201 Created, otherwise an error.

POST https://permapeople.org/api/plants
# Response

{<Plant>}

Params

Provide as a JSON object with content-type: application/json.

(required) scientific_name<string>: The scientific name of a plant; if in doubt, use wikipedia to find the correct and current name. (required) name<string>: The common name. Use the most common english name. (required) note<string>: Describe the action you did and what sources you used. (optional) description<string>: A free-text description of the plant. (optional) type<enum>: Plant or Variety. If Variety is used, parent_id is required and must be an existing plant id. (optional) data<array>: An array of {key, value} attribute pairs.

Update a plant

To update a plant, do a PUT request with the plant fields you want to change to the plant’s URL. On success it returns the whole plant object, otherwise an error.

Updates use optimistic locking: send the current version you last read. If the plant changed in the meantime, the server returns 422 with a version mismatch error — re-fetch the plant and try again. New data keys are merged into the existing data (sending data never deletes attributes you did not include); sending an existing key updates its value.

PUT https://permapeople.org/api/plants/101
# Request body

{
  "version" : 10,
  "note" : "Corrected the family from PFAF.",
  "data" : [ { "key" : "Family", "value" : "Moraceae" } ]
}
# Response

{<Plant>}

Params

Provide as a JSON object with content-type: application/json.

(required) version<int>: The current version of the plant as read (optimistic locking). (required) note<string>: A note describing the change. (required) <changed fields>: At least one changed field — any of scientific_name, name, description, type, parent_id, or data.

Errors

If the API throws an error, it will always return an appropriate HTTP status code and valid JSON in the following format:

{
  "error": "short description",
  "msg": "longer description on what to do"
}

Common cases:

Help & Feedback

If you have any questions or need help using the API, feel free to reach out per mail (hello at) or on twitter. 🌱✌️ Happy coding!

Supporting free access to plant data for everyone

Since Permapeople.org is a free service, we ask that those using the Permapeople API for their own projects provide credit (to Permapeople.org) in the form of backlinks, and donate what they can. You can find donation options through our Libera Pay donations page.