When I got my species list from GBIF, they came with their biological classifications of Kingdom, Phylum, Class, Order, Family, and Genus. None of those told me whether a Coragyps atratus was a plant, animal, or amoeba. I searched and read about 10 web pages about biological classifications and still could not align those terms with my categories. I asked ChatGPT for help, expecting something like a T-chart. What I got was another round of Apps Script. And by another round, I mean another ten rounds of tweaking code that ChatGPT swore was the final solution.
Nevertheless, I could hug ChatGPT for saving me from biology madness. Here’s the script that worked, except that shellfish became mammals because even ChatGPT didn’t know their classification:
function onOpen() {
const ui = SpreadsheetApp.getUi();
ui.createMenu("Species Tools")
.addItem("Categorize Next Batch", "categorizeSpeciesBatch")
.addToUi();
}
function categorizeSpeciesBatch() {
const sheet = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();
const data = sheet.getDataRange().getValues();
const maxRowsPerRun = 50; // adjust to suit execution time
let rowsProcessed = 0;
for (let i = 1; i < data.length; i++) {
const taxonUrl = data[i][2]; // Column C = iNaturalist taxon URL
const categoryCell = sheet.getRange(i + 1, 7); // Column G = category
if (categoryCell.getValue()) continue; // Skip if already categorized
const match = taxonUrl.match(/taxa\/(\d+)/);
if (!match) continue;
const taxonId = match[1];
try {
const response = UrlFetchApp.fetch(`https://api.inaturalist.org/v1/taxa/${taxonId}`);
const json = JSON.parse(response.getContentText());
const iconic = json.results[0]?.iconic_taxon_name?.toLowerCase() || "";
let category = "";
switch (iconic) {
case "plantae":
category = "Plants";
break;
case "aves":
category = "Birds";
break;
case "reptilia":
case "amphibia":
category = "Reptiles & Amphibians";
break;
case "actinopterygii":
case "chondrichthyes":
category = "Fish";
break;
case "insecta":
case "arachnida":
case "myriapoda":
category = "Bugs";
break;
case "mammalia":
default:
category = "Mammals";
}
categoryCell.setValue(category);
rowsProcessed++;
Utilities.sleep(1000); // pause to respect API rate limits
if (rowsProcessed >= maxRowsPerRun) break;
} catch (e) {
Logger.log(`Error at row ${i + 1}: ${e.message}`);
}
}
SpreadsheetApp.getUi().alert(`Processed ${rowsProcessed} rows. Run again to continue.`);
}
As usual, AI requires human oversight. I brought my species list back to Excel to give it a look-see. I sorted it by every single biological classification in hierarchical order. That lineup really exposed the need for a human touch. For example, only FOUR spiders made the cut when I saved only species that had been reported at least 10 times. How can I see Hunstman spiders all summer long at GTM and they don’t get reported? Meanwhile, I count at least THIRTEEN different species of mosquitos?! Yep, this list is just a starting point.

Back to categorizing these critters. The “Reptiles & Amphibians” title feels like two categories. I changed all occurrences of that title to “Herps”. Also, I could see that some Arthropods are bugs and some Arthropods are fish. Are crabs fish? I’m going with fish, since they’re in FWC’s Fish Rules app. I changed ChatGPT’s default Mammals to Fish for the crabs.

I will continue to tweak my species list. For now, it’s time to load these babies into WordPress.