Hi all. I made a small R package called inatpick for downloading photo files and metadata from iNaturalist observations. This happened as I needed to download some photos for my own research and decided I wanted to go back to this script in the future and maybe other people would find it helpful. First time I make an R package (disclaimet: with the help of AI), so please let me know if you find problems, improvements, etc.
inatpicklets you:
Search by taxon and place name to find IDs
Filter observations by taxon, place, user, quality grade (research / needs ID / any), date (year/month), and annotation (e.g. “flowering”, “green leaves”)
Restrict to CC-licensed photos only
Download all matching photos at your chosen resolution
Auto-save a metadata.csv alongside the photos, including coordinates, place, taxon, common name, observer, date, URL, and photo count
It respects API rate limits (delays between requests, no aggressive bulk pulling).
you should build this on v2 of the API, since v2 is no longer in beta.
for larger sets of observations, you probably need your delay between requests to be longer than 0.5 sec.
you may need to handle cases where the set includes more than 10000 observations.
ideally, you would want to keep track of how much you’re downloading from the non-AWS bucket, since iNat says they don’t want you to download more than some GB limit per hour and day.
What if you metadata has the coordinates in it?
Thinking of species that are automatically obscured/ or obscured by me.
Will this also be downloaded?
If so, then it would be a problem, and making obscure irrelevant.
v2 API: Good call, I’ll plan a migration to v2 in next release.
Delay: Ok, 0.5s is fine for small fetches but I’ll try to adapt it for larger pulls.
>10k observations: I’ll switch to cursor-based pagination (id_above) to handle this.
Bandwidth on non-AWS bucket: Good point, I could try to add tracking of cumulative downloaded MB and throttle to stay within iNat’s hourly/daily limits. I need to find the policy first, tho…
EDIT: I just saw
Downloading over 5 GB of media per hour or 24 GB of media per day may result in a permanent block
Hi!
No worries about that. The metadata it downloads is from the observation, not from the picture.
The pictures are processed by iNat and what is available from the API (and downloaded by inatpick) is already filtered if “obscured”, so no EXIF metadata can reveal the real coordinates of the original picture.
the API includes obscured and taxon obscured observations, but it won’t give you private coordinates unless you get the information via an authorized request and you have the proper authorization to get private coordinates for any given observation.
i don’t see you adding any filtering that would have excluded obscured or taxon obscured observations in your requests.
speaking of filters, your filtering for licensed is for observation-level licensing, but that’s different from photo-level licensing, and i think you’re conflating the two. you probably should handle observation filtering based on photo_licensed and probably photo_license, too (along with corresponding client-side filtering on photo records to make sure individual photos match those observation-level filteres). and you might as well add photos=true as a default, since you’re trying to download photos.
I meant that currently the coordinates of any “obscured” observation are not included in the metadata, which is what I understood from what @Shauns asked about. No metadata from an obscured observation or its pictures reveal coordinates right now. So “obscured” observations are not filtered out, but the location related metadata is not being downloaded.
As you pointed out, it would require special authorisation when using the API to get those coordinates.
Regarding the license filtering. Good point. I will include a way to filter by photo license as well.
Same for the asynchronous requests.
Thank you again for all the feedback.