Tutorial 1: Observations¶
This notebook will give a quick introduction to things you can do using data from your own iNaturalist observations.
import ipyplot
from rich import print
from pyinaturalist import (
enable_logging,
iNatClient,
pprint,
)
enable_logging()
client = iNatClient()
Basic observation search¶
We’ll start with the observation search page. client.observations.search() supports all the same search filters as the ones you see on iNaturalist.org:


Let’s start by searching for all of your own observations:
# Replace with your own username
USERNAME = 'jkcook'
my_observations = client.observations.search(user_id=USERNAME).all()
[02-21 23:40:11] INFO Request: session.py:332 GET https://api.inaturalist.org/v1/observations?user_id=jkcook&per_page=200&or der_by=id&order=asc User-Agent: python-requests/2.32.5 pyinaturalist/0.21.1 Accept-Encoding: gzip, deflate, br, zstd Accept: application/json Connection: keep-alive
[02-21 23:40:14] INFO This query will fetch 787 results in 4 requests. Estimated total request paginator.py:205 time: 3 seconds
INFO Request: session.py:332 GET https://api.inaturalist.org/v1/controlled_terms User-Agent: python-requests/2.32.5 pyinaturalist/0.21.1 Accept-Encoding: gzip, deflate, br, zstd Accept: application/json Connection: keep-alive
INFO Request: session.py:332 GET https://api.inaturalist.org/v1/observations?user_id=jkcook&id_above=310293 43&per_page=200&order_by=id&order=asc User-Agent: python-requests/2.32.5 pyinaturalist/0.21.1 Accept-Encoding: gzip, deflate, br, zstd Accept: application/json Connection: keep-alive
[02-21 23:40:16] INFO Request: session.py:332 GET https://api.inaturalist.org/v1/observations?user_id=jkcook&id_above=318247 04&per_page=200&order_by=id&order=asc User-Agent: python-requests/2.32.5 pyinaturalist/0.21.1 Accept-Encoding: gzip, deflate, br, zstd Accept: application/json Connection: keep-alive
[02-21 23:40:19] INFO Request: session.py:332 GET https://api.inaturalist.org/v1/observations?user_id=jkcook&id_above=385882 26&per_page=200&order_by=id&order=asc User-Agent: python-requests/2.32.5 pyinaturalist/0.21.1 Accept-Encoding: gzip, deflate, br, zstd Accept: application/json Connection: keep-alive
Observation data¶
Take a look at one of the observations to see what information it contains:
print(my_observations[0])
Observation( id=30688807, created_at='2019-08-12 15:22:47+00:00', captive=False, community_taxon_id=1415100, description='Located in Green Meadows West Prairie\n\nSpecies IDs:\nhttps://sites.google.com/site/gmwprairie/gmw-prairie-flora', identifications_count=1, identifications_most_agree=True, identifications_most_disagree=False, identifications_some_agree=True, license_code='CC-BY-NC', location=(41.67206561, -93.72957587), mappable=True, num_identification_agreements=1, num_identification_disagreements=0, oauth_application_id=2, obscured=False, observed_on='2019-08-12 10:16:00+00:00', outlinks=[{'source': 'GBIF', 'url': 'http://www.gbif.org/occurrence/2429228652'}], owners_identification_from_vision=False, place_guess='Johnston, IA, USA', place_ids=[ 1, 24, 1582, 9853, 59613, 64422, 64423, 66741, 82256, 97394, 116535, 129109, 137509, 154492, 155074 ], positional_accuracy=12, preferences={'prefers_community_taxon': None}, project_ids=[48611], project_ids_without_curator_id=[48611], public_positional_accuracy=12, quality_grade='research', reviewed_by=[1436999, 2115051], site_id=1, species_guess='Rocky Mountain Beeplant', updated_at='2022-10-04 06:10:12+00:00', uri='https://www.inaturalist.org/observations/30688807', uuid='aea799e1-4754-4eaf-adca-84720cdeaeb2', annotations=[Annotation(term='Flowers and Fruits', value='Flowers')], application=None, comments=[], faves=[], flags=[], identifications=[ Identification( id=302932647, username='jkcook', taxon_name='Cleomella serrulata (Rocky Mountain Beeplant)', created_at='Oct 04, 2022', truncated_body='' ), Identification( id=66501609, username='jkcook', taxon_name='Peritoma serrulata (Rocky Mountain Beeplant)', created_at='Aug 12, 2019', truncated_body='' ), Identification( id=74656703, username='colincroft', taxon_name='Peritoma serrulata (Rocky Mountain Beeplant)', created_at='Oct 16, 2019', truncated_body='' ), Identification( id=302933884, username='colincroft', taxon_name='Cleomella serrulata (Rocky Mountain Beeplant)', created_at='Oct 04, 2022', truncated_body='' ) ], ofvs=[], photos=[ Photo( id=47956314, license_code='CC-BY-NC', url='https://inaturalist-open-data.s3.amazonaws.com/photos/47956314/square.jpeg' ) ], project_observations=[ProjectObservation(project={'id': 48611}, user_id=2115051)], quality_metrics=[], sounds=[], taxon=Taxon(id=1415100, full_name='Cleomella serrulata (Rocky Mountain Beeplant)'), user=User(id=2115051, login='jkcook', name='Jordan Cook'), votes=[] )
Observation data compared to the web UI¶
Here is how some of those fields correspond to what you see on an observation page on iNaturalist.org:

