API
Version 2.0
Last updated 19.12.2025
Learn more about how to request access and more details on our Data sharing documentation
To be able to make requests to the API endpoints, you'll need an API key. This helps us to track usage and apply rate limiting, and ensure a performant experience for all API consumers. To find out more about how to apply for a key, see here.
Once a token has been obtained from the sessions endpoint, it can be used either via a bearer token header, or a token parameter specified alongside the requests for other API calls. This is in addition to the API key, and is only needed for requests that require an authenticated user to access them.
What's new in the API?
We recently expanded several responses so you can quickly understand how a location is monitored, how equipment is calibrated, and whether datasets are actively collecting data. These additions are available from the standard JSON endpoints—no extra headers or beta flags required.
Parameters tested at a site
Every /locations response now includes the parameters array, and each /observations entry
exposes a tested_parameters field. Both contain the human-readable parameter names that were configured on the dataset form.
Use this to drive map legends, filter dashboards, or to quickly confirm whether a location collects the parameter you care about before requesting individual readings.
GET /locations.json?api_key=YOUR_KEY
{
"type": "Feature",
"properties": {
"id": "loc_123",
"name": "River Lookout",
"parameters": ["Temperature", "Dissolved Oxygen", "pH"]
}
}
Location context (water_body_type + investigative monitoring)
Location responses now include water_body_type so integrations can distinguish lakes, rivers, wetlands, and other water bodies without additional lookups.
The investigative flag indicates monitoring at a site is investigative, which can help you filter exploratory locations from long-term baselines in your UI.
GET /locations.json?api_key=YOUR_KEY
{
"properties": {
"name": "River Lookout",
"water_body_type": "river",
"investigative": true
}
}
Equipment range metadata
Equipment objects referenced by form parameters expose range_bottom, range_top, and the formatted
range_limit helper. When paired with the observation data, this lets you communicate the intended detection window
of a given probe or colour comparator. If you need to warn data consumers that a measurement was taken outside that window,
you can compare the reading value to the range metadata supplied here.
Dataset activity (dormant)
The dataset endpoint returns a dormant boolean that flips to true when a dataset has not recorded an observation in the past year.
This flag is ideal for hiding inactive projects from your public dashboards, or for flagging the need to re-engage volunteers when you synchronize records on a schedule.
DataStream sharing status
Dataset responses now report DataStream sharing status. Use share_with_datastream to see if a dataset has requested to join DataStream,
integrated_with_datastream to confirm the integration is complete, and datastream_dataset_url to link directly to the DataStream entry.
GET /datasets/ds_789.json?api_key=YOUR_KEY
{
"name": "Great Lakes Watch",
"share_with_datastream": true,
"integrated_with_datastream": false,
"datastream_dataset_url": null
}
Equipment limit flags on readings
Every reading now includes an equipment_limit flag. When a value exceeds the calibrated range of the associated equipment,
the flag records whether it was above (>, >=) or below (<, <=) the supported range.
Combine this with the range metadata above to show audiences that a result was beyond what the test kit can accurately measure.
GET /observations/obs_456.json?api_key=YOUR_KEY
{
"readings": [
{
"parameter": "Conductivity",
"unit": "µS/cm",
"value": "1800",
"equipment_limit": ">"
}
],
"tested_parameters": ["Conductivity", "Temperature"]
}
Because limit flags are indexed, you can filter observations by limit type when building your own integrations (for example,
request /observations.json?reading_equipment_limit=%3E= to focus on samples that hit the upper detection limit).
Putting it together
A common workflow is to load locations to discover which parameters are measured, fetch the parent dataset to confirm whether it is active, and then pull observations for the matching parameter while checking the equipment limit flags. The steps below outline the flow:
- Request
/locations.jsonand filter theparameterslist for the parameter of interest. - Use the associated
dataset_idto fetch/datasets/:id.jsonand ensuredormantisfalse. - Call
/observations.json?reading_equipment_limit=(optional) and inspect individualequipment_limitflags alongside the equipment ranges.
This approach keeps traffic light, while letting you build rich data stories with clear QA context.
sessions
Authenticate a user and obtain an authentication token used for authenticated calls to other API endpoints
-
Request parameters
{"session":{"email":"","password":""}}
-
Response output
{"id":"","created_at":"","updated_at":"","email":"","first_name":"","last_name":"","token":""}
Endpoints
- POST: /users/sign_in.json
- DELETE: /users/sign_out.json
- POST: /sessions.json
organizations
Returns a list of organizations for this platform
-
Request parameters
{"organization":{"name":"","short_description":"","description":"","primary_country":"","state":"","nearest_town_city":"","street_address":"","contact_details":"","email":"","phone":"","website":"","approved":"","region":"","logo":""}}
-
Response output
{"id":"","created_at":"","updated_at":"","owner_id":"","name":"","program_name":"","description":"","short_description":"","slug":"","primary_country":"","state":"","nearest_town_city":"","street_address":"","contact_details":"","email":"","phone":"","website":""}
Endpoints
- GET: /organizations/:organization_id/region.json
- GET: /organizations/:organization_id/edit-region-geojson.json
- GET: /organizations/:organization_id/locations.json
- GET: /organizations/:organization_id/locations_map.json
- GET: /organizations/:organization_id/contributors.json
- GET: /organizations.json
- POST: /organizations.json
- GET: /organizations/:uuid.json
- PATCH: /organizations/:uuid.json
- PUT: /organizations/:uuid.json
- DELETE: /organizations/:uuid.json
datasets
Returns a list of datasets for this platform
-
Request parameters
{"dataset":{"name":"","description":"","start_date":"","frequency":"","frequency_details":"","standards_guideline_id":"","concerns":"","certification_requirements":"","local_contexts":"","share_with_datastream":"","share_with_watershedreports":"","share_with_uk_opendata":"","joinable":"","region":[{}],"impact_intended_audience":[{}],"impact_intended_use":[{}]}}
-
Response output
{"id":"","created_at":"","updated_at":"","owner_id":"","organization_id":"","form_id":"","name":"","description":"","slug":"","permalink":"","start_date":"","certification_requirements":"","last_observation_at":"","share_with_datastream":"","integrated_with_datastream":"","datastream_dataset_url":"","dormant":"","form":[{"id":"","created_at":"","updated_at":"","name":"","protocol":"","template":"","uuid":"","show_testers":"","show_sample_id":"","form_parameters":[{"id":"","created_at":"","updated_at":"","required":"","samples":"","row_order":"","parameter":[{"id":"","created_at":"","updated_at":"","name":"","qualitative":"","category":"","parameter_options":[{"id":"","created_at":"","updated_at":"","value":"","label":""}]}],"test_method":[{"id":"","created_at":"","updated_at":"","name":""}],"equipment":[{"id":"","created_at":"","updated_at":"","name":"","range_bottom":"","range_top":"","range_limit":""}]}]}]}
Endpoints
- GET: /organizations/:organization_id/datasets.json
- POST: /organizations/:organization_id/datasets.json
- GET: /datasets/:dataset_id/locations.json
- GET: /datasets/:dataset_id/observations.json
- GET: /datasets/:dataset_id/region.json
- GET: /datasets/:dataset_id/edit-region-geojson.json
- GET: /datasets.json
- POST: /datasets.json
- GET: /datasets/:uuid.json
- PATCH: /datasets/:uuid.json
- PUT: /datasets/:uuid.json
- DELETE: /datasets/:uuid.json
locations
Returns the available locations for this platform
-
Request parameters
{"location":{"name":"","description":"","organization_id":"","latitude":"","longitude":"","body_of_water":"","water_body_type":"","site_id":"","assigned_data_collector":"","reference_photo":"","investigative":"","land_uses":[{}],"bank_vegetations":[{}]}}
-
Response output
{"id":"","created_at":"","updated_at":"","name":"","dataset_id":"","latitude":"","longitude":"","body_of_water":"","water_body_type":"","investigative":"","tested_parameters":"","first_observation_at":"","last_observation_at":""}
Endpoints
- POST: /datasets/:dataset_id/locations.json
- GET: /locations/:location_id/observations.json
- GET: /locations.json
- POST: /locations.json
- GET: /locations/:uuid.json
- PATCH: /locations/:uuid.json
- PUT: /locations/:uuid.json
- DELETE: /locations/:uuid.json
- GET: /location/select-dataset.json
forms
Forms
-
Response output
{"id":"","created_at":"","updated_at":"","name":"","protocol":"","template":"","uuid":"","show_testers":"","show_sample_id":"","form_parameters":[{"id":"","created_at":"","updated_at":"","required":"","samples":"","row_order":"","parameter":[{"id":"","created_at":"","updated_at":"","name":"","qualitative":"","category":"","parameter_options":[{"id":"","created_at":"","updated_at":"","value":"","label":""}]}],"test_method":[{"id":"","created_at":"","updated_at":"","name":""}],"equipment":[{"id":"","created_at":"","updated_at":"","name":"","range_bottom":"","range_top":"","range_limit":""}]}]}
Endpoints
- GET: /datasets/:dataset_id/form.json
observations
Returns the observations recorded for this platform
-
Request parameters
{"observation":{"form_id":"","observed_at":"","observed_at_local":"","testers":"","sample_id":"","notes":"","photos":[{}],"readings_attributes":[{"form_parameter_id":"","unit_id":"","test_method_id":"","equipment_id":"","value":"","qualitative_value":"","values":[{}]}]}}
-
Response output
{"id":"","created_at":"","updated_at":"","owner_id":"","location_id":"","dataset_id":"","form_id":"","observed_at":"","notes":"","checked":"","qa_notes":"","qa_admin_notes":"","qa_admin_notes_author_id":"","qa_admin_notes_updated_at":"","tested_parameters":"","readings":[{"id":"","created_at":"","updated_at":"","parameter":"","unit":"","value":"","equipment_limit":""}]}
Endpoints
- POST: /locations/:location_id/observations.json
- GET: /observations.json
- GET: /observations/:uuid.json
- PATCH: /observations/:uuid.json
- PUT: /observations/:uuid.json
- DELETE: /observations/:uuid.json
point_of_interests
Point Of Interests
-
Response output
{"id":"","created_at":"","updated_at":"","name":"","dataset_id":"","receiving_water_course":"","has_upstream":"","has_downstream":"","url":"","lat":"","lng":"","current_status":"","current_status_at":""}
Endpoints
- GET: /poi.json
- GET: /poi/:uuid.json