Our API is intended for individual MO users to create and modify their and other users' MO Observations (and other objects) and for developing applications to support them. It is not intended for data scraping or for generalized experimentation with AI, image recognition or large data sets. For those purposes you should use the CSV files described below.
We're an all-volunteer organization with a tiny budget and scarce resources. Using the API to obtain large amounts of data puts an unnecessary strain on those resources and adversely impacts other users. If we notice improper usage of the API or usage that seriously impacts our performance we may institute blocks without notification.
We limit anonymous traffic to MO, including API traffic, by restricting the rate of requests to 20 per minute (1 request every 5 seconds on average), and less than 50% of the resoures of a single server instance. A good rule of thumb would be to read the "run_time" from the response, and make sure you wait at least 5 seconds or the last run_time before making a new request. If you get locked out, please contact us and we can discuss your needs and perhaps better ways of achieving them.
Browsable, per-endpoint documentation — every resource, its supported methods, and every accepted parameter with its description — lives at:
It is generated from the API code itself (bin/rails api:openapi, an
OpenAPI 3.1 spec at /api-docs/openapi.yaml),
so it stays current as the API changes. This file covers the context the
generated docs don't: intended use, bulk-data alternatives, rate limits,
and how the request styles and API keys work.
We update a set of CSV files nightly which are straight dumps of the MO database. If you are looking for large quantities of data, e.g. for data anaylsis or image recognition, look at these files first to see if they have the information you need. That will save our server a considerable amount of unnecessary traffic, and probably save you a lot of work!
- https://mushroomobserver.org/observations.csv
- https://mushroomobserver.org/images_observations.csv
- https://mushroomobserver.org/images.csv
- https://mushroomobserver.org/names.csv
- https://mushroomobserver.org/name_classifications.csv
- https://mushroomobserver.org/name_descriptions.csv
- https://mushroomobserver.org/locations.csv
- https://mushroomobserver.org/location_descriptions.csv
Mushroom Observer supports a simple API based on sending GET, POST, PATCH and DELETE requests to URLs of the form:
https://mushroomobserver.org/api2/<database_table>GET requests are read-only and do not require authentication. POST (create), PATCH (update) and DELETE (destroy) requests require authentication via an API key (see below).
Responses are JSON by default. XML output still works (via the Accept
header or format=xml) but is deprecated and no longer documented; use
JSON.
All GET queries accept a bunch of parameters allowing you to restrict the results to a specified subset. For example, you can filter observations by name, location, date, user, confidence, presence of images, comment text, etc. Combinations are of course welcome.
In addition to these filter parameters a few special pseudoparameters are accepted:
- help=1 -- Return a list of accepted parameters.
- detail=none -- Return only record ids (default).
- detail=low -- Return some basic data with each record.
- detail=high -- Return a great deal of data with each record.
- format=json -- Return JSON response (default).
- format=xml -- Return XML response.
Note that the result will be paginated for detailed responses. High detail responses in particular are intended for very small data sets. (High detail responses generally include 100 results per page, low detail 1000 per page).
It is easy to play with this aspect of the API in a browser. Try the following queries, for example:
- GET https://mushroomobserver.org/api2/observations?children_of=Tulostoma
- GET https://mushroomobserver.org/api2/observations?locations=Delaware&date=6
- GET https://mushroomobserver.org/api2/observations?help=1
These return, respectively, (1) ids of all observations of the genus Tulostoma, (2) ids of all observations from Delaware posted in June (any year), and (3) a list of accepted query parameters.
Most tables accept POST requests for creating new records. Include data for the new record in parameters. Example:
The response will include the id of the new record.
Attach an image as POST data or by URL — see the upload_file and
upload_url parameters on the images endpoint in the
interactive docs.
Structure your PATCH query the same as for GET requests, but also include "set_xxx" parameters to tell MO how to modify the matching records. For example, this would be a way to change the location of a set of your observations:
Structure the query the same as for GET requests. MO will destroy all matching records (that you have permission to destroy). For example, this should delete all your observations from a given location:
Authorization is currently done using an API key. Just include your API key in any POST, PATCH and DELETE requests. An API key belongs uniquely to a single user, so MO will know who you are.
The easiest way for an individual user to obtain an API key is to create one directly via the website:
For convenience, apps may also create a key on behalf of a user using a POST request:
In this latter case, the app creator must first create an API key by hand for that app. This is the key that will be used in the request above to create a new API key for another user. The user will then receive an email asking them to confirm that it's okay for your app to post observations and images and such in their name. The app will then use the user's new API key for all subsequent POST requests. The app will be responsible for remembering and keeping secure each user's API key.
An app can also create the account itself: POST to /api2/users with login, name, email and password, adding create_key=<your app name> to create an API key for the new account in the same request. The new account starts unverified, and the user must confirm their email address as usual before the account and its key can be used.
API keys are bearer credentials, the same widely used pattern as most API authentication: anyone holding a key can act as its user. Treat a key like a password — keep it out of shared code and logs, and send it as POST data rather than in the URL (URLs can end up in server logs). A user can revoke any of their keys at any time from https://mushroomobserver.org/account/api_keys. Contributions that harden this further are welcome — per-key scopes, key expiry/rotation, accepting the key via the Authorization header, or an OAuth flow for third-party apps.
Most of the important database tables have entry points:
- api_keys (POST only)
- collection_numbers
- comments
- external_links
- external_sites (GET only)
- field_slips
- herbaria (GET only)
- herbarium_records
- images
- locations (not DELETE)
- location_descriptions (GET only)
- names (not DELETE)
- name_descriptions (GET only)
- namings
- observations
- occurrences
- projects (not DELETE)
- sequences
- species_lists
- users
The interactive docs list every parameter each of these supports, with descriptions; the same information is available in-band via the special "help=1" parameter. It is safe to mess around with strange parameters and see what they do. Responses also include a copy of the SQL query used ("query"), which can be an effective way of discovering how unfamiliar parameters work. Here's the SQL query from one of the examples above:
SELECT DISTINCT observations.id
FROM `observations`
WHERE MONTH(observations.when) >= 6 AND MONTH(observations.when) <= 6
AND (observations.location_id IN (694,...,14040) OR observations.where LIKE '%Delaware%')
ORDER BY observations.id ASCSee also the database schema here:
v1 -- First version 2016. v2 -- Latest version 2020.
The endpoint is now "api2" instead of "api". The old endpoint has been removed. The default format is now JSON not XML, since JSON is significantly faster. And a few result structures have been tweaked slightly:
api_keys -- field names were wrong (created_at, last_used, num_uses)
herbarium_records -- field names were wrong (initial_determination, accession_number)
images -- moved observation_ids to high-detail response
observations -- full consensus, location and owner details moved to high detail response, now only consensus_id, consensus_name, location_id, location_name and owner_id are found in low detail response; votes moved up to top level of the structure for high detail response (used to be buried within namings)
projects -- removed admin and member ids
species_lists -- removed project_ids, full location details only available in high detail response now
collection_numbers, comments, external_links, herbarium_records -- only includes observation ids now not full details