worrmsThe World Register of Marine Species (WoRMS) is the authoritative global checklist of all known marine species. It is maintained by a network of taxonomic experts, hosted by VLIZ and provides:
Website: https://www.marinespecies.org/
worrms?worrms is the official R client for the WoRMS REST API,
developed by rOpenSci. It wraps
every public API endpoint into tidy, tibble-returning R functions.
Key design principles:
wm_ (WoRMS marker)_ accept
vectorised inputs (multiple IDs / names in one
call)tibble's or lists of
tibble'sworrmsworrms with the GBIF occurrence data (via
rgbif)# From CRAN
install.packages("worrms")
# Or the development version from GitHub (requires remotes)
# remotes::install_github("ropensci/worrms")
# Companion packages used in this tutorial
install.packages(c("tidyverse", "rgbif", "sf", "rnaturalearth",
"rnaturalearthdata", "DT", "knitr", "kableExtra"))library(worrms) # WoRMS API client
library(tidyverse) # Data wrangling + ggplot2
library(rgbif) # GBIF occurrence data
library(sf) # Spatial operations
library(rnaturalearth) # World basemap
library(rnaturalearthdata)
library(knitr) # Tables in Rmd
library(kableExtra) # Styled tablesAPI note:
worrmscalls the WoRMS REST API over HTTPS — no authentication key required. Be mindful of rate limits: avoid looping thousands of single calls; use the vectorised_variants instead.
Every taxon in WoRMS has a unique integer identifier called the AphiaID. This is the primary key used across all functions. The first thing you usually do is convert a scientific name to an AphiaID.
Tip: Always work with accepted AphiaIDs.
worrmswill tell you if a name is a synonym and what the valid name / ID is.
wm_name2id() — Scientific name → AphiaIDThe simplest entry point: give a name, get the ID.
#> [1] 236217
# Vectorised version: multiple names at once (note the trailing _)
antarctic_names <- c(
"Euphausia superba", # Antarctic krill
"Dissostichus eleginoides", # Patagonian toothfish
"Aptenodytes forsteri", # Emperor penguin (marine but also terrestrial)
"Notothenia coriiceps", # Black rockcod
"Balaenoptera musculus", # Blue whale
"Limacina helicina" # Pteropod sea butterfly
)
aphia_ids <- wm_name2id_(antarctic_names)
aphia_ids # Returns a named list#> $`Euphausia superba`
#> [1] 236217
#>
#> $`Dissostichus eleginoides`
#> [1] 234700
#>
#> $`Aptenodytes forsteri`
#> [1] 225773
#>
#> $`Notothenia coriiceps`
#> [1] 234679
#>
#> $`Balaenoptera musculus`
#> [1] -999
#>
#> $`Limacina helicina`
#> [1] 140223
wm_id2name() — AphiaID → Scientific nameReverse lookup: useful when you have a list of IDs from another database.
#> [1] "Euphausia superba"
# Vectorised version: look up IDs dynamically so they never go stale
id_vec <- unlist(wm_name2id_(c("Euphausia superba",
"Dissostichus eleginoides",
"Notothenia coriiceps")))
wm_id2name_(id_vec)#> $`236217`
#> [1] "Euphausia superba"
#>
#> $`234700`
#> [1] "Dissostichus eleginoides"
#>
#> $`234679`
#> [1] "Notothenia coriiceps"
wm_record() — Complete AphiaRecord for a single IDThis is the workhorse function. Returns ~25 fields per taxon.
krill_record <- wm_record(236217) # Euphausia superba AphiaID
# Show all columns
glimpse(krill_record)#> Rows: 1
#> Columns: 28
#> $ AphiaID <int> 236217
#> $ url <chr> "https://www.marinespecies.org/aphia.php?p=taxdeta…
#> $ scientificname <chr> "Euphausia superba"
#> $ authority <chr> "Dana, 1850"
#> $ status <chr> "accepted"
#> $ unacceptreason <lgl> NA
#> $ taxonRankID <int> 220
#> $ rank <chr> "Species"
#> $ valid_AphiaID <int> 236217
#> $ valid_name <chr> "Euphausia superba"
#> $ valid_authority <chr> "Dana, 1850"
#> $ parentNameUsageID <int> 110673
#> $ originalNameUsageID <int> 236217
#> $ kingdom <chr> "Animalia"
#> $ phylum <chr> "Arthropoda"
#> $ class <chr> "Malacostraca"
#> $ order <chr> "Euphausiacea"
#> $ family <chr> "Euphausiidae"
#> $ genus <chr> "Euphausia"
#> $ citation <chr> "Siegel, V.; De Grave, S. (Ed) (2026). World Eupha…
#> $ lsid <chr> "urn:lsid:marinespecies.org:taxname:236217"
#> $ isMarine <int> 1
#> $ isBrackish <int> 0
#> $ isFreshwater <int> 0
#> $ isTerrestrial <int> 0
#> $ isExtinct <int> 0
#> $ match_type <chr> "exact"
#> $ modified <chr> "2023-03-25T17:53:52.273Z"
Key fields to know:
| Field | Meaning |
|---|---|
AphiaID |
Unique WoRMS identifier |
scientificname |
Accepted or queried name |
status |
"accepted", "unaccepted" (synonym),
etc. |
valid_AphiaID |
AphiaID of the accepted name (differs from AphiaID if synonym) |
rank |
Taxonomic rank (Species, Genus, …) |
kingdom / phylum / class
… |
Higher taxonomy |
isMarine / isBrackish /
isFreshwater / isTerrestrial |
Habitat flags |
isExtinct |
Extinction status |
citation |
How to cite this record |
lsid |
Life Science Identifier (globally unique URI) |
wm_records_name() — Search by name (fuzzy optional)#> # A tibble: 1 × 4
#> AphiaID scientificname status rank
#> <int> <chr> <chr> <chr>
#> 1 236217 Euphausia superba accepted Species
# Fuzzy matching — great for typos or uncertain spelling
wm_records_name("Euphausia su", fuzzy = TRUE) |>
select(AphiaID, scientificname, status, rank, match_type)#> # A tibble: 1 × 5
#> AphiaID scientificname status rank match_type
#> <int> <chr> <chr> <chr> <chr>
#> 1 236217 Euphausia superba accepted Species like
wm_records_names() — Multiple names at once# Vectorised version for a species list
multi_records <- wm_records_names(antarctic_names)
# Each element of the list is a tibble for one input name
names(multi_records) <- antarctic_names
# Bind them and keep key columns
all_records <- bind_rows(multi_records, .id = "query") |>
select(query, AphiaID, scientificname, status, rank, isMarine, isExtinct)
kbl(all_records, caption = "WoRMS records for selected Antarctic taxa") |>
kable_styling(bootstrap_options = c("striped", "hover", "condensed"), full_width = FALSE)| query | AphiaID | scientificname | status | rank | isMarine | isExtinct |
|---|---|---|---|---|---|---|
| Euphausia superba | 236217 | Euphausia superba | accepted | Species | 1 | 0 |
| Dissostichus eleginoides | 234700 | Dissostichus eleginoides | accepted | Species | 1 | NA |
| Aptenodytes forsteri | 225773 | Aptenodytes forsteri | accepted | Species | 1 | NA |
| Notothenia coriiceps | 234679 | Notothenia coriiceps | accepted | Species | 1 | NA |
| Balaenoptera musculus | 137090 | Balaenoptera musculus | accepted | Species | 1 | NA |
| Balaenoptera musculus | 380449 | Balaenoptera musculus | unaccepted | Species | 1 | NA |
| Limacina helicina | 140223 | Limacina helicina | accepted | Species | 1 | NA |
wm_records_taxamatch() — TAXAMATCH fuzzy matching
algorithmTAXAMATCH is WoRMS’ own name-matching algorithm — more sophisticated than simple fuzzy matching. Ideal for cleaning up species lists from field data.
# Try some slightly messy names
messy_names <- c("Euphausia superba", # correct
"Euphausia supurba", # typo
"Notothenia coriceps") # missing letter
taxamatch_results <- wm_records_taxamatch(messy_names)
bind_rows(taxamatch_results, .id = "query_index") |>
select(query_index, scientificname, status, match_type)#> # A tibble: 3 × 4
#> query_index scientificname status match_type
#> <chr> <chr> <chr> <chr>
#> 1 1 Euphausia superba accepted exact
#> 2 2 Euphausia superba accepted phonetic
#> 3 3 Notothenia coriiceps accepted phonetic
wm_records_common() — Search by vernacular (common)
name# Find species whose common name includes "krill"
krill_results <- wm_records_common("krill", fuzzy = FALSE)
krill_results |> select(AphiaID, scientificname, rank) |> head(10)#> # A tibble: 3 × 3
#> AphiaID scientificname rank
#> <int> <chr> <chr>
#> 1 1128 Euphausiacea Order
#> 2 110671 Euphausiidae Family
#> 3 110690 Meganyctiphanes norvegica Species
wm_records_rank() — Browse by taxonomic rank within a
group# Look up the AphiaID for Euphausiidae dynamically (avoids hardcoded IDs going stale)
euphausiidae_id <- wm_name2id("Euphausiidae")
cat("Euphausiidae AphiaID:", euphausiidae_id, "\n")#> Euphausiidae AphiaID: 110671
# All accepted species in Euphausiidae — rank 220 = Species in WoRMS
euphausiid_spp <- wm_records_rank(220, id = euphausiidae_id) |> distinct(scientificname, AphiaID, .keep_all = TRUE)
nrow(euphausiid_spp)#> [1] 50
#> # A tibble: 10 × 3
#> AphiaID scientificname status
#> <int> <chr> <chr>
#> 1 370561 Boreophausia inermis superseded combination
#> 2 492675 Boreophausia raschii superseded combination
#> 3 1650121 Cyrtopia detruncata nomen dubium
#> 4 492689 Cyrtopia rostrata nomen dubium
#> 5 110682 Euphausia americana accepted
#> 6 478929 Euphausia antarctica junior subjective synonym
#> 7 492650 Euphausia australis junior subjective synonym
#> 8 110683 Euphausia brevis accepted
#> 9 236216 Euphausia crystallorophias accepted
#> 10 221056 Euphausia diomedeae accepted
wm_records_date() — Records modified since a given
dateUseful for tracking updates to the database over time.
# Records added or modified since the start of 2024
recent <- wm_records_date("2024-01-01T00:00:00+00:00")
nrow(recent)#> [1] 50
#> # A tibble: 8 × 4
#> AphiaID scientificname rank modified
#> <int> <chr> <chr> <chr>
#> 1 156233 Mancikellia Genus 2024-01-01T00:22:00.073Z
#> 2 737782 Mysella (Rochefortia) molinae Species 2024-01-01T00:22:00.073Z
#> 3 1672914 Mioerycina Genus 2024-01-01T00:22:00.073Z
#> 4 1726216 Mysella (Rochefortia) Subgenus 2024-01-01T00:22:00.073Z
#> 5 1726217 Kellia (Mancikellia) Subgenus 2024-01-01T00:22:00.073Z
#> 6 584883 Bouchetriphora pallida Species 2024-01-01T00:45:50.633Z
#> 7 1606753 Blasicrura (Blasicrura) Subgenus 2024-01-01T00:45:50.633Z
#> 8 758280 Buccinulum (Evarnula) sufflatum Species 2024-01-01T03:02:19.750Z
wm_synonyms() — All synonyms for a taxon# Synonyms of Euphausia superba
syn <- wm_synonyms(236217)
if (nrow(syn) > 0) {
syn |> select(AphiaID, scientificname, status, unacceptreason)
} else {
cat("No synonyms found for this taxon.\n")
}#> # A tibble: 4 × 4
#> AphiaID scientificname status unacceptreason
#> <int> <chr> <chr> <lgl>
#> 1 478929 Euphausia antarctica junior subjective synonym NA
#> 2 492650 Euphausia australis junior subjective synonym NA
#> 3 492649 Euphausia glacialis junior subjective synonym NA
#> 4 478926 Euphausia murrayi junior subjective synonym NA
# Patagonian toothfish
toothfish_id <- wm_name2id("Dissostichus eleginoides")
toothfish_syn <- wm_synonyms(toothfish_id)
toothfish_syn |> select(AphiaID, scientificname, status)#> # A tibble: 3 × 3
#> AphiaID scientificname status
#> <int> <chr> <chr>
#> 1 313430 Dissostichus amissus unaccepted
#> 2 402572 Dissosticus eleginoides unaccepted
#> 3 318991 Macrias amissus unaccepted
wm_common_id() — Common names for an AphiaID#> # A tibble: 4 × 3
#> vernacular language_code language
#> <chr> <chr> <chr>
#> 1 Antarctic krill eng English
#> 2 Antarctisch krill nld Dutch
#> 3 Antarktischer Krill deu German
#> 4 krill antarctique fra French
wm_distribution() — Distribution records by AphiaID#> Rows: 1
#> Columns: 12
#> $ locality <chr> "Antarctic Ocean"
#> $ locationID <chr> "http://marineregions.org/mrgid/1907"
#> $ higherGeography <chr> "Southern Ocean"
#> $ higherGeographyID <chr> "http://marineregions.org/mrgid/1907"
#> $ recordStatus <chr> "valid"
#> $ typeStatus <lgl> NA
#> $ establishmentMeans <lgl> NA
#> $ invasiveness <lgl> NA
#> $ occurrence <lgl> NA
#> $ decimalLatitude <lgl> NA
#> $ decimalLongitude <lgl> NA
#> $ qualityStatus <chr> "checked"
#> # A tibble: 1 × 2
#> locality n
#> <chr> <int>
#> 1 Antarctic Ocean 1
wm_external() — Get an external ID for a taxonWoRMS stores cross-references to many other databases. Common type codes:
| Code | Database |
|---|---|
tsn |
ITIS Taxonomic Serial Number |
bold |
Barcode of Life Database |
eol |
Encyclopedia of Life |
col |
Catalogue of Life |
iucn |
IUCN Red List ID |
fishbase |
Fishbase species key |
# Get the GBIF taxon key for Antarctic krill
wm_external(234700, type = "fishbase") # Patagonian toothfish#> [1] 467
wm_record_by_external() — Go the other way: external ID →
WoRMS record# Find WoRMS record using an ITIS TSN (Integrated Taxonomic Information System)
# TSN 96559 = Euphausia superba in ITIS
wm_record_by_external(96559, type = "tsn")#> $AphiaID
#> [1] 220144
#>
#> $url
#> [1] "https://www.marinespecies.org/aphia.php?p=taxdetails&id=220144"
#>
#> $scientificname
#> [1] "Nematopalaemon tenuipes"
#>
#> $authority
#> [1] "(Henderson, 1893)"
#>
#> $status
#> [1] "accepted"
#>
#> $unacceptreason
#> NULL
#>
#> $taxonRankID
#> [1] 220
#>
#> $rank
#> [1] "Species"
#>
#> $valid_AphiaID
#> [1] 220144
#>
#> $valid_name
#> [1] "Nematopalaemon tenuipes"
#>
#> $valid_authority
#> [1] "(Henderson, 1893)"
#>
#> $parentNameUsageID
#> [1] 205502
#>
#> $originalNameUsageID
#> [1] 210557
#>
#> $kingdom
#> [1] "Animalia"
#>
#> $phylum
#> [1] "Arthropoda"
#>
#> $class
#> [1] "Malacostraca"
#>
#> $order
#> [1] "Decapoda"
#>
#> $family
#> [1] "Palaemonidae"
#>
#> $genus
#> [1] "Nematopalaemon"
#>
#> $citation
#> [1] "DecaNet eds. (2026). DecaNet. Nematopalaemon tenuipes (Henderson, 1893). Accessed through: World Register of Marine Species at: https://www.marinespecies.org/aphia.php?p=taxdetails&id=220144 on 2026-03-20"
#>
#> $lsid
#> [1] "urn:lsid:marinespecies.org:taxname:220144"
#>
#> $isMarine
#> [1] 1
#>
#> $isBrackish
#> [1] 1
#>
#> $isFreshwater
#> [1] 0
#>
#> $isTerrestrial
#> [1] 0
#>
#> $isExtinct
#> [1] 0
#>
#> $match_type
#> [1] "exact"
#>
#> $modified
#> [1] "2024-01-12T15:47:12.097Z"
wm_sources() — Literature sources for a taxon#> # A tibble: 5 × 3
#> source_id reference page
#> <int> <chr> <lgl>
#> 1 5192 "Boltovskoy D. (Ed.). (1999). South Atlantic Zooplankton 2… NA
#> 2 10060 "Mauchline, J. and Fisher, L.R. (1969) The Biology of Euphaus… NA
#> 3 10063 "Mackintosh, N. A. (1973). \"Distribution of post-larval kril… NA
#> 4 10066 "Baker AdeC, Boden BP, Brinton E (1990) A Practical Guide to … NA
#> 5 10067 "Brinton E, Ohman MD, Townsend AW, Knight MD, Bridgeman AL (2… NA
Attributes are the most powerful and underused part of WoRMS. They encode functional traits like body size, diet, habitat, IUCN status, etc.
wm_attr_def() — Attribute type definitions#> # A tibble: 1 × 4
#> measurementTypeID measurementType CategoryID children
#> <int> <chr> <int> <list>
#> 1 1 IUCN Red List Category 1 <df [2 × 4]>
#> # A tibble: 1 × 4
#> measurementTypeID measurementType CategoryID children
#> <int> <chr> <lgl> <list>
#> 1 15 Body size NA <df [8 × 4]>
wm_attr_category() — Attribute category values# What values exist for functional group (CategoryID = 7)?
func_groups <- wm_attr_category(id = 7)
func_groups |> select(measurementValueID, measurementValue)#> # A tibble: 8 × 2
#> measurementValueID measurementValue
#> <int> <chr>
#> 1 183 benthos
#> 2 184 plankton
#> 3 194 nekton
#> 4 323 neuston
#> 5 621 pleuston
#> 6 620 benthopelagic
#> 7 378 edaphofauna
#> 8 331 not applicable
wm_attr_data() — Trait data for a specific taxon#> Rows: 2
#> Columns: 10
#> $ AphiaID <int> 236217, 236217
#> $ measurementTypeID <int> 23, 23
#> $ measurementType <chr> "Species importance to society", "Species importance…
#> $ measurementValue <chr> "FAO-ASFIS: Species for Fishery Statistics Purposes"…
#> $ source_id <int> 197354, 127093
#> $ reference <chr> "FAO Fishery Fact Sheets Collections: Aquatic Scienc…
#> $ qualitystatus <chr> "unreviewed", "unreviewed"
#> $ AphiaID_Inherited <int> 236217, 236217
#> $ CategoryID <int> 13, 13
#> $ children <list> [<data.frame[1 x 10]>], [<data.frame[1 x 10]>]
# Vectorised: get traits for several taxa at once
ids_to_check <- unlist(wm_name2id_(c("Euphausia superba",
"Dissostichus eleginoides",
"Notothenia coriiceps")))
attr_list <- wm_attr_data_(ids_to_check)
# Bind and summarise
attr_all <- bind_rows(attr_list)
attr_all |>
select(AphiaID, measurementType, measurementValue) |>
head(20)#> # A tibble: 10 × 3
#> AphiaID measurementType measurementValue
#> <int> <chr> <chr>
#> 1 236217 Species importance to society FAO-ASFIS: Species …
#> 2 236217 Species importance to society IUCN Red List
#> 3 234700 Species importance to society FAO-ASFIS: Species …
#> 4 234700 Body size 215
#> 5 234700 Body size 70
#> 6 234700 Species exhibits underwater soniferous behaviour Does not or is unli…
#> 7 234679 Species importance to society FAO-ASFIS: Species …
#> 8 234679 Body size 62
#> 9 234679 Body size 50
#> 10 234679 Species exhibits underwater soniferous behaviour Does not or is unli…
wm_attr_aphia() — All taxa that have a given attribute# Get AphiaIDs of taxa that have any attribute from category 1 (IUCN Red List)
# (returns up to 50 per page — the API is paginated)
taxa_with_fg <- wm_attr_aphia(id = 1)
head(taxa_with_fg, 10)#> # A tibble: 10 × 2
#> AphiaID Attributes
#> <int> <list>
#> 1 100801 <df [1 × 10]>
#> 2 100807 <df [1 × 10]>
#> 3 100822 <df [1 × 10]>
#> 4 100823 <df [1 × 10]>
#> 5 100830 <df [1 × 10]>
#> 6 100831 <df [1 × 10]>
#> 7 100870 <df [1 × 10]>
#> 8 100888 <df [1 × 10]>
#> 9 100906 <df [1 × 10]>
#> 10 100985 <df [1 × 10]>
Here we bring everything together. We will:
# AphiaID for suborder Notothenioidei
notothenioidei_id <- wm_name2id("Notothenioidei")
notothenioidei_id#> [1] 151728
# Classification to confirm position
wm_classification(notothenioidei_id) |> select(rank, scientificname)#> # A tibble: 10 × 2
#> rank scientificname
#> <chr> <chr>
#> 1 Kingdom Animalia
#> 2 Phylum Chordata
#> 3 Subphylum Vertebrata
#> 4 Infraphylum Gnathostomata
#> 5 Parvphylum Osteichthyes
#> 6 Gigaclass Actinopterygii
#> 7 Superclass Actinopteri
#> 8 Class Teleostei
#> 9 Order Perciformes
#> 10 Suborder Notothenioidei
# Direct children (families) — look up ID dynamically
notothenioidei_id <- wm_name2id("Notothenioidei")
notothenioidei_families <- tryCatch(
wm_children(notothenioidei_id),
error = function(e) {
message("wm_children() returned no content or errored: ", conditionMessage(e))
tibble()
}
)
notothenioidei_families |> select(AphiaID, scientificname, rank)#> # A tibble: 9 × 3
#> AphiaID scientificname rank
#> <int> <chr> <chr>
#> 1 151398 Artedidraconidae Family
#> 2 151405 Bathydraconidae Family
#> 3 151403 Bovichtidae Family
#> 4 234518 Channichthyidae Family
#> 5 1517588 Eleginopidae Family
#> 6 267021 Eleginopsidae Family
#> 7 151397 Harpagiferidae Family
#> 8 151404 Nototheniidae Family
#> 9 267029 Pseudaphritidae Family
# For each family, get all species (rank 220)
# We use wm_records_rank_ (vectorised) where possible
family_ids <- notothenioidei_families |>
filter(rank == "Family") |>
pull(AphiaID)
# Retrieve species per family — wrap in purrr for safe iteration
species_list <- map(family_ids, function(fid) {
tryCatch(
wm_records_rank(220, id = fid),
error = function(e) NULL
)
})
# Name the list by family
names(species_list) <- notothenioidei_families |>
filter(rank == "Family") |>
pull(scientificname)
# Bind into one data frame
notothenioidei_spp <- bind_rows(species_list, .id = "family") |>
filter(status == "accepted")
cat("Total accepted Notothenioidei species in WoRMS:", nrow(notothenioidei_spp), "\n")#> Total accepted Notothenioidei species in WoRMS: 124
notothenioidei_spp |>
count(family, name = "n_species") |>
arrange(desc(n_species)) |>
kbl(caption = "Accepted species per Notothenioidei family") |>
kable_styling(bootstrap_options = c("striped", "condensed"), full_width = FALSE)| family | n_species |
|---|---|
| Artedidraconidae | 37 |
| Channichthyidae | 25 |
| Bathydraconidae | 18 |
| Nototheniidae | 18 |
| Harpagiferidae | 12 |
| Bovichtidae | 11 |
| Pseudaphritidae | 2 |
| Eleginopidae | 1 |
# Get accepted scientific names to pass to GBIF
target_species <- notothenioidei_spp |>
# slice_head(n = 20) |> # limit to 20 spp for demo (remove slice_head for full run)
pull(scientificname)
# Download occurrences from GBIF via occ_search()
gbif_results <- map(target_species, function(sp) {
tryCatch(
occ_search(
scientificName = sp,
decimalLatitude = "-90,-60", # south of 60°S
hasCoordinate = TRUE,
limit = 200, # limit to 200 records per species for demo
fields = c("name", "decimalLatitude", "decimalLongitude",
"year", "datasetName", "species")
)$data,
error = function(e) NULL
)
})
names(gbif_results) <- target_species
gbif_df <- bind_rows(gbif_results) |>
filter(!is.na(decimalLatitude), !is.na(decimalLongitude))
cat("Total GBIF occurrence records retrieved:", nrow(gbif_df), "\n")#> Total GBIF occurrence records retrieved: 7224
# Get WoRMS AphiaIDs for the same species names
worms_lookup <- notothenioidei_spp |>
filter(scientificname %in% target_species) |>
select(scientificname, AphiaID, family, isMarine, isExtinct, citation)
# Join
gbif_enriched <- gbif_df |>
rename(scientificname = species) |>
left_join(worms_lookup, by = "scientificname")
glimpse(gbif_enriched)#> Rows: 7,224
#> Columns: 10
#> $ scientificname <chr> "Artedidraco glareobarbatus", "Artedidraco glareobarb…
#> $ decimalLatitude <dbl> -76.02880, -76.02880, -76.04286, -61.75290, -62.46050…
#> $ decimalLongitude <dbl> 168.4050, 168.4050, 168.3702, -52.0085, -56.0943, -53…
#> $ year <int> 1998, 1998, NA, 2008, 2004, 2004, 2003, 2002, 1995, 1…
#> $ datasetName <chr> "NMNH Extant Biology", "NMNH Extant Biology", NA, NA,…
#> $ AphiaID <int> 279404, 279404, 279404, 234734, 234734, 234734, 23473…
#> $ family <chr> "Artedidraconidae", "Artedidraconidae", "Artedidracon…
#> $ isMarine <int> 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,…
#> $ isExtinct <int> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, N…
#> $ citation <chr> "Froese, R. and D. Pauly. Editors. (2026). FishBase. …
# Get AphiaIDs
focal_ids <- worms_lookup$AphiaID
# Fetch trait data (may take a moment)
trait_list <- map(focal_ids, function(aid) {
tryCatch(wm_attr_data(aid), error = function(e) NULL)
})
names(trait_list) <- focal_ids
trait_df <- bind_rows(trait_list, .id = "AphiaID") |>
mutate(AphiaID = as.integer(AphiaID)) |>
filter(measurementType %in% c("Functional group", "IUCN Red List Category",
"Body size", "Diet"))
# Pivot wider for easy use
trait_wide <- trait_df |>
select(AphiaID, measurementType, measurementValue) |>
distinct() |>
pivot_wider(names_from = measurementType, values_from = measurementValue,
values_fn = \(x) paste(unique(x), collapse = "; "))
# Final enriched species table
species_enriched <- worms_lookup |>
left_join(trait_wide, by = "AphiaID")
kbl(species_enriched |> select(scientificname,`Body size`),
caption = "Notothenioidei species with WoRMS Body Size") |>
kable_styling(bootstrap_options = c("striped", "hover", "condensed"), full_width = FALSE)| scientificname | Body size |
|---|---|
| Artedidraco glareobarbatus | NA |
| Artedidraco longibarbatus | NA |
| Artedidraco mirus | 12.5 |
| Artedidraco orianae | 15.1 |
| Artedidraco shackletoni | 14.6 |
| Dolloidraco longedorsalis | 13.7 |
| Histiodraco velifer | 19.2 |
| Neodraco lonnbergi | 11 |
| Neodraco skottsbergi | 10 |
| Pogonophryne albipinna | 3.75 |
| Pogonophryne barsukovi | 25 |
| Pogonophryne bellingshausenensis | 23.4 |
| Pogonophryne brevibarbata | 26.7; 26.2 |
| Pogonophryne cerebropogon | 25 |
| Pogonophryne dewitti | 6.7 |
| Pogonophryne eakini | 19.4; 17.7 |
| Pogonophryne favosa | NA |
| Pogonophryne fusca | 13.8 |
| Pogonophryne immaculata | 25 |
| Pogonophryne lanceobarbata | 25 |
| Pogonophryne macropogon | 34 |
| Pogonophryne maculiventrata | NA |
| Pogonophryne marmorata | 21 |
| Pogonophryne mentella | NA |
| Pogonophryne neyelovi | 35.5; 35 |
| Pogonophryne orangiensis | NA |
| Pogonophryne pallida | NA |
| Pogonophryne pavlovi | NA |
| Pogonophryne permitini | 21 |
| Pogonophryne platypogon | 8.05 |
| Pogonophryne sarmentifera | NA |
| Pogonophryne scotti | 31 |
| Pogonophryne skorai | NA |
| Pogonophryne squamibarbata | 14.7 |
| Pogonophryne stewarti | 24.8 |
| Pogonophryne tronio | 23.4; 26 |
| Pogonophryne ventrimaculata | 26 |
| Acanthodraco dewitti | NA |
| Akarotaxis nudiceps | 13 |
| Bathydraco antarcticus | 24 |
| Bathydraco joannae | 20 |
| Bathydraco macrolepis | 25 |
| Bathydraco marri | 23 |
| Bathydraco scotiae | 17 |
| Chaenichthys rhinoceratus | NA |
| Cygnodraco mawsoni | 41 |
| Gerlachea australis | 24 |
| Gymnodraco acuticeps | 34 |
| Parachaenichthys charcoti | 42 |
| Parachaenichthys georgianus | 59 |
| Prionodraco evansii | 15 |
| Psilodraco breviceps | 20 |
| Racovitzia glacialis | 24 |
| Racovitzia harrissoni | NA |
| Vomeridens infuscipinnis | 22 |
| Bovichtus angustifrons | 28 |
| Bovichtus argentinus | NA |
| Bovichtus chilensis | 9.4 |
| Bovichtus diacanthus | NA |
| Bovichtus oculus | NA |
| Bovichtus psychrolutes | NA |
| Bovichtus variegatus | NA |
| Bovichtus veneris | NA |
| Cottoperca gobio | 80; 33.89 |
| Cottoperca trigloides | NA |
| Halaphritis platycephala | 16.82 |
| Chaenocephalus aceratus | 72; 50 |
| Chaenodraco wilsoni | 43; 30 |
| Champsocephalus esox | 35 |
| Champsocephalus gunnari | 66; 35 |
| Channichthys aelitae | 33.4 |
| Channichthys bospori | NA |
| Channichthys irinae | 25.9 |
| Channichthys mithridatis | 37; 43.7 |
| Channichthys panticapaei | 40.2; 36.1 |
| Channichthys rhinoceratus | 60; 40 |
| Channichthys richardsoni | 37.4 |
| Channichthys rugosus | 3 |
| Channichthys velifer | 49 |
| Chionobathyscus dewitti | 60 |
| Chionodraco hamatus | 49 |
| Chionodraco myersi | 38 |
| Chionodraco rastrospinosus | 52; 30 |
| Cryodraco antarcticus | 39.3 |
| Cryodraco pappenheimi | NA |
| Dacodraco hunteri | 29 |
| Neopagetopsis ionah | NA |
| Pagetodes atkinsoni | 29.3 |
| Pagetopsis macropterus | 33 |
| Pagetopsis maculata | 25 |
| Pseudochaenichthys georgianus | 60; 50 |
| Eleginops maclovinus | 90 |
| Harpagifer andriashevi | NA |
| Harpagifer antarcticus | 9.5 |
| Harpagifer bispinis | 7 |
| Harpagifer crozetensis | 9.2 |
| Harpagifer georgianus | 7 |
| Harpagifer kerguelensis | 8.2 |
| Harpagifer macquariensis | NA |
| Harpagifer marionensis | NA |
| Harpagifer nybelini | NA |
| Harpagifer palliolatus | 9.3 |
| Harpagifer permitini | 8.05 |
| Harpagifer spinosus | 8 |
| Aethotaxis mitopteryx | 42; 18.5 |
| Cryothenia amphitreta | NA |
| Cryothenia peninsulae | NA |
| Dissostichus eleginoides | 215; 70 |
| Dissostichus mawsoni | 200; 127.3 |
| Gobionotothen acuta | NA |
| Gobionotothen angustifrons | 20.5 |
| Gobionotothen barsukovi | NA |
| Gobionotothen gibberifrons | 55; 40 |
| Gobionotothen marionensis | NA |
| Gvozdarus balushkini | NA |
| Gvozdarus svetovidovi | 100 |
| Lepidonotothen squamifrons | 35 |
| Lindbergichthys mizops | 15 |
| Lindbergichthys nudifrons | 19.5 |
| Notothenia angustata | 41 |
| Notothenia coriiceps | 62; 50 |
| Notothenia cyanobrancha | 30; 20 |
| Pseudaphritis undulatus | NA |
| Pseudaphritis urvillii | 36; 17 |
world <- ne_countries(scale = "medium", returnclass = "sf")
gbif_sf <- st_as_sf(gbif_enriched,
coords = c("decimalLongitude", "decimalLatitude"),
crs = 4326)
ggplot() +
geom_sf(data = st_transform(world, 3031),
fill = "grey80", colour = "white", linewidth = 0.15) +
geom_sf(data = st_transform(gbif_sf, 3031),
aes(colour = family),
alpha = 0.7, size = 1.8) +
coord_sf(crs = 3031,
xlim = c(-4000000, 4000000),
ylim = c(-4000000, 4000000)) +
scale_colour_brewer(palette = "Set3", name = "Family") +
labs(title = "Notothenioidei occurrence records (GBIF, south of 60S)",
subtitle = paste0(nrow(gbif_enriched), " records | ",
n_distinct(gbif_enriched$scientificname), " species"),
x = NULL, y = NULL) +
theme_minimal(base_size = 12) +
theme(legend.position = "bottom",
plot.title = element_text(face = "bold"))GBIF occurrences of Notothenioidei south of 60°S
notothenioidei_spp |>
count(family, name = "n_species") |>
mutate(family = fct_reorder(family, n_species)) |>
ggplot(aes(x = n_species, y = family, fill = n_species)) +
geom_col(show.legend = FALSE) +
scale_fill_viridis_c(option = "mako", direction = -1) +
labs(title = "Species richness across Notothenioidei families (WoRMS)",
subtitle = "Accepted species only",
x = "Number of accepted species", y = NULL) +
theme_minimal(base_size = 13) +
theme(plot.title = element_text(face = "bold"))Species richness per Notothenioidei family
gbif_enriched |>
filter(!is.na(year)) |>
mutate(decade = (year %/% 10) * 10) |>
count(decade, family) |>
ggplot(aes(x = decade, y = n, fill = family)) +
geom_col() +
scale_fill_brewer(palette = "Set3", name = "Family") +
labs(title = "GBIF sampling effort through time — Notothenioidei",
x = "Decade", y = "Number of occurrence records") +
theme_minimal(base_size = 13) +
theme(legend.position = "bottom",
plot.title = element_text(face = "bold"))GBIF records per decade for focal Notothenioidei
| Function | Purpose | Vectorised version |
|---|---|---|
| wm_name2id() | Scientific name → AphiaID | wm_name2id_() |
| wm_id2name() | AphiaID → scientific name | wm_id2name_() |
| wm_record() | Full AphiaRecord for one ID | wm_record_() |
| wm_records_name() | Search by name (fuzzy optional) | — |
| wm_records_names() | Multiple names → list of records | — |
| wm_records_taxamatch() | TAXAMATCH fuzzy matching | — |
| wm_records_common() | Search by vernacular name | wm_records_common_() |
| wm_records_rank() | All taxa of a given rank under a parent | — |
| wm_records_date() | Records modified since a date | — |
| wm_classification() | Full taxonomic classification | wm_classification_() |
| wm_children() | Direct taxonomic children | wm_children_() |
| wm_synonyms() | All synonyms / unaccepted names | wm_synonyms_() |
| wm_common_id() | Vernacular names for an AphiaID | wm_common_id_() |
| wm_distribution() | Geographic distribution records | wm_distribution_() |
| wm_external() | External database ID (GBIF, IUCN, …) | wm_external_() |
| wm_record_by_external() | External ID → WoRMS record | wm_record_by_external_() |
| wm_sources() | Literature sources / references | wm_sources_() |
| wm_attr_def() | Attribute type definitions | wm_attr_def_() |
| wm_attr_category() | Attribute category values | wm_attr_category_() |
| wm_attr_data() | Trait attribute data for a taxon | wm_attr_data_() |
| wm_attr_aphia() | AphiaIDs with a given attribute | wm_attr_aphia_() |
| wm_ranks_id() | Rank name from rank ID | — |
| wm_ranks_name() | Rank ID from rank name | — |
1. Always use vectorised _ variants for multiple
taxa
# Slow — one API call per species
results <- map(my_ids, wm_record)
# Fast — one API call for up to 50 IDs
results <- wm_record_(my_ids)2. Handle NULL / empty results
gracefully
The WoRMS API returns HTTP 204 (No Content) when nothing matches.
worrms converts these to NULL or empty
tibbles. Always use tryCatch() or
purrr::possibly() in loops.
3. Check status before using
records
Some names are unaccepted synonyms. Always filter or redirect to
valid_AphiaID.
records |> filter(status == "accepted")
# or
records |> mutate(use_id = if_else(status == "accepted", AphiaID, valid_AphiaID))4. Cite WoRMS in your work
Use wm_sources() to get the exact reference for each
taxon, or cite the database itself:
WoRMS Editorial Board (2024). World Register of Marine Species. Available from https://www.marinespecies.org at VLIZ. Accessed 2026-03-20. DOI: 10.14284/170
#> R version 4.4.1 (2024-06-14 ucrt)
#> Platform: x86_64-w64-mingw32/x64
#> Running under: Windows 11 x64 (build 26200)
#>
#> Matrix products: default
#>
#>
#> locale:
#> [1] LC_COLLATE=English_Belgium.utf8 LC_CTYPE=English_Belgium.utf8
#> [3] LC_MONETARY=English_Belgium.utf8 LC_NUMERIC=C
#> [5] LC_TIME=English_Belgium.utf8
#>
#> time zone: Europe/Brussels
#> tzcode source: internal
#>
#> attached base packages:
#> [1] stats graphics grDevices utils datasets methods base
#>
#> other attached packages:
#> [1] kableExtra_1.4.0 knitr_1.51 rnaturalearthdata_1.0.0
#> [4] rnaturalearth_1.1.0 sf_1.0-24 rgbif_3.8.2
#> [7] lubridate_1.9.4 forcats_1.0.0 stringr_1.6.0
#> [10] dplyr_1.1.4 purrr_1.0.4 readr_2.1.5
#> [13] tidyr_1.3.2 tibble_3.3.0 ggplot2_3.5.2
#> [16] tidyverse_2.0.0 worrms_0.4.3
#>
#> loaded via a namespace (and not attached):
#> [1] gtable_0.3.6 xfun_0.56 bslib_0.10.0 tzdb_0.5.0
#> [5] vctrs_0.6.5 tools_4.4.1 generics_0.1.4 curl_6.4.0
#> [9] proxy_0.4-29 pkgconfig_2.0.3 KernSmooth_2.23-24 data.table_1.17.8
#> [13] RColorBrewer_1.1-3 lifecycle_1.0.5 compiler_4.4.1 farver_2.1.2
#> [17] textshaping_1.0.1 htmltools_0.5.8.1 class_7.3-22 sass_0.4.10
#> [21] yaml_2.3.12 lazyeval_0.2.2 pillar_1.11.1 jquerylib_0.1.4
#> [25] whisker_0.4.1 classInt_0.4-11 cachem_1.1.0 tidyselect_1.2.1
#> [29] digest_0.6.37 stringi_1.8.7 labeling_0.4.3 fastmap_1.2.0
#> [33] grid_4.4.1 cli_3.6.5 magrittr_2.0.3 utf8_1.2.6
#> [37] triebeard_0.4.1 crul_1.5.0 dichromat_2.0-0.1 e1071_1.7-17
#> [41] withr_3.0.2 scales_1.4.0 oai_0.4.0 timechange_0.3.0
#> [45] rmarkdown_2.30 httr_1.4.7 hms_1.1.3 evaluate_1.0.5
#> [49] viridisLite_0.4.3 urltools_1.7.3.1 rlang_1.1.6 Rcpp_1.0.14
#> [53] httpcode_0.3.0 glue_1.8.0 DBI_1.2.3 xml2_1.5.2
#> [57] svglite_2.2.1 rstudioapi_0.17.1 jsonlite_2.0.0 R6_2.6.1
#> [61] plyr_1.8.9 systemfonts_1.2.3 units_1.0-0