Searching for observations containing a specific taxon ID

I would like to search for all observations that contain a certain taxon ID in the observation’s timeline. Specifically, the ID I’m looking for is: Sessile-flowered Trilliums (Subgenus Sessilia). I tried &ident_taxon_id but that evidently does not work since the output contains observations that do not contain the desired ID. For example, the first observation in the search output does not contain the ID.

I’ve used &ident_taxon_id before on species-level taxa with good results. Apparently I don’t understand the nuances of using this parameter.

Does anyone know how to do this?

ident_taxon_ID will contain the specified taxon plus all of its children. The example observation you posted is a species nested within sessile-flowered trilliums, so that’s why it shows up. I’m not sure if there’s a way to specify only an exact taxon ID without including child taxa.

In the filters, for rank select “subgenus” for both high and low and you should get all observations of subgenus Sessilia

i’m fairly certain you’d have to search for identifications rather than observations. this can be done through the API:

here’s a page that can help you visualize this a little more easily:


Thanks but that doesn’t work since the third observation in the search output does not contain the desired identification.

For my purposes, the Observation Taxon is irrelevant. I am only interested in the identifications in the observation’s timeline.

Does this list contain duplicate observations? I suspect so since there are so many records listed.

I’m afraid I need a list of observations (not identifications) so I can pipe the output of that list into the Identify tool. I need to review every observation with the desired identification in its timeline since an unknown number of errors have been introduced as a consequence of a recent taxon swap.

there could be multiple identifications for an observation.

it looks like the API is returning a little more than 8000 records. at 200 records per_page, you could get all the identifications you need with 40 or so pages of results. from there, it should be relatively easy to get a list of distinct observation ids. when you have a distinct list, you can look up the list of observations from the identify page by observation id. for example:,research,casual&id=134481593,110117640,110172429.

i don’t know how you could achieve what you’re describing otherwise without getting staff assistance.

I would like to know where to go to learn to search with taxon IDs! Where are these taxon IDs? Most of all I would like to filter for moths, that is, select Lepidoptera and omit butterflies both in Explore searching and in Identify.
–Sorry this is off track from what you are requesting. Clearly the people here know the answer to my question…

Yes, I think I understand the workflow you’re proposing. It might work if I can retrieve a list of identifications with inactive taxon_id 1024476. Using the API docs you provided, I came up with the following query:


which returns 8819 results. Of these, I only need to review the observations with disagreeing IDs (since these are the only IDs affected by the taxon swap). Is there a way to constraint the results to disagreeing IDs only?

add &disagreement=true (which you narrow thing down to less than 700 records). if i’m understanding correctly what you’re trying to do, you may also want to add &observation_hrank=species (both of these would narrow things down to less than 450 records).

see this and the rest of the source thread:

Whoa, that just saved me hours of extra work! Thanks @pisum!

Hmm, I’m not sure why. The Observation Taxon could be anything, I think.

1 Like

Oh, now I see why. Subsequent ID activity may have negated the effect of the taxon swap, in which case no further action is necessary. Thanks again, @pisum!

1 Like

There is a section here on how to find ID numbers for taxa and various other items in iNaturalist:

Thanks. Also, I found the tutorial wiki.

Many hours later, here is a brief summary. Using a tool developed by @pisum:

I reviewed 430 observations and reissued 305 disagreeing IDs. The current result of the above query includes observations for which a disagreeing ID was not reissued (since the error had already been corrected).

Lesson Learned. Be careful what you ask for. A minor name change may be unnecessarily disruptive. Consider your options.

i saw in your other thread that you noted:

in case it wasn’t entirely clear, i wanted to point out that in my earlier post, i described how to view the observations in the Identify screen. just for example, here’s how you would view those remaining 126 records in the Identify screen, after you you got the list of obs ids formatted as a comma-separated string:,research,casual&id=129030929,114926421,114556515,113804338,114079012,112466043,71985501,111707148,111745933,111586356,111592779,111548388,110634493,110346566,110223612,110085660,110049073,109996071,109834130,109754927,109497718,109338427,109171380,109076551,108993733,108975984,108941381,108396923,108337638,108111068,108107511,108109284,107687837,106819778,24191040,44642603,102845866,93852215,96609256,87742733,75067575,85914552,85346552,78273439,41376159,75459968,75314192,74970316,74930299,74627115,74191708,74172120,74168336,74080035,74075352,73534778,73336925,73339791,73106209,73162419,72846920,72796869,72705020,72608705,72503435,72368804,72298008,71706583,72265397,72209764,72209810,72199956,72165429,72165454,72115207,41242486,71706583,71493145,3461272,70273420,70272780,69790822,69983437,69882101,69630129,66303492,54678155,53776764,51539511,47179032,47374274,47374196,41346903,40748828,44475225,44483249,44466819,44466392,43947857,43485656,43051453,24856599,42265447,42119542,41941789,41814508,41800720,41773732,41648255,41585033,41502542,41299652,41186341,41057759,41056724,41048440,41013113,40791977,40573063,40213910,40055013,39980970,39580064,38687923,38236616,21284866

