1 Introduction

1.1 What is WoRMS?

The 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:

  • Validated, accepted scientific names with full synonymy
  • Taxonomic classification (kingdom → species)
  • Trait / attribute data (functional groups, IUCN status, habitat)
  • Geographic distribution
  • Cross-links to external databases (GBIF, BOLD, …)

Website: https://www.marinespecies.org/

1.2 What is 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:

  • Every function name begins with wm_ (WoRMS marker)
  • Functions ending in _ accept vectorised inputs (multiple IDs / names in one call)
  • All results are returned as tibble's or lists of tibble's

1.3 Session goals

  1. Install and load worrms
  2. Walk through every function family with Antarctic examples
  3. Demonstrate a full biodiversity use-case combining worrms with the GBIF occurrence data (via rgbif)

2 Setup

2.1 Installation

# 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"))

2.2 Load packages

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 tables

API note: worrms calls 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.


3 Core concepts: the AphiaID

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. worrms will tell you if a name is a synonym and what the valid name / ID is.


4 Function family 1 — Name ↔︎ ID lookups

4.1 wm_name2id() — Scientific name → AphiaID

The simplest entry point: give a name, get the ID.

# Single species: Antarctic krill
krill_id <- wm_name2id("Euphausia superba")
krill_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

4.2 wm_id2name() — AphiaID → Scientific name

Reverse lookup: useful when you have a list of IDs from another database.

wm_id2name(236217)   # single ID — Euphausia superba
#> [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"

5 Function family 2 — Full taxon records

5.1 wm_record() — Complete AphiaRecord for a single ID

This 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)

5.2 wm_records_name() — Search by name (fuzzy optional)

# Exact match
wm_records_name("Euphausia superba") |> select(AphiaID, scientificname, status, rank)
#> # 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

5.3 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)
WoRMS records for selected Antarctic taxa
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

5.4 wm_records_taxamatch() — TAXAMATCH fuzzy matching algorithm

TAXAMATCH 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

5.5 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

5.6 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
euphausiid_spp |> select(AphiaID, scientificname, status) |> head(10)
#> # 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

5.7 wm_records_date() — Records modified since a given date

Useful 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
recent |> select(AphiaID, scientificname, rank, modified) |> head(8)
#> # 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

6 Function family 3 — Taxonomy tree navigation

6.1 wm_classification() — Full classification path

# Full taxonomy from kingdom to species for Antarctic krill
krill_class <- wm_classification(236217)
krill_class
#> # A tibble: 11 × 3
#>    AphiaID rank       scientificname   
#>      <int> <chr>      <chr>            
#>  1       2 Kingdom    Animalia         
#>  2    1065 Phylum     Arthropoda       
#>  3    1066 Subphylum  Crustacea        
#>  4  845959 Superclass Multicrustacea   
#>  5    1071 Class      Malacostraca     
#>  6    1086 Subclass   Eumalacostraca   
#>  7    1089 Superorder Eucarida         
#>  8    1128 Order      Euphausiacea     
#>  9  110671 Family     Euphausiidae     
#> 10  110673 Genus      Euphausia        
#> 11  236217 Species    Euphausia superba
# Tidy view
krill_class |>
  select(rank, scientificname) |>
  kbl(caption = "Taxonomic classification of *Euphausia superba*") |>
  kable_styling(bootstrap_options = "striped", full_width = FALSE)
Taxonomic classification of Euphausia superba
rank scientificname
Kingdom Animalia
Phylum Arthropoda
Subphylum Crustacea
Superclass Multicrustacea
Class Malacostraca
Subclass Eumalacostraca
Superorder Eucarida
Order Euphausiacea
Family Euphausiidae
Genus Euphausia
Species Euphausia superba

6.2 wm_children() — Direct taxonomic children

# Children of order Euphausiacea (AphiaID = 1128)
# We look this up dynamically first to future-proof the code
euphausiacea_id <- wm_name2id("Euphausiacea")
cat("Euphausiacea AphiaID:", euphausiacea_id, "\n")
#> Euphausiacea AphiaID: 1128
# wm_children() returns HTTP 204 (No Content) — and therefore throws an error —
# when a taxon has no registered children. Always guard with tryCatch().
euphausiacea_children <- wm_children(euphausiacea_id)
euphausiacea_children |> select(AphiaID, scientificname, rank) |> head(15)
#> # A tibble: 3 × 3
#>   AphiaID scientificname    rank  
#>     <int> <chr>             <chr> 
#> 1  110670 Bentheuphausiidae Family
#> 2 1649973 Euphausidae       Family
#> 3  110671 Euphausiidae      Family

6.3 wm_ranks_id() and wm_ranks_name() — Look up taxonomic rank names

Important:The same rank (e.g., Species) exists independently in multiple kingdoms

