Skip to content

omnilab/worms

Omnilab WoRMS: a taxon of the World Register of Marine Species by its AphiaID, its name, or the ITIS TSN, NCBI taxon or IUCN id WoRMS knows it by - accepted name and status, classification, synonyms, vernacular names, habitat flags, the citation WoRMS asks for, its ITIS, NCBI and IUCN ids as cross-references - and a search of names, through the WoRMS REST service, no key needed.

Floor
Provider · Omnilab
Version
1.x
Conformance suite
not wired
Verified for real
Its README does not say
Licence
MIT
composer require omnilab/worms:1.x-dev

The World Register of Marine Species' REST service (https://www.marinespecies.org/rest/) for omnisense/omnilab: a taxon by its AphiaID, its name, or its ITIS, NCBI or IUCN id - accepted name, status, classification, synonyms, vernacular names, the citation it asks for -, with its ITIS, NCBI and IUCN ids as cross-references; a search of names. No key.

Installation

composer require omnilab/worms
use Omnilab\Worms\WormsSourceFactory;

$worms = (new WormsSourceFactory($httpClient))->create([
    'mailto' => 'support@glitchr.io',   // optional, in the User-Agent
    'external_ids' => ['tsn', 'ncbi'],  // optional: the cross-references read
]);

With the Symfony bundle, the factory is registered as worms (see the core's docs).

Credentials

None: WoRMS's REST service is free and takes no key. For large extracts WoRMS asks to be contacted for a download rather than queried name by name.

Options

The factory's options, with the defaults it applies (WormsSourceFactory):

Option Type Default Required Effect
classification bool true no entity() reads the lineage (a call)
synonyms bool true no entity() reads the synonyms (a call)
vernaculars bool true no entity() reads the vernacular names (a call)
external_ids list<string> [tsn, ncbi, iucn] no the cross-references entity() reads, a call each: tsn, ncbi, iucn (another is an \InvalidArgumentException at create()); []: none
language ?string eng no the vernacular names kept, by ISO 639-3 code (eng, fra...); null, empty or all: every one
marine_only bool false no names and searches among marine taxa only (marine_only=true on AphiaRecordsByName)
mailto ?string null no a contact address, sent in the User-Agent
user_agent ?string null no the whole User-Agent, in place of the one made from mailto
throttle float 0 no seconds left between two calls: WoRMS publishes no rate (one to three seconds a call)
base_uri string https://www.marinespecies.org/rest/ no the API the source calls

Capabilities

capabilities() answers entity, search, cross_references; an operation it does not answer throws NotSupportedException before any call.

Capability Supported
entity yes an AphiaID (137094, worms:, aphia:, its LSID, its page), an ITIS, NCBI or IUCN Identifier, or an exact scientific name (the accepted usage first); another scheme refused before any call; up to seven calls whole
search yes the names that begin with the text, accepted or not, WoRMS's order, 50 a page at most, no total; the record alone each; a domain other than taxonomy answers an empty page
series no
products no
occurrences no the records of a marine taxon are OBIS's (omnilab/obis)
datasets no
jobs no
open no
structure no
cross_references yes its ITIS TSN, NCBI taxon and IUCN id (external_ids)
forecast no
historical no
area no
station no
cloud_cover no

Which API

WoRMS's REST service, https://www.marinespecies.org/rest/ (JSON; WoRMS answers 204 for nothing). A taxon read whole:

  1. AphiaRecordByAphiaID/{id} (or AphiaRecordByExternalID/{id}?type=tsn|ncbi|iucn, or AphiaRecordsByName/{name}?like=false): the record;
  2. AphiaClassificationByAphiaID/{id}: the lineage (classification);
  3. AphiaSynonymsByAphiaID/{id} (synonyms);
  4. AphiaVernacularsByAphiaID/{id} (vernaculars);
  5. AphiaExternalIDByAphiaID/{id}?type= for each of external_ids (WoRMS takes one type a call; its types are algaebase, bold, dyntaxa, fishbase, iucn, lsid, ncbi, tsn, gisd - only those the core has a scheme for are read).

Calls

Method Reads WoRMS
entity() an AphiaID: 137094, worms:137094, aphia:137094, urn:lsid:marinespecies.org:taxname:137094, its page the record, then 2-5
an Identifier of itis, ncbi.taxon or iucn (itis:180438, iucn:134817215) AphiaRecordByExternalID, then 2-5
an exact scientific name (Delphinus delphis); of several usages the accepted first AphiaRecordsByName/{name}?like=false, then 2-5
search() Query::$text: the names that begin with it, accepted or not, WoRMS's order, page by offset (50 at most); a domain other than taxonomy answers nothing AphiaRecordsByName/{text}?like=true&offset=

An unaccepted name is itself (its status unaccepted, unaccept_reason, its accepted taxon related). Unknown is null (204). An identifier of another scheme (a GBIF key, a Catalogue of Life id, a Wikidata item) is refused before any call. A search's results hold the record alone (no cross-references, synonyms or lineage); its total is not known.

What an entity holds

id (worms:137094), label (its scientific name), domain taxonomy, kind (its rank, lower case), synonyms (its vernacular names in language, then its scientific synonyms), url (its WoRMS page), identifiers (itis, ncbi.taxon, iucn), and:

Key WoRMS Value
taxon_name, taxon_authority, scientific_name scientificname, authority, both text
taxon_rank, taxonomic_status, unaccept_reason rank, status, unacceptreason text
accepted_name valid_name and valid_authority, when another taxon text
kingdom, phylum, class, order, family, genus the record's text
lineage the classification, from Biota text
marine, brackish, freshwater, terrestrial, extinct isMarine... (WoRMS leaves some unset) boolean
common_name the first vernacular name in language text
synonym_count the synonyms' number number
citation the citation WoRMS asks for this taxon text
modified the day the record last changed text
missing what WoRMS failed to give this time (a 500, a time-out), when it did text

related: parent (parentNameUsageID), accepted (valid_AphiaID, for an unaccepted name), synonym (the synonyms' AphiaIDs).

What WoRMS does not give here

  • Not read: the attributes (body size, functional group, Habitats Directive...), the distributions, the sources, the children, the BOLD, AlgaeBase, FishBase, Dyntaxa and GISD ids (no scheme in the core).
  • No GBIF key, no Catalogue of Life id: the Catalogue of Life takes WoRMS as a source, and omnilab/col carries the AphiaID of a taxon it got from WoRMS.

The Collector

  • From WoRMS: collect('worms', '137094', 1, ['itis', 'ncbi', 'iucn']) follows its TSN, its NCBI taxon and its IUCN id.
  • Into WoRMS: an entity carrying itis:180438 or ncbi.taxon:9728 (ITIS, NCBI, GBIF, the Catalogue of Life) reaches WoRMS by AphiaRecordByExternalID; OBIS's records and taxa are keyed worms: already.

Example

<?php

require __DIR__.'/vendor/autoload.php';

use Omnilab\Model\Identifier;
use Omnilab\Model\Scheme;
use Omnilab\Source\Query;
use Omnilab\Worms\WormsSourceFactory;
use Symfony\Component\HttpClient\HttpClient;

$worms = (new WormsSourceFactory(HttpClient::create()))->create(['mailto' => 'you@example.org']);

$dolphin = $worms->entity(Identifier::of(Scheme::ITIS, '180438'));   // by its ITIS TSN
echo $dolphin->label, ' (', $dolphin->id, '): ', $dolphin->value('taxonomic_status'), ', ', $dolphin->value('common_name'), ', marine: ', $dolphin->property('marine'), "\n";
echo '  ITIS ', $dolphin->identifier(Scheme::ITIS), ', NCBI ', $dolphin->identifier(Scheme::NCBI_TAXON), ', IUCN ', $dolphin->identifier(Scheme::IUCN), ', ', $dolphin->value('synonym_count'), " synonyms\n";
echo '  ', $dolphin->value('citation'), "\n";

foreach ($worms->search(new Query('Delphinus', limit: 3)) as $taxon) {
    echo '  ', $taxon->id, '  ', $taxon->label, '  ', $taxon->value('taxonomic_status'), "\n";
}
Delphinus delphis (worms:137094): accepted, common dolphin, marine: yes
  ITIS 180438, NCBI 9728, IUCN 134817215, 24 synonyms
  Fordyce, E.; Perrin, W.F. (2026). World Cetacea Database. Delphinus delphis Linnaeus, 1758. Accessed through: World Register of Marine Species at: https://www.marinespecies.org/aphia.php?p=taxdetails&id=137094 on 2026-10-27
  worms:137015  Delphinus  accepted
  worms:383523  Delphinus (Chaenodelphinus)  accepted
  worms:384361  Delphinus (Chaenodelphinus) edentulus  unaccepted

(as answered on 2026-10-27: eight calls - the record by its TSN, its lineage, synonyms, vernaculars, three external ids, then the search)

# config/packages/omnilab.yaml
omnilab:
    sources:
        worms: { factory: worms, options: { mailto: '%env(OMNILAB_MAILTO)%', external_ids: [tsn, ncbi, iucn], language: eng } }

Errors

Every call goes through HttpSource: a 404 or a 410 (and a 204) is null - an empty page for a list -; a 429 is a RateLimitedException (retryAfter from Retry-After); a 5xx, a time-out or a host that does not answer an UnavailableException; any other 4xx a ProviderException with its status and what the service said; an answer that is not JSON a ProviderException, a web page answered for JSON an UnavailableException.

WoRMS's own: nothing is a 204, read as null (an empty page for a search). A call besides the record that is unavailable (a 5xx, a time-out; not a 4xx) is left out of the entity and named in its missing property rather than thrown; the record's own failure is thrown as above. Before any call: an identifier of another scheme (NotSupportedException).

Verified, and not

Against the real service on 2026-10-15, without a key: the common dolphin read whole (seven calls, 8.5 s: its lineage from Biota, 24 synonyms, its English names, ITIS 180438, NCBI 9728, IUCN 134817215, the World Cetacea Database's citation); an unaccepted name (Delphinus albimanus, 383799); the dolphin by its TSN, its NCBI taxon, its IUCN id, its name and its LSID; unknown ids and names (204); a search for "Delphinus" in two pages of ten. That evening a synonyms call answered 500 after 31 s, then 200 after 16 s, then after 2 s: hence missing. The fixtures in Tests/Fixtures/ are its answers (index.json names each one's address).

Again on 2026-10-27: the common dolphin by its TSN, read whole (eight calls with the search), and the search for "Delphinus" (the example above).

Not verified: a 429 (none met; WoRMS publishes no rate); marine_only live.

Responsibility

This package is an independent, unofficial client: it is not affiliated with, endorsed by or sponsored by WoRMS or VLIZ. WoRMS's content is under CC BY 4.0 and to be cited ("WoRMS Editorial Board (2026). World Register of Marine Species... doi:10.14284/170"), each taxon with its own citation, as the README's Terms of use says. Whoever runs the package is responsible for complying with them and for any use made of what it returns: see the README's Responsibility section.

Propose a change docs/index.md