if you have additional questions about this process, feel free to ask.

I considered this but figured it wasn’t worth the effort. Can you briefly describe how you produced the comma-separated string from the API results?

i think the process would vary slightly depending on what kind of tools you have access to.

since i have Windows and Microsoft Office, the general process i would have used to handle this, given ~450 records (3 pages of results) from, would have been:

  1. open the above URL in your favorite browser
  2. copy the table section of the page without the headers
  3. open up Excel
  4. click the first cell in the first blank row. paste special (text only).
  5. go back to your browser and click the button on the page to get the next page of results.
  6. repeat 2-5 above, as needed.
  7. select and copy the column that represents the observation id
  8. open up Word
  9. in a blank document, paste special (text only).
  10. find all / replace (ctrl+H) : find all instances of ^p (return) and replace each with , (comma)
  11. eliminate the last comma in the resulting comma-separated list of ids

if there were a lot more records (many more pages of results), then i probably would have used Power Automate Desktop (included with Windows 11) to automate steps 1-6 above. you can copy the script below and paste it into your own new flow to do the same. (this particular script uses the Edge browser and outputs to Excel, but you could easily change it to use the Chrome or Firefox browser, and then output to a CSV.)

# This flow contains the basic structure to extract data from most /stirfry/iNatAPIv1_xxx.html pages and then open the data in an Excel spreadsheet. In an ideal world, a data extraction flow would just need 2 steps -- one to open the browser, and another to extract the data, handle pagination, and export to Excel. However, the /stirfry pages load a basic skeleton first and then add data based on the response from an API request. This delay between initial load and API response can cause issues issues for the the standard data extraction step, since there's no mechanism to force it to wait for the API request to complete. So this flow's structure allows for such a wait, in part, by handling pagination and data export separately from the data extraction action.
SET urlBase TO $''''''
SET pageFirst TO 1
SET pageLast TO 50
SET perPage TO 200
SET delayBeforeExtract TO 1
SET delayMaxRetry TO 20
# dataExtracted is a data table variable that will store the combined results extracted from each page. It is initialized first with column header labels. These labels need to be set to match the fields defined in the main data extraction step.
SET dataExtracted TO { ^['Obs ID', 'Obs URL', 'Obs Grade', 'Obs User Login', 'Obs Observed', 'Obs Submitted', 'Obs Taxon Name', 'Obs Taxon Common Name', 'ID #', 'ID Taxon Name', 'ID Taxon Common Name', 'ID User Login', 'ID Datetime', 'ID Category'] }
Variables.CreateNewList List=> pagesNotExtracted
WebAutomation.LaunchEdge.LaunchEdge Url: $'''%urlBase%&per_page=%perPage%&page=%pageFirst%''' WindowState: WebAutomation.BrowserWindowState.Normal ClearCache: False ClearCookies: False Timeout: 60 BrowserInstance=> Browser
LOOP pageCurr FROM pageFirst TO pageLast STEP 1
    LOOP LoopIndex FROM 1 TO delayMaxRetry STEP 1
        WAIT delayBeforeExtract
        # When the page gets a response from the API, it will add a paragraph <p> to the body of the page which displays either error messages returned from the API or some summary information about the data returned. So if this <p> is found, data extraction can begin. Otherwise, wait again before retrying (up to the maximum number of retries).
        IF (WebAutomation.IfWebPageContains.WebPageContainsElement BrowserInstance: Browser Control: appmask['WebPage']['pInfo']) THEN
            # This is the main data extraction step. Note that it is not set up to handle pagination, since pagination is handled by the rest of the flow.
            WebAutomation.ExtractData.ExtractTable BrowserInstance: Browser Control: $'''html > body > table > tbody > tr''' ExtractionParameters: {[$'''td:eq(1) > a''', $'''Own Text''', $'''%''%''', $'''Value #1'''], [$'''td:eq(1) > a''', $'''Href''', $'''%''%''', $'''Value #2'''], [$'''td:eq(3)''', $'''Own Text''', $'''%''%''', $'''Value #3'''], [$'''td:eq(5)''', $'''Own Text''', $'''%''%''', $'''Value #4'''], [$'''td:eq(6)''', $'''Own Text''', $'''%''%''', $'''Value #5'''], [$'''td:eq(7)''', $'''Own Text''', $'''%''%''', $'''Value #6'''], [$'''td:eq(9)''', $'''Own Text''', $'''%''%''', $'''Value #7'''], [$'''td:eq(10)''', $'''Own Text''', $'''%''%''', $'''Value #8'''], [$'''td:eq(11)''', $'''Own Text''', $'''%''%''', $'''Value #9'''], [$'''td:eq(13)''', $'''Own Text''', $'''%''%''', $'''Value #10'''], [$'''td:eq(14)''', $'''Own Text''', $'''%''%''', $'''Value #11'''], [$'''td:eq(16)''', $'''Own Text''', $'''%''%''', $'''Value #12'''], [$'''td:eq(17)''', $'''Own Text''', $'''%''%''', $'''Value #13'''], [$'''td:eq(18)''', $'''Own Text''', $'''%''%''', $'''Value #14'''] } PostProcessData: True ExtractedData=> DataFromWebPage
            IF DataFromWebPage.RowsCount = 0 THEN
                Variables.AddItemToList Item: pageCurr List: pagesNotExtracted
                LOOP FOREACH CurrentItem IN DataFromWebPage
                    SET dataExtracted TO dataExtracted + CurrentItem
            EXIT LOOP
        ELSE IF LoopIndex = delayMaxRetry THEN
            Variables.AddItemToList Item: pageCurr List: pagesNotExtracted
    IF (WebAutomation.IfWebPageContains.WebPageDoesNotContainElement BrowserInstance: Browser Control: appmask['WebPage']['nextPage']) THEN
        EXIT LOOP
    WebAutomation.Click.Click BrowserInstance: Browser Control: appmask['WebPage']['nextPage']
