iNatClient, an improved
interface first introduced as a preview in v0.15, is complete.
Highlights:
- Provides a single object to run all queries from
- Returns fully typed model objects
- Paginated endpoints wrap results in a lazy-loaded
Paginatorobject to significantly simplify pagination - Simplifies handling advanced session settings
- Automatically refreshes access tokens and pass them to authenticated endpoints if/when needed
Supported resources include:
- Annotations and observation fields (including create/update/delete)
- Identifications
- Observations (including create/update/delete/media upload)
- Places
- Projects
- Taxa
- Users
- Unified text search
- Added
ClientSessionargumentuse_file_lock(replacesFileLockSQLiteBucketuse)
- Add
termandvaluearguments foriNatClient.annotations.create(), to add annotations by label instead of by ID - Add
exact_matchoption foriNatClient.taxa.autocomplete() - Add
full_recordsoption foriNatClient.taxa.autocomplete()
- Add
iNatClient.observations.life_list()to get a user's dynamic life list data - Add taxon endpoints for v2 API:
pyinaturalist.v2.get_taxa()pyinaturalist.v2.get_taxa_by_id()pyinaturalist.v2.get_taxa_autocomplete()pyinaturalist.v2.get_taxa_iconic()
- Add an
Observation.formatted_locationproperty (coordinates + geoprivacy) - Add an
Observation.place_strproperty (fall back to coordinates ifplace_guessis missing)
Add the following new attributes, mostly from v2 API responses:
New model attributes:
BaseMedia.attribution_nameBaseMedia.hiddenComment.parent_idComment.parent_typeControlledTerm.blockingControlledTerm.labelsControlledTerm.valid_within_cladeControlledTermValue.labelsControlledTermValue.valid_within_cladeIdentification.observation_idIdentification.taxon_change_idIdentification.taxon_change_typeMessage.comments_countObservationFieldValue.observation_fieldPhoto.typePlace.matched_termPlace.observations_countPlace.userPlace.without_check_listProject.delegated_project_idProject.is_delegated_umbrellaProjectObservation.current_user_is_memberProjectUser.prefers_curator_coordinate_access_forProjectUser.taxa_countQualityMetric.userUser.descriptionUser.faved_project_idsUser.last_activeUser.monthly_supporterUser.preferencesUser.updated_at
Combine ID-only and nested-object API response values on the following models:
consolidated model attributes:
Flag.user_id->Flag.user.idIdentification.taxon_id->Identification.taxon.idObservationFieldValue.taxon_id->ObservationFieldValue.taxon.idObservationFieldValue.user_id->ObservationFieldValue.user.idProject.user_id->Project.user.idProjectObservation.project_id->ProjectObservation.project.idProjectObservation.user_id->ProjectObservation.user.idQualityMetric.user_id->QualityMetric.user.idVote.user_id->Vote.user.id
In addition, checking attributes on nested objects will not raise AttributeError if missing from the API response. This means that instead of checking, for example, id = Project.user.id if Project.user else Project.user_id, just check Project.user.id.
- Add a separate
write_timeoutparameter forClientSession, to be used for create/update endpoints; default to 60 seconds - For create/update endpoints, use write timeout as connect (socket) timeout
- Extend retry behavior to apply to write timeouts
- Support
ClientSession(timeout=None)to disable all timeouts (connect, read, and write)
- Fix displaying images in Jupyter with
Photo.show() - Fix corner case resulting in
observed_onnot being converted todatetime - Fix list slicing on custom collection types (
TaxonCounts, etc.) - Fix pprint with empty results
- Add support for python 3.15
- Update to pyrate-limiter v4. See its changelog for breaking changes, if you are using its features directly. Changes in pyinaturalist:
- Remove
ClientSessionargumentlock_path; lockfile will be placed in the same directory asratelimit_path(defaults to platform-specific user data dir). - Remove
ClientSessionargumentmax_delay(removed upstream) - Deprecate
FileLockSQLiteBucketin favor ofuse_file_lock(but still available for backwards-compatibility)
- Remove
- Drop support for python 3.8 and 3.9 (removed upstream)
- Remove
pyinaturalist.rest_apiandpyinaturalist.node_apimodules (deprecated in 0.14)
- Update
v2.create_observation()andupdate_observation()to accept multiple observation field values, consistent with v0 and v1 APIs. - When paginating observations by ID ranges, allow reversing the order with
order='desc' - Add
force_refreshoption to bypass the cache (applies to most API functions) - Add additional response info to debug logging
- Add create/update/delete observation endpoints for v2 API:
pyinaturalist.v2.create_observation()pyinaturalist.v2.update_observation()pyinaturalist.v2.delete_observation()
- Add observation media upload for v2 API (
pyinaturalist.v2.upload()) - Add support in v2
get_observations()for querying more than 30 observation IDs at once
- Add
Photo.original_filenameandSound.original_filenamefrom v2 observation results - Add
ObservationFieldValue.updater_idattribute in observation responses - Add
Comment.updated_atattribute - Add
Histogramclass to provide terminal formatting for histogram responses - Handle slash-delimited
Place.ancestryfield from places CSV export
- Fix error when initializing Place objects from empty results
- Fix
io.UnsupportedOperationerror when uploading observation photos from URLs
- Add async paginator method to get a single result (
Paginator.async_one()) - Add support for python 3.14
- Handle empty user when pretty-printing
Identificationobjects ClientSessionargumentsratelimit_pathandlock_pathwill now apply to the separate rate-limiter used for/projects/{id}
- Fix
AttributeErrorwhen initializingObservation.taxonwith aTaxonobject
- Drop support for python 3.7
- Add
User.annotated_observations_countfield - Add
root_idfilter totaxon.make_tree()to explicitly set the root taxon instead of determining it automatically - Fix
taxon.make_tree()rank filtering to allow skipping any number of rank levels - Fix
taxon.make_tree()rank filtering to handle all available ranks filtered out - Update
taxon.make_tree()to return copies of original taxon objects instead of modifying them in-place - Update
Taxon.flatten(hide_root=True)to only hide the root taxon if it was automatically inserted bymake_tree() - Add shortcut properties to
Taxonfor ancestors of common ranks:Taxon.kingdom,phylum,class_(note the_; 'class' is a reserved keyword),order,family,genus - Update
Observation.taxon.ancestorsbased on identification data, if available
- Increase default request timeout from 10 to 20 seconds
- Add
validate_token()function to manually check if an access token is valid - Support rate limits less than one request per second (example:
ClientSession(per_second=0.5)) - Add error handling for file uploads over 20MB (not accepted by API)
- Allow setting lockfile path used for multiprocess rate limiting (example:
ClientSession(lock_path='/tmp/pyinat.lock'))
- Fix
KeyErrorwhen usingcreate_observation()in dry-run mode
- Add support for python 3.13
- Convert packaging and project config to
uv. This has no impact on users. For developers, see Contributing Guide for details.
- Added new User endpoint:
get_current_user() - Added new Taxon endpoint:
get_life_list_metadata()
Add support for searching observations by observation fields, using a new observation_fields param for the following functions:
get_observations()get_observation_histogram()get_observation_identifiers()get_observation_observers()get_observation_popular_field_values()get_observation_species_counts()
Observation:
- Add
Observation.ident_taxon_idsdynamic property to get all identification taxon IDs (with ancestors) - Add
Observation.cumulative_idsdynamic property to calculate agreements/total community identifications - Add
Applicationmodel forObservation.application(for v2 API) - Add
Favemodel forObservation.faves - Add
Flagmodel forObservation.flags - Add
QualityMetricmodel forObservation.quality_metrics - Add
Soundmodel forObservation.sounds - Add
Votemodel forObservation.votes
Project:
- Add
Project.last_post_atdatetime attribute - Add
Project.observation_requirements_updated_atdatetime attribute
Taxon:
- Add
make_tree()function to build a tree fromTaxonobjects or aLifeList - Add
pprint_tree()function to print a taxon tree on the console - Add
Taxon.flatten()method to return a taxon and its descendants as a flat list - Fix initialization of
ListedTaxon.place
- Added support for python 3.12
get_observation()is now deprecated, and will be removed in a future release- Please use
get_observations()orget_observations_by_id()instead
- Please use
- Remove redundant
Annotation.controlled_attribute_idandcontrolled_value_idattributes (These can still be used as init arguments)
- Added new Observation endpoint:
get_observations_by_id()- Replaces
get_observation - Accepts multiple IDs
- Uses a different endpoint (
GET /observations/{id}instead ofGET /observations?id={id}) - returns full term and value details for annotations
- Replaces
- Add some undocumented request params to
get_taxa_by_id():localepreferred_place_idall_names
- Add full mapping of conservation status codes for IUCN, NatureServe, Norma Oficial Mexicana, and other generic codes
- Translate
ConservationStatus.status_namebased on status code and authority for more consistent results - Add
ConservationStatus.display_nameproperty - Add
Observation.default_photoproperty - Use taxon icon as placeholder for
Observation.default_photoif observation has no photos - Update
Annotationmodel to include controlled term and value details from updatedGET /observations/{id}response format - Add
Annotation.termandvalueproperties and init arguments for simpler initialization from term and value labels
- Add
cache_controloption toClientSessionto disable using Cache-Control headers for cache expiration (enabled by default) - Add
urls_expire_afteroption toClientSessionto update default cache expiration URL patterns
- Fix error
413: Payload too largewhen usingadd_project_users()ordelete_project_users()with a project with a large number of rules - Fix JWT caching
- Fix
ObservationFieldValueconversions fordateanddatetimefields - Fix printing
Annotationobjects withrich - Fix printing
Observationtables when taxon is missing
- Added support for python 3.11
- All API functions that accept taxonomic rank parameters (
rank,lrank,observation_hrank,etc.) now accept all rank variations that iNaturalist accepts (var,sppsub-species, etc.) - Optionally use
ultrajsoninstead of stdlibjson, if installed - Add
loopargument to iNatClient and Paginator classes to allow passing an async event loop to be used for async iteration - Add
Paginator.async_all()method (async, non-blocking version of.all())
- Use a single data directory instead of separate 'cache' and 'user data' dirs
- Platform-dependent; on Linux, for example, this will be
~/.local/share/pyinaturalist
- Platform-dependent; on Linux, for example, this will be
- Add retries for requests that return invalid (truncated) JSON
max_retriesandbackoff_factorarguments forClientSessionwill apply to these retries (in addition to other request errors)
- Handle nested 'photo' dictionaries when loading
Taxon.taxon_photos - Add model for
ListedTaxon.list, and handle differences in format between/taxaand/observations/{id}/taxon_summary - Minor fixes and improvements for creating and converting
Observation,Taxon, and other model objects
- Fix pagination bug causing only the first two pages of results to be returned
- Add Photo.uuid, observation_id, and user_id fields (for compatibility with inaturalist-open-data)
- Handle errors in ObservationField type conversions
- Improve terminal output for nested model objects (like
Observation.taxon,Taxon.ancestors, etc.) - If
Taxon.ancestor_idsis missing, populate from eitherancestrystring orancestorobjects, if possible
- Dropped support for python 3.6
- Added new Observation endpoint:
get_observation_popular_field_values()
- Added new Project endpoint and helper functions:
update_project()add_project_users()delete_project_users()
- Updated
get_projects_by_id()to allow string values (URL slugs) forproject_id - Updated
get_user_by_id()to allow string values (usernames) foruser_id - Added support for setting timeout for individual API requests (with
timeoutparameter) - Added support for setting cache timeout for individual API requests (with
expire_afterparameter) - Added support for bypassing the cache for individual API requests (with
refreshparameter)
- Added support for JWT authentication, which will now be used by default
- To get an OAuth access token instead of a JWT, call
get_access_token(jwt=False)
- To get an OAuth access token instead of a JWT, call
- Added caching to
get_access_token(). JWTs will be stored in the API response cache and reused until they expire.
- Remove default values from output when model objects are printed with
rich - Add
ControlledTermCountmodel for use with/observations/popular_field_values Photo: AddextandmimetypepropertiesTaxon: Use icon in place ofdefault_photoif missing
- Add an optional, abbreviated namespace
pyinatas an alias forpyinaturalist - Updated rate limiting with a SQLite-based backend. This adds persistence for rate limit tracking across multiple threads, processes, and/or application restarts. See pyrate-limiter docs for more details.
- Add a
clear_cache()function for clearing cached API responses - Misc improvements for response pretty-printing
- Removed
pyinaturalist.user_agentglobal variable and API function keyword args, and recommend setting on session object instead - Removed
pyinaturalist.DRY_RUN*global variables, and recommend setting in environment variables instead
- Added new Taxon endpoint:
get_taxa_map_layers() - Added new Message endpoints:
get_messages()get_message_by_id()get_unread_message_count()
The following changes apply to upload(), create_observation(), and update_observation():
- Added support for uploading observation photo & sound files from URLs
- Added support for attaching previously uploaded photos to an observation by photo ID
- Added support for python 3.10
- Fixed
count_only=True/per_page=0to not run full query - Do not error on unrecognized
**kwargs, for cases where the API may accept some additional undocumented parameters - Allow overriding default location for API request cache
- Added new functions for v1 Observation endpoints:
create_observation()update_observation()delete_observation()upload()(uploads both photos and sounds)- These are now preferred over the older v0 endpoints
- Added new functions for v1 Observation field value endpoints:
set_observation_field()(creates and updates observation field values)delete_observation_field()
- Added new function for Observation taxon summary:
get_observation_taxon_summary() - Added new functions for Project observation endpoints:
add_project_observation()delete_project_observation()
- Added a
dry_runargument to all API request functions to dry-run an individual request - Added a
reverseargument to all paginated API request functions to reverse the sort order
- Added new data models:
- ListedTaxon
- TaxonSummary
- UserCounts
- Added a preview version of
iNatClient, a higher-level interface for API requests, which returns model objects instead of JSON. See issues #163 and #217 for details.
- Added API request caching with requests-cache
- Updated rate-limiting not apply to cached requests
- Added custom
ClientSessionclass to configure caching, rate-limiting, retries, and timeouts
- Improved logging output for dry-run mode: now shows formatted
PreparedRequestdetails instead ofrequest()keyword args - Added an
enable_logging()function to optionally show prettier logs withrich - Updated logging to redact all credentials from logged API requests
- Increased default timeout to 10 seconds to accommodate some longer-running queries
- Added
get_interval_ranges()function to help with queries over a series of date/time intervals - Fixed bug with
rule_detailsparam forget_projects_by_id() - Added more tutorial/example notebooks
- Added new function for Posts endpoint:
get_posts() - Fixed broken
response_formatparameter inv0.get_observations()
- Deprecated
pyinaturalist.rest_apimodule (moved topyinaturalist.v0subpackage) - Deprecated
pyinaturalist.node_apimodule (moved topyinaturalist.v1subpackage) - Deprecated
get_geojson_observations()(moved to join other observation conversion tools inpyinaturalist-convert)
- Added new function for Observation sounds endpoint:
upload_sounds() - Added new function for Life list endpoint:
get_observation_taxonomy()
- Added support for passing a
requests.Sessionobject to all API request functions - Added a
photosparametercreate_observation()andupdate_observation()to upload photos - Added a
soundsparametercreate_observation()andupdate_observation()to upload sounds - Renamed
add_photo_to_observation()toupload_photos()- The alias
rest_api.add_photo_to_observation()is still available for backwards-compatibility
- The alias
- Updated
upload_photos()to take accept either a single photo or a list of photos, and return a list of responses - Updated
upload_sounds()to take accept either a single sound or a list of sounds, and return a list of responses - Added alias
observed_onforobserved_on_stringincreate_observation() - Fixed conversion for datetime parameters in
create_observation()andupdate_observation() - Updated all requests to correctly convert
datetimeobjects to strings - Moved API functions into separate modules by API version and resource type.
- All can still be imported via
from pyinaturalist import * - Added aliases for backwards-compatibility, so imports from
pyinaturalist.rest_apiandpyinaturalist.node_apiwill still work
- All can still be imported via
Added data models for all API response types, to support working with typed python objects instead of JSON.
Models:
- Comment
- ControlledTerm
- ControlledTermValue
- Annotation
- Identification
- LifeList
- LifeListTaxon
- Observation
- ObservationField
- ObservationFieldValue
- Photo
- Place
- Project
- ProjectObservation
- ProjectObservationField
- ProjectUser
- SearchResult
- Taxon
- ConservationStatus
- EstablishmentMeans
- TaxonCount
- TaxonCounts
- User
Model features:
- Type conversions
- Lazy initialization
- Basic formatters
- Table formatters
- Consolidated response formatting into a single
pprint()function (instead of one per resource type) - Refactored and reorganized the following internal utility modules (see API docs for details):
convertersdocsformattersrequest_params
- Added a default response timeout of 5 seconds
- Added an example (
examples/sample_responses.py) containing response JSON, model objects, and tables of every type to experiment with - Added a tutorial notebook (
examples/Tutorial.ipynb) - Set up pyinaturalist-notebook to be runnable with Binder
- The following methods are now deprecated. They are still functional, but will raise a
DeprecationWarning, and will be removed in a future release:node_api.get_all_observations()node_api.get_all_observation_species_counts()rest_api.get_all_observation_fields()
- Removed
minifyoption fromget_taxa_autocomplete
- Added new function for Search endpoint:
search()(combined search for places, projects, taxa, and users) - Added new functions for Identifications endpoints:
get_identifications()andget_identifications_by_id() - Added new functions for Users endpoints:
get_user_by_id()andget_users_autocomplete()
- Added undocumented
ident_user_idparameter toget_observations() - Added
count_only=Trueas an alias forper_page=0(to get only result counts). - Added generic auto-pagination that can apply to any endpoint that supports pagination.
- Added
page='all'as a shortcut for auto-pagination - The above changes apply to all functions that support pagination:
node_api.get_identifications()node_api.get_observations()node_api.get_observation_species_counts()node_api.get_observation_observers()node_api.get_observation_identifiers()node_api.get_places_autocomplete()node_api.get_projects()node_api.get_taxa()rest_api.get_observations()rest_api.get_observation_fields()
- Updated
rest_api.get_observation_fields()to return a dict with'results'for consistency with other endpoints
- Added response formatting functions to
pyinaturalist.formatters - All API functions and formatters can now be imported from the top-level package, e.g.
from pyinaturalist import * - Published pyinaturalist on conda-forge
- Added global rate-limiting to stay within the rates suggested in API Recommended Practices (per second, minute, and day)
- Moved
Dockerfileanddocker-compose.ymlto a separate repo (pyinaturalist-notebook) and published on Docker Hub - Packaging is now handled with Poetry. For users, installation still works the same. For developers, see Contributing Guide for details.
- Add undocumented
ident_user_idparameter toget_observations()
- Dropped support for python 3.5
- Removed request parameters that were deprecated in 0.11
- Added new function for Observation Histogram endpoint:
get_observation_histogram() - Added new function for Observers endpoint:
get_observation_observers() - Added new function for Identifiers endpoint:
get_observation_identifiers() - Added new function for Controlled Terms endpoints:
get_controlled_terms()- Wraps both
GET /controlled_termsand/controlled_terms/for_taxonendpoints
- Wraps both
- Added conversion from date/time strings to timezone-aware python
datetimeobjects. This applies to the following functions:node_api.get_observation()node_api.get_observations()node_api.get_all_observation()node_api.get_projects()node_api.get_projects_by_id()node_api.get_taxa()node_api.get_taxa_by_id()rest_api.get_observation()rest_api.get_observation_fields()rest_api.get_all_observation_fields()
- Added conversion for an additional
locationfield in observation responses
- Added support for providing credentials via environment variables
- Added integration with system keyring for credentials storage
- Added documentation & examples for authentication options
- Added a
Dockerfileanddocker-compose.ymlfor a Jupyter notebook containing pyinaturalist + other relevant packages - Added some more detailed usage examples under
examples/ - Improved performance for large paginated queries
- Fixed bug that dropped request parameter values of
0as if they wereNone
- Dropped support for python 3.4
- Using the
paramspositional argument for the handful of functions that used it will raise aDeprecationWarning, but will otherwise still be functional until0.12 - Using the
search_queryargument forrest_api.get_observation_fields()andrest_api.get_all_observation_fields()will raise aDeprecationWarning, but will otherwise still be functional until0.12
- Added new functions for Node API Places endpoints:
get_places_by_id()get_places_nearby()get_places_autocomplete()
- Added new functions for Node API Projects endpoints:
get_projects()get_projects_by_id()
- Added new function for an additional Node API Observation endpoint:
get_observation_species_counts()get_all_observation_species_counts()
- Added support for simplified observation field syntax (
observation_fields={id: value}) forcreate_observations()andupdate_observation() - Updated
node_api.get_taxa_by_id()to accept multiple IDs - Updated
rest_api.get_observations()with type conversion from strings to floats for response lat/long coordinates. Only applies to JSON response format. - Updated
node_api.get_taxa_autocomplete()with optionalmin_rankandmax_rankparameters, for consistency withget_taxa() - Renamed
search_queryargument toqto be consistent with API request parameters - Renamed
create_observations()tocreate_observation(), as this only supports creating a single observation per call. This is aliased tocreate_observations()for backwards-compatibility, but will raise aDeprecationWarning.
- Added example response data to docs all endpoints
- Added links to official API reference to docs for all endpoints
- Added full API request parameters to all API functions, in the form of keyword arguments with type annotations and docstrings
- Added complete table of iNaturalist API endpoints and endpoints implemented by pyinaturalist
- Added and improved usage examples
- Numerous other documentation improvements
- Made all API function signatures consistent by taking request params as keyword arguments
- Added support for python 3.9
- Added parameter validation for multiple-choice request parameters
- Added new Observation endpoint:
rest_api.get_observations(), with 6 additional observation response formats, including GeoJSON, Darwin Core, and others
- Added
minifyoption tonode_api.get_taxa_autocomplete()
- Added more info & examples to README for taxa endpoints, and other documentation improvements
- Added conversion for all date and datetime parameters to timezone-aware ISO 8601 timestamps
- Added a dry-run mode to mock out API requests for testing
- Set up pre-release builds for latest development version
- Bugfix: proper support for boolean and integer list parameters (Issue #17)
-
Added new functions for Node API Taxa endpoints:
node_api.get_taxa()node_api.get_taxa_autocomplete()node_api.get_taxa_by_id()
- All functions now take an optional
user-agent <https://en.wikipedia.org/wiki/User_agent>_ parameter in order to identify yourself to iNaturalist. If not set,Pyinaturalist/<VERSION>will be used.
rest_api.delete_observation()now raisesObservationNotFoundif the observation doesn't exist- minor dependencies update for security reasons
- New function:
rest_api.delete_observation()
- New function:
node_api.get_observation()
create_observation()now raises exceptions in case of errors.
update_observation()now raises exceptions in case of errors.
- Better infrastructure (type annotations, documentation, ...)
- Dropped support for Python 2.
- New function:
update_observation() rest_api.AuthenticationErroris nowexceptions.AuthenticationError
- First release on PyPI.