You’ll notice that there are many more fields available; see the Observation docs for a complete list.
Previewing data¶
In many cases, you will want to quickly preview API results without looking through the full details for each result. pyinaturalist.pprint() can be used to show a condensed table of almost all response types. Here’s an example with just the first 30 results:
pprint(my_observations[:30])
ID Taxon ID Taxon Observed on User Location ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 30688807 1415100 Cleomella serrulata (Rocky Mountain Aug 12, 2019 jkcook Johnston, IA, USA Beeplant) 30688955 47912 Asclepias tuberosa (Butterfly Aug 12, 2019 jkcook Johnston, IA, USA Milkweed) 30689111 60251 Verbena hastata (Blue Vervain) Aug 12, 2019 jkcook Johnston, IA, USA 30689221 121968 Andropogon gerardi (Big Bluestem) Aug 12, 2019 jkcook Johnston, IA, USA 30689306 121968 Andropogon gerardi (Big Bluestem) Aug 12, 2019 jkcook Johnston, IA, USA 30689425 128701 Desmanthus illinoensis (Illinois Aug 12, 2019 jkcook Johnston, IA, USA Bundleflower) 30689463 121976 Silphium laciniatum (Compass Plant) Aug 12, 2019 jkcook Johnston, IA, USA 30689506 168207 Rudbeckia subtomentosa (Sweet Aug 12, 2019 jkcook Johnston, IA, USA Black-Eyed Susan) 30689603 121976 Silphium laciniatum (Compass Plant) Aug 12, 2019 jkcook Johnston, IA, USA 30689780 81594 Elymus hystrix (Bottlebrush Grass) Aug 12, 2019 jkcook Johnston, IA, USA 30690105 127907 Chamaecrista fasciculata (Partridge Aug 12, 2019 jkcook Johnston, IA, USA Pea) 30690175 141767 Veronicastrum virginicum (Culver's Aug 12, 2019 jkcook Johnston, IA, USA Root) 30690204 48678 Genus Solidago (Goldenrods) Aug 12, 2019 jkcook Johnston, IA, USA 30690327 128701 Desmanthus illinoensis (Illinois Aug 12, 2019 jkcook Johnston, IA, USA Bundleflower) 30726806 127186 Securigera varia (Purple Crownvetch) Aug 12, 2019 jkcook Johnston, IA, USA 30727162 128695 Eryngium yuccifolium (Rattlesnake Aug 12, 2019 jkcook Johnston, IA, USA Master) 30727377 129000 Elymus canadensis (Canada Wild Rye) Aug 12, 2019 jkcook Johnston, IA, USA 30727961 48662 Danaus plexippus (Monarch) Aug 12, 2019 jkcook Green Meadows West Prairie 30728796 120215 Bombus griseocollis (Brown-Belted Aug 12, 2019 jkcook Green Meadows West Prairie Bumble Bee) 30728902 55556 Oncopeltus fasciatus (Large Milkweed Aug 12, 2019 jkcook Green Meadows West Prairie Bug) 30729015 81599 Silphium perfoliatum (Cup Plant) Aug 12, 2019 jkcook Green Meadows West Prairie 30729970 47911 Asclepias syriaca (Common Milkweed) Aug 12, 2019 jkcook Johnston, IA, USA 30729981 130382 Heliopsis helianthoides (False Aug 12, 2019 jkcook Johnston, IA, USA Sunflower) 30730005 204330 Iris domestica (Blackberry Lily) Aug 12, 2019 jkcook Johnston, IA, USA 30730009 128695 Eryngium yuccifolium (Rattlesnake Aug 12, 2019 jkcook Johnston, IA, USA Master) 30730021 54781 Quercus macrocarpa (Bur Oak) Aug 12, 2019 jkcook Johnston, IA, USA 30730033 127907 Chamaecrista fasciculata (Partridge Aug 12, 2019 jkcook Green Meadows West Prairie Pea) 30730042 48627 Echinacea purpurea (Purple Aug 12, 2019 jkcook Johnston, IA, USA Coneflower) 30730087 62741 Rudbeckia hirta (Black-Eyed Susan) Aug 12, 2019 jkcook Johnston, IA, USA 30730133 56031 Genus Calystegia (False Bindweeds) Aug 12, 2019 jkcook Green Meadows West Prairie
Observation species¶
On iNaturalist.org, the next tab of the observation search page is Species. You can get this information with client.observations.species_counts().
For example, all the frogs and toads observed in Mexico:

Here is how to get that same information from the API:
# Note: 6793 is the place ID for Mexico, and 20979 is the taxon ID for the Order Anura
taxa = client.observations.species_counts(place_id=6793, taxon_id=20979)
[02-21 23:40:28] INFO Request: session.py:332 GET https://api.inaturalist.org/v1/observations/species_counts?place_id=6793&t axon_id=20979 User-Agent: python-requests/2.32.5 pyinaturalist/0.21.1 Accept-Encoding: gzip, deflate, br, zstd Accept: application/json Connection: keep-alive
Then we can see a preview of the top 10 results:
pprint(taxa[:20])
ID Rank Scientific name Common name Count ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 24277 species 🐸 Smilisca baudinii Mexican Treefrog 6083 517119 species 🐸 Rhinella horribilis Giant Toad 5668 1668926 species 🐸 Dryophytes arenicolor Canyon Tree Frog 4797 65860 species 🐸 Incilius valliceps Central American Gulf Coast Toad 4713 1668952 species 🐸 Dryophytes eximius Mountain Tree Frog 4144 1148187 complex 🐸 Trachycephalus vermiculatus Vermiculated Tree Frog 3021 135027 species 🐸 Agalychnis dacnicolor Mexican Giant Tree Frog 2173 65849 species 🐸 Incilius nebulifer Gulf Coast Toad 2114 64984 species 🐸 Anaxyrus punctatus Red-spotted Toad 2039 554652 species 🐸 Rheohyla miotympanum Small-eared Tree Frog 1867 65975 species 🐸 Lithobates berlandieri Rio Grande Leopard Frog 1797 24259 species 🐸 Pseudacris regilla Pacific chorus frog 1564 22951 species 🐸 Leptodactylus melanonotus Sabinal Frog 1356 65847 species 🐸 Incilius mazatlanensis Sinaloa toad 1318 26698 species 🐸 Spea multiplicata Mexican Spadefoot 1273 65850 species 🐸 Incilius occidentalis Pine Toad 1210 65846 species 🐸 Incilius marmoreus Marbled Toad 1146 70725 species 🐸 Lithobates brownorum Browns' Leopard Frog 1131 26691 species 🐸 Scaphiopus couchii Couch's Spadefoot 1095 64970 species 🐸 Anaxyrus boreas Western Toad 992
Observation identifiers¶
client.observations.identifiers() gets us information shown on the next tab: Identifiers:

users = client.observations.identifiers(place_id=6793, taxon_id=20979)
pprint(users[:10])
[02-21 23:40:37] INFO Request: session.py:332 GET https://api.inaturalist.org/v1/observations/identifiers?place_id=6793&taxo n_id=20979&per_page=500 User-Agent: python-requests/2.32.5 pyinaturalist/0.21.1 Accept-Encoding: gzip, deflate, br, zstd Accept: application/json Connection: keep-alive
ID Username Display name Count ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 22707 coatzin_tutor Matías Domínguez-Laso 7664 1474958 rene_vela 6558 7835388 iske88 5770 46681 cris-tzabcan Cristian Olvera 4860 4144631 leomcampos24 Leonardo M. Martínez-Campos 4853 1051916 pedro_nahuat Pedro E. Nahuat-Cervera 3791 1315 escalante-pasos Jorge Armín Escalante-Pasos 3780 36855 sonoran Chris Gruenwald Herp.mx 3417 23647 jorgehvaldez Jorge H. Valdez 3328 2313757 alexbasti Salamander 2851
Observation observers¶
And client.observations.observers() gets us information from the Observers tab:

users = client.observations.observers(place_id=6793, taxon_id=20979)
pprint(users[:10])
[02-21 23:40:40] INFO Request: session.py:332 GET https://api.inaturalist.org/v1/observations/observers?place_id=6793&taxon_ id=20979&per_page=500 User-Agent: python-requests/2.32.5 pyinaturalist/0.21.1 Accept-Encoding: gzip, deflate, br, zstd Accept: application/json Connection: keep-alive
ID Username Display name Count ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 1051916 pedro_nahuat Pedro E. Nahuat-Cervera 1614 23647 jorgehvaldez Jorge H. Valdez 701 1291340 bromelio_67 José Rogelio Cedeño Vázquez 526 54220 aplomadobirdy Big Birdy 454 5772 juancruzado Juan Cruzado Cortés 453 28799 luis_diaz-gamboa Luis Díaz-Gamboa 391 46555 magazhu Cheryl Harleston López Espino 380 4325541 david_jacobo David Jacobo 375 71758 andrea_navarro20 Andrea Navarro 355 2950223 sebastian_hb Sebastián de Jesús Herrera Buenfil 350
Observation photos¶
When you’re working in Jupyter, there are a number of ways to preview observation photos. For these examples, we’ll use your own observation data from the first step in this tutorial.
Viewing individual observation photos¶
Use Photo.show() to see a photo from a single observation:
my_observations[-4].photos[0].show()
Observation photo grid¶
We can use ipyplot to preview observation images. Observation.photos contains a list of Photo objects, and we can use those to get a thumnail URL for first photo from each observation. For image labels, just call str(observation) to get a summary of the observation (who/what/when/where).
images = [obs.photos[0].thumbnail_url for obs in my_observations[:15]]
labels = [obs.taxon.full_name for obs in my_observations[:15]]
ipyplot.plot_images(images, labels)
Observation photo grid grouped by iconic taxon¶
We can organize this a bit more by grouping these photos by iconic taxon. Use ipyplot.plot_class_tabs to group by label, and use Observation.taxon.iconic_taxon_name as the image labels:
images = [obs.photos[0].thumbnail_url for obs in my_observations]
labels = [obs.taxon.iconic_taxon_name for obs in my_observations]
ipyplot.plot_class_tabs(images, labels, max_imgs_per_tab=15)
Observation histogram¶
Another useful format is the
observation histogram,
which shows the number of observations over a given interval.
The default is month_of_year, which will show counts of all your observations by month, for all years combined:
histogram = client.observations.histogram(user_id=USERNAME)
pprint(histogram)
[02-21 23:40:53] INFO Request: session.py:332 GET https://api.inaturalist.org/v1/observations/histogram?user_id=jkcook User-Agent: python-requests/2.32.5 pyinaturalist/0.21.1 Accept-Encoding: gzip, deflate, br, zstd Accept: application/json Connection: keep-alive
Month Count ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ Jan 8 █ Feb 1 █ Mar 20 ███ Apr 31 ████ May 35 █████ Jun 66 ████████ Jul 17 ███ Aug 414 ██████████████████████████████████████████████████ Sep 95 ████████████ Oct 70 █████████ Nov 23 ███ Dec 7 █
The underlying data is just a dict of {month: count}:
pprint(histogram.raw)
{1: 8, 2: 1, 3: 20, 4: 31, 5: 35, 6: 66, 7: 17, 8: 414, 9: 95, 10: 70, 11: 23, 12: 7}
Or use month + a date range to get monthly counts for a single year:
histogram = client.observations.histogram(
user_id=USERNAME, interval='month', d1='2020-01-01', d2='2020-12-31'
)
pprint(histogram)
[02-21 23:41:02] INFO Request: session.py:332 GET https://api.inaturalist.org/v1/observations/histogram?user_id=jkcook&inter val=month&d1=2020-01-01T00%3A00%3A00%2B00%3A00&d2=2020-12-31T00%3A00%3A00% 2B00%3A00 User-Agent: python-requests/2.32.5 pyinaturalist/0.21.1 Accept-Encoding: gzip, deflate, br, zstd Accept: application/json Connection: keep-alive
Month Count ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 2020-01 5 ███████ 2020-02 1 ██ 2020-03 0 2020-04 9 ████████████ 2020-05 27 ████████████████████████████████████ 2020-06 38 ██████████████████████████████████████████████████ 2020-07 12 ████████████████ 2020-08 8 ███████████ 2020-09 8 ███████████ 2020-10 1 ██ 2020-11 1 ██