IF dataExtracted.RowsCount > 0 THEN
    Excel.LaunchExcel.LaunchUnderExistingProcess Visible: True Instance=> ExcelInstance
    Excel.WriteToExcel.WriteCell Instance: ExcelInstance Value: dataExtracted.ColumnHeadersRow Column: $'''A''' Row: 1
    Excel.WriteToExcel.WriteCell Instance: ExcelInstance Value: dataExtracted Column: $'''A''' Row: 2
IF pagesNotExtracted.Count > 0 THEN
    Text.JoinText.JoinWithCustomDelimiter List: pagesNotExtracted CustomDelimiter: $''', ''' Result=> pagesNotExtracted_CommaSeparated
    Display.ShowMessageDialog.ShowMessage Title: $'''Extraction Issues''' Message: $'''No data extracted from these page numbers: %pagesNotExtracted_CommaSeparated%

Check the browser window to see if data exists or error messages were returned from the API.''' Icon: Display.Icon.ErrorIcon Buttons: Display.Buttons.OK DefaultButton: Display.DefaultButton.Button1 IsTopMost: True

# [ControlRepository][PowerAutomateDesktop]

  "ControlRepositorySymbols": [
      "Name": "appmask",
      "ImportMetadata": {
        "DisplayName": "Computer",
        "ConnectionString": "",
        "Type": "Local"
      "Repository": "{\r\n  \"Screens\": [\r\n    {\r\n      \"Controls\": [\r\n        {\r\n          \"AutomationProtocol\": \"uia3\",\r\n          \"ScreenShot\": null,\r\n          \"ElementTypeName\": \"<p>\",\r\n          \"Name\": \"pInfo\",\r\n          \"SelectorCount\": 1,\r\n          \"Selectors\": [\r\n            {\r\n              \"CustomSelector\": \" > body > p:eq(1)\",\r\n              \"Elements\": [\r\n                {\r\n                  \"Attributes\": [\r\n                    {\r\n                      \"Ignore\": true,\r\n                      \"IsOrdinal\": false,\r\n                      \"Name\": \"Class\",\r\n                      \"Operation\": \"EqualTo\",\r\n                      \"Value\": null\r\n                    },\r\n                    {\r\n                      \"Ignore\": true,\r\n                      \"IsOrdinal\": false,\r\n                      \"Name\": \"Id\",\r\n                      \"Operation\": \"EqualTo\",\r\n                      \"Value\": null\r\n                    },\r\n                    {\r\n                      \"Ignore\": true,\r\n                      \"IsOrdinal\": true,\r\n                      \"Name\": \"Ordinal\",\r\n                      \"Operation\": \"EqualTo\",\r\n                      \"Value\": \"-1\"\r\n                    },\r\n                    {\r\n                      \"Ignore\": true,\r\n                      \"IsOrdinal\": false,\r\n                      \"Name\": \"Title\",\r\n                      \"Operation\": \"EqualTo\",\r\n                      \"Value\": null\r\n                    }\r\n                  ],\r\n                  \"CustomValue\": null,\r\n                  \"Ignore\": false,\r\n                  \"Name\": \"<body>\",\r\n                  \"Tag\": \"body\"\r\n                },\r\n                {\r\n                  \"Attributes\": [\r\n                    {\r\n                      \"Ignore\": true,\r\n                      \"IsOrdinal\": false,\r\n                      \"Name\": \"Class\",\r\n                      \"Operation\": \"EqualTo\",\r\n                      \"Value\": null\r\n                    },\r\n                    {\r\n                      \"Ignore\": true,\r\n                      \"IsOrdinal\": false,\r\n                      \"Name\": \"Id\",\r\n                      \"Operation\": \"EqualTo\",\r\n                      \"Value\": null\r\n                    },\r\n                    {\r\n                      \"Ignore\": false,\r\n                      \"IsOrdinal\": true,\r\n                      \"Name\": \"Ordinal\",\r\n                      \"Operation\": \"EqualTo\",\r\n                      \"Value\": \"1\"\r\n                    },\r\n                    {\r\n                      \"Ignore\": true,\r\n                      \"IsOrdinal\": false,\r\n                      \"Name\": \"Title\",\r\n                      \"Operation\": \"EqualTo\",\r\n                      \"Value\": null\r\n                    }\r\n                  ],\r\n                  \"CustomValue\": null,\r\n                  \"Ignore\": false,\r\n                  \"Name\": \"<p>\",\r\n                  \"Tag\": \"p\"\r\n                }\r\n              ],\r\n              \"Ignore\": false,\r\n              \"IsCustom\": true,\r\n              \"IsWindowsInstance\": false,\r\n              \"Order\": 0,\r\n              \"Name\": \"Selector 1\"\r\n            }\r\n          ],\r\n          \"Tag\": \"p\",\r\n        },\r\n        {\r\n          \"AutomationProtocol\": null,\r\n          \"ScreenShot\": null,\r\n          \"ElementTypeName\": \"a\",\r\n          \"Name\": \"nextPage\",\r\n          \"SelectorCount\": 1,\r\n          \"Selectors\": [\r\n            {\r\n              \"CustomSelector\": \"a[Id=\\\"button_next\\\"]\",\r\n              \"Elements\": [],\r\n              \"Ignore\": false,\r\n              \"IsCustom\": true,\r\n              \"IsWindowsInstance\": false,\r\n              \"Order\": 0,\r\n              \"Name\": \"Selector 1\"\r\n            }\r\n          ],\r\n          \"Tag\": \"a\",\r\n          \"ScreenshotPath\": \"controlRepo-screenshots\\\\\\\\6cafce73-16e7-445e-b8fa-292a7d877708.png\"\r\n        }\r\n      ],\r\n      \"ScreenShot\": null,\r\n      \"ElementTypeName\": \"Web Page\",\r\n      \"Name\": \"WebPage\",\r\n      \"SelectorCount\": 1,\r\n      \"Selectors\": [\r\n        {\r\n          \"CustomSelector\": \":desktop > domcontainer\",\r\n          \"Elements\": [\r\n            {\r\n              \"Attributes\": [],\r\n              \"CustomValue\": \"domcontainer\",\r\n              \"Ignore\": false,\r\n              \"Name\": \"Web Page\",\r\n              \"Tag\": \"domcontainer\"\r\n            }\r\n          ],\r\n          \"Ignore\": false,\r\n          \"IsCustom\": false,\r\n          \"IsWindowsInstance\": false,\r\n          \"Order\": 0,\r\n          \"Name\": \"Selector 1\"\r\n        }\r\n      ],\r\n      \"Tag\": \"domcontainer\",\r\n    }\r\n  ],\r\n  \"Version\": 1\r\n}"
  "ImageRepositorySymbol": {
    "Name": "imgrepo",
    "ImportMetadata": {},
    "Repository": "{\r\n  \"Folders\": [],\r\n  \"Images\": [],\r\n  \"Version\": 1\r\n}"
  "ConnectionReferences": []

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