iNaturalist API desktop usage without creating an App?

Is it possible to create iNaturalist observations without applying with an app? I would like to create some way of uploading observations created in ESRI FieldMaps.
The idea was to use some Python scripts in order to link vegetation sampling conducted via ESRI apps (FieldMaps/Survey123) with iNaturalist. This could help with the identification of plants that were observed in the field but could not be identified.
Since I am not a programmer, my plan was to use the ArcGIS Python API to draw images, locations, and notes from ArcGIS Online and then use the unofficial iNaturalist Python API to create observations on a desktop computer. However, it appears not only the user account login, but also some authentication token is required… is it even possible to use the iNaturalist API in such a case, or is trying a waste of time?

1 Like

assuming you’re talking about pyinaturalist, @jcook is probably going to be the one who can provide the most information about it.

i’m pretty sure that pyinaturalist can hit both the old deprecated version of the API and the current v1 API, and you should probably use the v1 API. when i read the documentation for pyinaturalist.v1.observations.create_observation, it does seem to indicate that it will attempt to use an access token acquired through an iNat App oAuth flow, and i think the iNat POST /v1/observations can use an access token acquired that way, but the v1 endpoint i think is really designed to use a JWT which can be acquired from https://www.inaturalist.org/users/api_token. (you can programmatically get the JWT by first getting an oAuth access token and then hitting /users/api_token, but if you’re signed into iNaturalist, you could just manually navigate to /users/api_token, and then manually copy the JWT from the json response.)

i’m not sure if there’s a way to use pyinaturalist.v1.observations.create_observation with a JWT. i don’t see it in the documentation, but you might be able to find something about that the in the GitHub repo, or maybe jcook will be provide more info.

that said, i’m fairly certain you could create observations by posting directly to the iNat API (without going through pyiNaturalist) using just the JWT (and without having to set up an app and programmatically getting the access token).

but just be aware of the new guidelines regarding machine-generated content. currently, official guidelines indicate that “Writing a script to create observations from a manually curated local folder of your images and metadata on your desktop” is acceptable behavior. but then when i read this forum post, it sort of sounds like they may have intended to limit scripts (although i’m not sure how they could do so except just by banning IPs or user accounts). anyway, that was my long way of saying that you should be careful to follow the new rules – whatever they are – so that you don’t get your account banned.

EDIT: in a later response to this post, i note that part of this post is incorrect. see the reply for more details.

1 Like

Reference info:

If you want auth to be 100% automated, you will need to register an OAuth2 app. Outside of iNaturalist.org, anything else that uses the API to create or modify data is considered an “application,” even if you’re just running some scripts on your own computer.

If you want to go that route, I think on the client side there’s not going to be much practical difference between using the OAuth access_token vs JWT api_token, except that the JWT will need to be refreshed every 24 hours instead of every hour. Depending on how you store your creds, that may or may not make a difference. Someone please comment if there are more differences I might be missing there.

However, if you don’t need it to be 100% automated and are okay with copy-pasting from the browser like pisum suggested, that does give you the advantage of not having to register an OAuth app. pyinaturalist doesn’t currently use JWT by default because it hasn’t been needed yet, but that could certainly be added. You can request that in a new issue here, if you’d like.

You can use the existing access_token parameter, since both JWT and OAuth2 tokens get sent the same way (via the Authorization request header):

from pyinaturalist import create_observation

create_observation(
    ...,  # Your observation details
    access_token='<your api_key here>',
)

Or if you don’t need pyinaturalist’s features, you could also do it with plain requests:

import requests

requests.post(
    'https://api.inaturalist.org/v1/observations',
    json={'observation': {...}},  # Your observation details
    headers={'Authorization': 'Bearer <your api_key here>'},
)
2 Likes

@jcook – not exactly related, but do you know if there’s a way to use the API to attach an existing iNat photo record to an observation. for example, if i want to duplicate an observation without re-uploading / creating new photo records, or if i want to move photos from one observation to another (to merge observations or correct a mistaken attachment to the wrong observation). i dug around a bit, but i’m not seeing anything that would handle this kind of thing… maybe it’s there though, and i’m just looking in the wrong place.

