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:
AphiaRecordByAphiaID/{id}(orAphiaRecordByExternalID/{id}?type=tsn|ncbi|iucn, orAphiaRecordsByName/{name}?like=false): the record;AphiaClassificationByAphiaID/{id}: the lineage (classification);AphiaSynonymsByAphiaID/{id}(synonyms);AphiaVernacularsByAphiaID/{id}(vernaculars);AphiaExternalIDByAphiaID/{id}?type=for each ofexternal_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/colcarries 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:180438orncbi.taxon:9728(ITIS, NCBI, GBIF, the Catalogue of Life) reaches WoRMS byAphiaRecordByExternalID; OBIS's records and taxa are keyedworms: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.