Note: a faster API with more consistent response formats can be found at api.inaturalist.org. You will still need to refer to these docs for obtaining an OAuth access token.
The iNat API is a set of REST endpoints that can be used to read data from
iNat and write data back on the behalf of users. Data can be retrieved in
different formats by appending
.[format]
to the endpoint, e.g. /observations.json
to retrieve observations as JSON. Read-only endpoints generally do not require
authentication, but if you want to access data like unobscured coordinates on
behalf of users or write data to iNat, you will need to make authenticated
requests (see below).
Note that endpoints that receive binary data must have the
Content-Type: multipart-formdata
header and parts that are not
binary should be simple form data, even if you're requesting JSON data.
iNaturalist is an OAuth2 provider and supports the
Authorization Code,
Resource Owner Password Credentials,
and
Proof Key for Code Exchange (PKCE)
flows, as well as some custom auth flows for 3rd party authentication. Methods marked
Auth required
require authentication via one of the aforementioned methods. Please make
sure to use
https://www.inaturalist.org
as the base URL for authorization flows and for authenticated requests to
ensure passwords and access tokens are encrypted. Unfortunately we must
maintain the vanilla http endpoints for backward compatability, but all
future API users should make requests with SSL. Note that many PUT and
DELETE methods also check for ownership and will fail if you try to use
them when not authenticated as the user who owns the associated
resources.
Before you make any authenticated requests, you'll need to
create an iNat application.
The
redirect_uri
should be functioning URL to which users will be
redirected upon successfully authorizing your app with access to the
user's iNat data.
There are plenty of language-specific OAuth2 libraries out there, so find one you like or get familiar enough with OAuth2 to construct requests and manage redirect flows yourself. The following examples use Ruby's oauth2 gem.
This is what you'll want to use if your app is a web app, and/or you're sure
you're users can open a web browser for auth *and* you can store your client
secret in a secure fashion. The rough outline is you redirect your user to
iNat, the user is offered the choice to authorize your app with access to
their iNat data, and if they agree, they are redirected to the
redirect_uri
you specified with an
access_token
in the params.
This is a variation on the Authorization Code Flow for use in situations where you cannot store your client secret securely, e.g. in a client-side Javascript application or a mobile application that could be decompiled. Instead of providing a secret to identify itself, the client provides a hashed, single-use "code verifier" when requesting an authorization code. When the client requests an access token it must provide the unhashed code verifier to verify that it is the same client that requested the authorization code. More details at the IETF, but the Doorkeeper docs might be easier to read.
Retrieve an access token based on the user's iNat username and password. This is more for desktop and mobile apps, or any situation where you can store a user's password and they have reason to trust you, i.e. not for web.
This is roughly based on http://tools.ietf.org/html/draft-ietf-oauth-assertions-01#section-6.3 and used to retrieve an access token for a user based on a 3rd party access token from Facebook or Google. It assumes the user is already a member of iNat or wants to be, and uses the 3rd party token to sign them up and / or authenticate them. It returns an access token without the authorization step. This method is only available to partners we're working with directly.
All of these endpoints should use
https://www.inaturalist.org
as the base URL, particularly endpoints that require auth.
/comments
Create comments. Comments are automatically associated with the signed in user.
Formats: json
/comments/:id
Update a comment. Params are the same as POST /comments.
Formats: json
/identifications
Create identifications. Identifications are automatically associated with the signed in user.
Formats: json
/identifications/:id
Update a identification. Params are the same as POST /identifications.
Formats: json
/observations
Primary endpoint for retrieving observations. If you're looking for
pagination info, check the X headers in the response. You should see
X-Total-Entries
, X-Page
, and
X-Per-Page
. JSON
and ATOM responses are what you'd expect, but DwC is
Simple Darwin Core, an
XML schema for biodiversity data. iNat uses JSON responses internally quite a
bit, so it will probably always be the most information-rich. The widget
response is a JS snippet that inserts HTML. It should be used
with a script tag, e.g.
<script src="http://www.inaturalist.org/observations.widget"></script>. Note that while this endpoint doesn't require authentication under normal circumstances, it WILL require it when you request pages higher than 100. If you're going to scrape us, we want to know who you are.
Formats: atom, csv, dwc, json, kml, widget
/observations.json
[{ "user_login": "greatjon", "place_guess": "San Francisco, San Francisco", "location_is_exact": false, "quality_grade": "casual", "latitude": 37.713539, "created_at": "2012-04-10T21:48:25-07:00", "timeframe": null, "species_guess": "gray wolf", "observed_on": "2012-04-10", "num_identification_disagreements": 0, "delta": true, "updated_at": "2012-04-10T21:49:50-07:00", "num_identification_agreements": 0, "license": null, "geoprivacy": null, "positional_accuracy": 354, "coordinates_obscured": false, "taxon_id": 42048, "id": 2281, "iconic_taxon": { "name": "Mammalia", "ancestry": "48460/1/2", "rank": "class", "id": 40151, "rank_level": 50, "iconic_taxon_name": "Mammalia" }, "time_observed_at_utc": "2012-04-11T04:47:55Z", "user_id": 53, "time_observed_at": "2012-04-10T21:47:55-07:00", "observed_on_string": "Tue Apr 10 2012 21:47:55 GMT-0700 (PDT)", "short_description": "", "time_zone": "Pacific Time (US & Canada)", "out_of_range": null, "longitude": -122.340054, "description": "", "user": { "login": "greatjon" }, "positioning_method": null, "map_scale": null, "photos": [], "iconic_taxon_name": "Mammalia", "positioning_device": null, "iconic_taxon_id": 40151 }]
/observations
Primary endpoint for creating observations. POST params can be specified for a single observation (e.g. observation[species_guess]) or as multiple to add a multiple observations at a time (e.g. observations[0][species_guess]).
Formats: html, json
order
is just
an integer starting with zero specifying the order of entry.
observation[observation_field_values_attributes][0][observation_field_id]=1&observation[observation_field_values_attributes][0][value]=foo
.
/observations.json? observation[species_guess]=Northern+Cardinal& observation[taxon_id]=9083& observation[observed_on_string]=2013-01-03& observation[time_zone]=Eastern+Time+(US+%26+Canada)& observation[description]=what+a+cardinal& observation[tag_list]=foo,bar& observation[place_guess]=clinton,+ct& observation[latitude]=41.27872259999999& observation[longitude]=-72.5276073& observation[map_scale]=11& observation[location_is_exact]=false& observation[positional_accuracy]=7798& observation[geoprivacy]=obscured& observation[observation_field_values_attributes][0][observation_field_id]=5& observation[observation_field_values_attributes][0][value]=male& flickr_photos[0]=8331632744
[ { "created_at": "2013-01-15T15:04:57-05:00", "delta": true, "description": "what a cardinal", "geoprivacy": "obscured", "iconic_taxon_id": 3, "id": 3281, "latitude": "41.2995543746", "license": "CC-BY", "location_is_exact": false, "longitude": "-72.5571845909", "map_scale": 11, "num_identification_agreements": 0, "num_identification_disagreements": 0, "observed_on": "2013-01-03", "observed_on_string": "2013-01-03", "out_of_range": null, "place_guess": "clinton, ct", "positional_accuracy": 7798, "positioning_device": null, "positioning_method": null, "quality_grade": "casual", "species_guess": "Northern Cardinal", "taxon_id": 9083, "time_observed_at": null, "time_zone": "Eastern Time (US & Canada)", "timeframe": null, "updated_at": "2013-01-15T17:10:02-05:00", "uri": "http://www.inaturalist.org/observations/3281", "user_id": 1, "user_login": "kueda", "iconic_taxon_name": "Aves", "created_at_utc": "2013-01-15T20:04:57Z", "updated_at_utc": "2013-01-15T22:10:02Z", "time_observed_at_utc": null, "coordinates_obscured": true, "observation_field_values": [{ "created_at": "2013-01-15T15:05:00-05:00", "id": 403, "observation_field_id": 5, "observation_id": 3281, "updated_at": "2013-01-15T15:05:00-05:00", "value": "male" }], "project_observations": [], "observation_photos": [{ "created_at": "2013-01-15T15:04:59-05:00", "id": 995, "observation_id": 3281, "photo_id": 1298, "position": null, "updated_at": "2013-01-15T15:04:59-05:00", "photo": { "created_at": "2013-01-15T15:04:59-05:00", "file_content_type": null, "file_file_name": null, "file_file_size": null, "file_processing": null, "file_updated_at": null, "id": 1298, "large_url": "http://farm9.staticflickr.com/8499/8331632744_8a0fc40fbb_b.jpg", "license": 2, "medium_url": "http://farm9.staticflickr.com/8499/8331632744_8a0fc40fbb.jpg", "metadata": null, "mobile": false, "native_page_url": "http://www.flickr.com/photos/ken-ichi/8331632744/", "native_photo_id": "8331632744", "native_realname": "Ken-ichi Ueda", "native_username": "Ken-ichi", "original_url": "http://farm9.staticflickr.com/8499/8331632744_f84c5a3f3c_o.jpg", "small_url": "http://farm9.staticflickr.com/8499/8331632744_8a0fc40fbb_m.jpg", "square_url": "http://farm9.staticflickr.com/8499/8331632744_8a0fc40fbb_s.jpg", "thumb_url": "http://farm9.staticflickr.com/8499/8331632744_8a0fc40fbb_t.jpg", "updated_at": "2013-01-15T17:10:01-05:00", "user_id": 1 } }] } ]
/observations/:id
Update a single observation. Not that since most HTTP clients do not support PUT requests, you can fake it be specifying a _method param. This basically takes all the same params as POST /observations using the observation[] params. Here we're documenting some additional params for updating.
_delete
param.
observation[observation_field_values_attributes][0][id]=1&observation[observation_field_values_attributes][0][value]=foo
.
You could delete the same with
observation[observation_field_values_attributes][0][id]=1&observation[observation_field_values_attributes][0][_delete]=true
/observations/:id
Delete an observation. Authenticated user must own the observation. No response body, just a 200 SUCCESS.
Formats: json
/observations/297867.json
/observations/:id/quality/:metric
Vote on quality metrics for an observation. These allow any user to vote on aspects of any observation's data quality.
Formats: json
wild
(whether or not observation is of a wild vs. captive
/ cultivated organism),
location
(whether or not coordinates seem accurate),
date
(whether or not date seem accurate).
wild
and agree is false
, the user
is voting that the observation is not of a wild organism, i.e. it is
captive or cultivated.
/observations/:id/quality/:metric
Remove the user's vote on a quality metric. Not the same as voting no.
Same id
and metric
params as POST
/observations/:id/quality/:metric
Formats: json
/observations/:id/viewed_updates
Mark updates associated with this observation (e.g. new comment notifications) as viewed. Response should be NO CONTENT.
Formats: json
/observations/:username
Mostly the same as /observations except filtered by a username.
/observations/project/:id
Just like /observations except filtered by a project. :id can be a project ID or slug. CSV response will return some extra project-specific daa.
Formats: atom, csv, json, kml, widget
/observations/taxon_stats
Retrieve some stats about taxa within a range of observations.
species_counts
are counts of observations identified to the
species level or lower. total
is the total number of taxa
of all ranks. You must include the on
, d1/d2
,
place_id
, user_id
, or projects
params or this endpoint will just return blank results. This endpoint
should accept all the params available for /observations
.
Formats: json
/observations/taxon_stats.json?on=2008-03-19
{ total: 40, species_counts: [ { count: "2", taxon: { id: 8649, name: "Calocitta formosa", rank: "species", rank_level: 10, default_name: { created_at: "2011-05-17T20:12:15-07:00", creator_id: null, id: 139851, is_valid: true, lexicon: "English", name: "White-throated Magpie-Jay", name_provider: null, source_id: null, source_identifier: null, source_url: null, taxon_id: 8649, updated_at: "2011-05-17T20:12:15-07:00", updater_id: null }, image_url: "http://farm6.staticflickr.com/5179/5479426866_e0f6740520_s.jpg", iconic_taxon_name: "Aves", conservation_status_name: "least_concern" } } ], rank_counts: { superfamily: "1", genus: "1", species: "36", family: "2" } }
/observations/user_stats
Retrieve some stats about users within a range of observations.
You must include the on
, d1/d2
,
place_id
, user_id
, or projects
params or this endpoint will just return blank results. This endpoint
should accept all the params available for /observations
.
Formats: json
/observations/user_stats.json?on=2008-03-19
{ "total": 20, "most_observations": [{ "count": "9", "user": { id: "1", count: "2", user: { id: 1, login: "kueda", name: "Ken-ichi Ueda", user_icon_url: "http://www.inaturalist.org/attachments/users/icons/1-thumb.jpg" } } }, ...], "most_species": [{ "count": "9", "user": { id: "1", count: "2", user: { id: 1, login: "kueda", name: "Ken-ichi Ueda", user_icon_url: "http://www.inaturalist.org/attachments/users/icons/1-thumb.jpg" } } }, ...] }
/observation_fields
List / search observation fields. ObservationFields are basically typed data fields that users can attach to observation.
Formats: json
/observation_field_values
Create a new observation field value. ObservationFields are basically typed data fields that users can attach to observation. ObservationFieldValues are the instaces of those fields. Note that you can also create these with nested params in POST /observations.
Formats: json
/observation_field_values/:id
Update an observation field value. Parameters are the same as POST /observation_field_values
Formats: json
/observation_photos
Add photos to observations. This is only for iNat-hosted photos. For adding photos hosted on other services, see POST /observations and PUT /observations/:id. This should be a multipart request.
Content-Disposition: form-data;
and no
Content-Type
/places
Retrieve information about places.
Formats: json
/places.json?taxon=Calochortus+tiburonensis&place_type=open+space
[{ "ancestry": "1/14/2319", "check_list_id": 6079, "code": null, "created_at": "2009-06-29T22:24:50-07:00", "display_name": "Ring Mountain Open Space Preserve, CA, US", "id": 3104, "latitude": "37.9130554199", "longitude": "-122.4938278198", "name": "Ring Mountain Open Space Preserve", "nelat": "37.92107", "nelng": "-122.48223", "parent_id": 2319, "place_type": 100, "source_identifier": "Ring Mountain Preserve", "source_name": "Ring Mountain Preserve", "swlat": "37.90504", "swlng": "-122.50542", "updated_at": "2012-09-26T03:14:01-07:00", "user_id": null, "woeid": null, "place_type_name": "Open Space" }]
/places.json?place_type=state&q=California
[{ "ancestry": "1", "check_list_id": 312, "code": "US-CA", "created_at": "2009-06-29T21:46:28-07:00", "display_name": "California, US", "id": 14, "latitude": "37.2691993713", "longitude": "-119.3069992065", "name": "California", "nelat": "42.008804", "nelng": "-114.131211", "parent_id": 1, "place_type": 8, "source_identifier": "3195", "source_name": "3195", "swlat": "32.528832", "swlng": "-124.480543", "updated_at": "2012-09-26T03:13:52-07:00", "user_id": null, "woeid": 2347563, "place_type_name": "State" }, { "ancestry": "6793", "check_list_id": 7754, "code": "MX-BCN", "created_at": "2009-10-24T18:01:56-07:00", "display_name": "Baja California, MX", "id": 7403, "latitude": "30.3589992523", "longitude": "-114.9445877075", "name": "Baja California", "nelat": "32.71804", "nelng": "-112.4939948948", "parent_id": 6793, "place_type": 8, "source_identifier": "1805", "source_name": "1805", "swlat": "27.835026736", "swlng": "-118.4187830224", "updated_at": "2012-09-26T03:14:35-07:00", "user_id": null, "woeid": 2346265, "place_type_name": "State" }, { "ancestry": "6793", "check_list_id": 9190, "code": "MX-BCS", "created_at": "2010-06-28T12:49:24-07:00", "display_name": "Baja California Sur, MX", "id": 8501, "latitude": "25.4334506989", "longitude": "-111.53881073", "name": "Baja California Sur", "nelat": "28.0027035634", "nelng": "-109.362976567", "parent_id": 6793, "place_type": 8, "source_identifier": "1806", "source_name": "1806", "swlat": "18.2910523956", "swlng": "-115.8180955045", "updated_at": "2012-09-26T03:14:37-07:00", "user_id": null, "woeid": 2346266, "place_type_name": "State" }]
/projects
Retrieve information about projects on iNaturalist.
Formats: json
/projects.json?featured=true
[{ "created_at": "2011-08-12T10:21:28-07:00", "created_at_utc": "2011-08-12T17:21:28Z", "title": "ASC Pika Project", "project_type": "contest", "project_observation_rule_terms": "must be on list", "updated_at": "2012-04-26T19:38:51-07:00", "updated_at_utc": "2012-04-27T02:38:51Z", "source_url": "", "id": 44, "user_id": 477, "featured_at": "2012-04-26T19:38:51-07:00", "featured_at_utc": "2012-04-27T02:38:51Z", "icon_url": "http://www.inaturalist.org/attachments/projects/icons/44/span2/APAlogo.png?1315005828", "icon_file_size": 9454, "icon_file_name": "APAlogo.png", "icon_content_type": "image/png", "description": "The goal of this project is to document the persistence or extirpation of American Pika throughout their range for science and conservation. Read our flyer to find out more! According to the pika range map compiled by the IUCN, American pika are thought to occur in two occur in 2 Countries, 12 States (or Canadian Provinces) and 276 Counties (or Canadian Regional Districts). We seek to collect recent observations verifying Pika persistence in each of these places. This project is supported by the North American Pika Consortium, the California Department of Fish and Game, and the Front Range Pika Project.", "cached_slug": "asc-pika-project", "project_list": { "comprehensive": false, "place_id": null, "created_at": "2011-08-12T12:33:04-07:00", "created_at_utc": "2011-08-12T19:33:04Z", "title": "American Pika Atlas's Check List", "updated_at": "2011-08-12T12:33:04-07:00", "project_id": 44, "updated_at_utc": "2011-08-12T19:33:04Z", "taxon_id": null, "id": 52561, "user_id": null, "last_synced_at": null, "description": "Every species observed by members of American Pika Atlas", "source_id": null }, "terms": "Please only upload observations if you agree to make them available non-commercial scientific analyses. We will attribute you as the photographer if your photos are used in any published scientific analyses. If possible please include locations, dates, and photos with your observations.", "observed_taxa_count": 2, "icon_updated_at": "2011-09-02T16:23:48-07:00", "rule_place": null, "project_observations_count": 12 }]
/projects/:id
Retrieve information about a single project. :id is the project ID or slug.
Formats: json
/projects/:id?iframe=true
This returns a complete web page without header or footer suitable for use in an IFRAME.
/projects/user/:login
Lists projects the user specified by :login
has joined. Actually it lists our
ProjectUser records, which represent membership in a project.
Formats: json
http://www.inaturalist.org/projects/user/kueda.json
[{ "created_at": "2012-05-02T15:27:13-04:00", "id": 63, "observations_count": 0, "project_id": 1, "role": "curator", "taxa_count": 0, "updated_at": "2012-05-02T15:35:22-04:00", "user_id": 23, "user": { "login": "kueda" }, "project": { "created_at": "2011-09-18T17:10:07-04:00", "delta": false, "description": "In which we observe things that may or may not be like white whales.", "featured_at": null, "icon_content_type": "image/jpeg", "icon_file_name": "spilosoma.jpg", "icon_file_size": 119605, "icon_updated_at": "2011-09-18T17:10:05-04:00", "id": 1, "latitude": null, "longitude": null, "map_type": "terrain", "observed_taxa_count": 8, "place_id": null, "project_type": "contest", "slug": "white-whales-et-al", "source_url": "", "terms": "You must be awesome. The thing you've observed must be like unto a white whale.", "title": "White Whales et al.", "updated_at": "2012-05-07T21:31:15-04:00", "user_id": 1, "zoom_level": null, "icon_url": "http://www.inaturalist.org/attachments/projects/icons/1/span2/spilosoma.jpg?1316380205", "project_observation_rule_terms": "", "featured_at_utc": null, "rule_place": null, "cached_slug": "white-whales-et-al", "project_list": { "comprehensive": false, "created_at": "2011-09-18T17:10:07-04:00", "description": "Every species observed by members of White Whales et al.", "id": 99143, "last_synced_at": null, "place_id": null, "project_id": 1, "show_obs_photos": true, "source_id": null, "taxon_id": null, "title": "White Whales et al.'s Check List", "updated_at": "2011-09-18T17:10:07-04:00", "user_id": null }, "project_observation_fields": [] } }]
/projects/:id/members
Get the users who have joined this project.
/projects/:id/join
Adds the authenticated user as a member of the project
Formats: json
/projects/:id/leave
Removes the authenticated user as a member of the project
Formats: json
/project_observations
Add observations to projects
Formats: json
/users
Create a new iNaturalist user
Formats: json
POST /observations
above for valid time zone values,
POST /users.json?user[login]=karina&user[email]=foo@bar.net& user[password]=******&user[password_confirmation]=******
{ "created_at": "2013-03-19T04:17:42Z", "description": null, "email": "foo@bar.net", "icon_content_type": null, "icon_file_name": null, "icon_file_size": null, "icon_updated_at": null, "icon_url": null, "id": 2, "identifications_count": 0, "journal_posts_count": 0, "life_list_id": 3, "life_list_taxa_count": 0, "login": "karina", "name": null, "observations_count": 0, "time_zone": null, "updated_at": "2013-03-19T04:17:42Z", "uri": "http://www.inaturalist.org/users/2" }
/users/:id
Update a user. Takes the same parameters as POST /users
and response should be the same. :id is the user ID.
Formats: json
/users/edit
Retrieve user profile info. Response should be like POST /users
above.
Formats: json
/users/new_updates
Get info about new updates for the authenticated user, e.g. comments and identifications on their content.
Formats: json
GET /users/new_updates.json
[ { created_at: "2013-10-07T16:22:43-07:00", id: 954, notification: "activity", notifier_id: 610, notifier_type: "Comment", resource_id: 1, resource_owner_id: 1, resource_type: "Post", subscriber_id: 1, updated_at: "2013-10-07T16:22:43-07:00", viewed_at: "2013-10-07T16:22:49-07:00" }, { created_at: "2013-09-12T13:39:21-07:00", id: 945, notification: "activity", notifier_id: 607, notifier_type: "Comment", resource_id: 199, resource_owner_id: 1, resource_type: "Observation", subscriber_id: 1, updated_at: "2013-09-12T13:39:21-07:00", viewed_at: "2013-10-07T16:21:09-07:00" } ]