That sounds familiar… I’m fairly sure there’s a way to do this by referencing photo IDs, but I don’t remember the details off the top of my head. I’ll look into that some more soon and get back to you.

1 Like

I’ve always used access-tokens rather than api-keys, but I have never known one to expire after only an hour. My assumption was that both lasted for 24 hours, but I just did some testing with an old access-token from a backup, and it still hasn’t expired since it was created in May 2020! (The Authentication docs show example JSON responses from OAuth requests with an expires_in field - but that field is missing in the current responses, and there’s now only a created_at field).

Other than the expiry timestamp, the only other material difference is that observations created with an access-key will have a link to the app added under the copyright notice. However, if an api-token is created via an OAuth authenticated request, it will also behave in the same way, because the app-id is incorporated in the payload of the token. A decoded iNat JWT looks like this:

header: {'alg': 'HS512'}
payload: {
  'user_id': 1234567, # iNat user id
  'oauth_application_id': 123, # iNat app id
  'exp': 1643231611 # expiry (unix timestamp)
}
signature: b'\x8a...\xd2'

So it may be that the intention now is to store the access-token indefinitely and then use it to create api-tokens as needed. The docs don’t really make that clear, though.

i didn’t try to verify, but i seem to vaguely remember that when i looked at the access token expiration way back in the day, it seemed to expire only once a new token was generated for the user / app (or i presume if the user revokes authorization for the app)… something like that.

No, currently, all access tokens remain valid no matter how they are subsequently used. I know this, because my app stores the access token in a config file and automatically fetches a new one after 24 hours. I have a few old config files stored in backups, and the access tokens contained within them are all still valid. (I have obviously used my app to upload thousands of observations using different access tokens during the intervening years).

The JSON response from an OAuth request currently looks like this:

{
  "access_token": "xxx",
  "token_type": "Bearer",
  "scope": "write login",
  "created_at": 1642971184
}

whereas the docs currently show example output like this:

{
  "access_token": "xxx",
  "token_type": "bearer",
  "expires_in": null,
  "refresh_token": null,
  "scope": "write"
}

So the implementation has clearly changed since those docs were written. It may well be that access tokens did expire in the past, but they certainly don’t do so any more.

The question is whether the current behaviour is intentional or not. Either way, the docs clearly need to be updated.

hmmm… it’s probably not a great thing for there to be a lot of unexpired tokens out there. i think if they’re not expiring one way or another, making the docs accurate to reflect the situation is probably a lesser priority than making sure tokens do expire.

It could be a long time before the implementation is actually fixed/changed, though. The docs can easily be updated right now, so that should be the immediate priority.

The main problem with the docs is that they don’t give sufficient prominence to the API Reccommended Practices page. The only link I can find for it is buried half-way down the Developers Page. It really should be the first thing you see when opening the API docs. Once found, the Authentication section does provide some reasonable hints, but it should ideally offer much firmer guidance on the correct procedure to use for both the old and new APIs. As it is, it’s only really possible to fully work this out by trial and error.

i thought this was true, but i had never actually tried it (or maybe i hadn’t tried it recently). i tried it today, and when i tried to use a JWT obtained this way, i got status 401 errors (“You need to sign in or sign up before continuing.”), which i think means that this JWT can only be used along with an iNat session cookie, which i think means it can only be used within the iNat website or the API page Swagger UI. so i guess that loophole that i thought existed doesn’t actually exist. (you still might be able to use some sort of RPA tool to leverage the Swagger UI to run commands dynamically though.)

however, i was able to obtain a JWT using other means, and that one worked just fine for running commands from my own environment. so it’s still not strictly necessary to create your own app in order to run commands, as far as i can tell.

UPDATE: i guess this is incorrect. see posts below.

I can’t reproduce this. For me, both kinds of JWT have always worked the same way. I re-tested this just now with a copy-pasted JWT, and was able to upload a new observation without any errors. Maybe you just experienced a temporary glitch?

1 Like

oh strange. i guess it must have been a glitch because i just tried again, and it worked. go figure.

so i guess if you can copy and paste from the /user/api_token page, you could use a little bit of RPA to automate that process, and that could be an alternative way to fully automate token acquisition + script execution.

This topic was automatically closed 60 days after the last reply. New replies are no longer allowed.