# What is rank 220?
wm_ranks_id(220)
#> # A tibble: 9 × 4
#>   taxonRankID taxonRank AphiaID kingdom             
#>         <int> <chr>       <int> <chr>               
#> 1         220 Species         2 Animalia            
#> 2         220 Species         3 Plantae             
#> 3         220 Species         4 Fungi               
#> 4         220 Species         5 Protozoa            
#> 5         220 Species         6 Bacteria            
#> 6         220 Species         7 Chromista           
#> 7         220 Species         8 Archaea             
#> 8         220 Species        10 Viruses             
#> 9         220 Species        12 Biota incertae sedis
# What rank ID does "Species" have?
wm_ranks_name("Species")
#> # A tibble: 9 × 4
#>   taxonRankID taxonRank AphiaID kingdom             
#>         <int> <chr>       <int> <chr>               
#> 1         220 Species         2 Animalia            
#> 2         220 Species         3 Plantae             
#> 3         220 Species         4 Fungi               
#> 4         220 Species         5 Protozoa            
#> 5         220 Species         6 Bacteria            
#> 6         220 Species         7 Chromista           
#> 7         220 Species         8 Archaea             
#> 8         220 Species        10 Viruses             
#> 9         220 Species        12 Biota incertae sedis

7 Function family 4 — Synonyms & nomenclature

7.1 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

8 Function family 5 — Vernacular names

8.1 wm_common_id() — Common names for an AphiaID

common <- wm_common_id(236217)   # Euphausia superba
common
#> # 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

9 Function family 6 — Geographic distribution

9.1 wm_distribution() — Distribution records by AphiaID

krill_dist <- wm_distribution(236217)
glimpse(krill_dist)
#> 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"
# How many records per locality type?
krill_dist |> count(locality, sort = TRUE) |> head(15)
#> # A tibble: 1 × 2
#>   locality            n
#>   <chr>           <int>
#> 1 Antarctic Ocean     1

10 Function family 7 — External identifiers

10.1 wm_external() — Get an external ID for a taxon

WoRMS 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

10.2 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"

11 Function family 8 — Sources & citations

11.1 wm_sources() — Literature sources for a taxon

sources <- wm_sources(236217)
sources |> select(source_id, reference, page) |> head(5)
#> # 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

12 Function family 9 — Trait attributes

Attributes are the most powerful and underused part of WoRMS. They encode functional traits like body size, diet, habitat, IUCN status, etc.

12.1 wm_attr_def() — Attribute type definitions

# Type 1 = IUCN Red List Category
wm_attr_def(id = 1)
#> # A tibble: 1 × 4
#>   measurementTypeID measurementType        CategoryID children    
#>               <int> <chr>                       <int> <list>      
#> 1                 1 IUCN Red List Category          1 <df [2 × 4]>
# Type 15 = Body size
wm_attr_def(id = 15)
#> # A tibble: 1 × 4
#>   measurementTypeID measurementType CategoryID children    
#>               <int> <chr>           <lgl>      <list>      
#> 1                15 Body size       NA         <df [8 × 4]>

12.2 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

12.3 wm_attr_data() — Trait data for a specific taxon

# Attributes for Antarctic krill
krill_attrs <- wm_attr_data(236217)
krill_attrs |> glimpse()
#> 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…

12.4 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]>

13 Full use-case — Antarctic fish biodiversity with WoRMS + GBIF

Here we bring everything together. We will:

  1. Identify the taxonomic subtree of Antarctic Notothenioidei (the dominant Antarctic fish group) via WoRMS
  2. Resolve accepted names and AphiaIDs
  3. Retrieve occurrence records from GBIF
  4. Enrich with WoRMS trait data
  5. Visualise species richness and trait composition

13.1 Step 1 — Find the Notothenioidei subtree in WoRMS

# 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)
Accepted species per Notothenioidei family
family n_species
Artedidraconidae 37
Channichthyidae 25
Bathydraconidae 18
Nototheniidae 18
Harpagiferidae 12
Bovichtidae 11
Pseudaphritidae 2
Eleginopidae 1

13.2 Step 2 — Retrieve GBIF occurrences south of 60°S

# 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

13.3 Step 3 — Join WoRMS metadata onto GBIF records

# 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. …

13.4 Step 4 — Retrieve WoRMS trait data and join

# 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)
Notothenioidei species with WoRMS Body Size
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

13.5 Step 5 — Visualise

13.5.1 Occurrence map — south polar projection

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

GBIF occurrences of Notothenioidei south of 60°S

13.5.2 Species richness per family

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

Species richness per Notothenioidei family

13.5.3 Occurrence records per decade

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

GBIF records per decade for focal Notothenioidei


14 Quick-reference cheat sheet

worrms function cheat sheet
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

15 Tips & best practices

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.

safe_record <- possibly(wm_record, otherwise = NULL)
results <- map(my_ids, safe_record)

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


16 Session information

sessionInfo()
#> 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