Aller au contenu

omnilab/ebird

Omnilab eBird: a bird taxon of the eBird/Clements taxonomy by its species code (no key) - scientific and common names in a locale, category, order, family, banding codes - and, with the user's eBird API key, a taxon by name, a search of names, a checklist, and the recent observations around a point or in a region or a region's observations of a day, through the eBird API 2.0.

Étage
Fournisseur · Omnilab
Version
1.x
Suite de conformité
non branchée
Vérifié en réel
Son README ne le dit pas
Licence
MIT
composer require omnilab/ebird:1.x-dev

The eBird API 2.0 (Cornell Lab of Ornithology: https://api.ebird.org/v2/) for omnisense/omnilab: bird taxa by species code without a key; with the user's key, taxa by name, a search, checklists, and the recent observations around a point or in a region as occurrences. Not tested live beyond the taxonomy.

Installation

composer require omnilab/ebird
use Omnilab\Ebird\EbirdSourceFactory;

$ebird = (new EbirdSourceFactory($httpClient))->create([
    'key' => getenv('EBIRD_API_KEY') ?: null,  // the user's eBird API key; none: the taxonomy only
    'locale' => 'en',
]);

With the Symfony bundle, the factory is registered as ebird (see the core's docs); keep the key in an environment variable or the secrets vault, never in the code.

Credentials

The taxonomy (entity() by species code) needs no key. Everything else - a taxon by name, the search, checklists, observations - needs the user's eBird API key: an eBird account gives it at ebird.org/api/keygen, under the eBird API Terms of Use. It is given as key and sent in the X-eBirdApiToken header. Without it those calls are refused before they are made. Keep the key in an environment variable or the secrets vault.

Options

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

Option Type Default Required Effect
key ?string null no the user's eBird API key, sent as X-eBirdApiToken; null or empty: the taxonomy only, every other call refused (InvalidConfigException) before it is made
locale string en no the common names' language (eBird's locales: en, fr, es, de...)
radius_km float 25 no the circle around a Point for occurrences() when the query gives no radius; 50 at most
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: eBird asks for reasonable volumes, no figure
base_uri string https://api.ebird.org/v2/ no the API the source calls

Per call, occurrences() reads the filters OccurrenceQuery::TAXON (a species code), region (EbirdSource::REGION: FR, US-NY, US-NY-109) and provisional (EbirdSource::PROVISIONAL); any other key is refused before any call (Occurrences).

Capabilities

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

Capability Supported
entity yes a species code (amerob, ebird:, its page) without a key; a scientific or common name, or a checklist (S123456789), with the key; another scheme refused before any call
search yes eBird's search of names, one page; with the key only
series no
products no
occurrences yes the recent observations (the latest of each species at each location) around a Point or in a region, up to 30 days back, or a region's single past day; 10,000 at most, one page; with the key only
datasets no
jobs no
open no
structure no
cross_references no none: eBird gives no other base's id (Wikidata carries the species code, P3444)
forecast no
historical no
area no a box is refused before any call (a point and a radius, or a region)
station no
cloud_cover no

Which API

The eBird API 2.0, https://api.ebird.org/v2/ (documentation: documenter.getpostman.com/view/664302/S1ENwy59). The key goes in the X-eBirdApiToken header. On 2026-10-16, without a key: ref/taxonomy/ebird answered (200), ref/taxon/find, data/obs/geo/recent and product/checklist/view answered 403.

Calls

Method Reads eBird Key
entity() a species code: amerob, ebird:amerob, https://ebird.org/species/amerob ref/taxonomy/ebird?fmt=json&species=&locale= no
a scientific or common name, exactly (Turdus migratorius, American Robin) ref/taxon/find?q=&locale=, then the taxonomy yes
a checklist: S123456789, ebird.checklist:S123456789, its page product/checklist/view/{subId} yes
search() Query::$text: eBird's search of names (its site's search box), one page, then one taxonomy call for the page ref/taxon/find, ref/taxonomy/ebird?species=a,b,c yes
occurrences() an OccurrenceQuery (below) data/obs/... yes

An unknown code is null (eBird answers []). A bare string of lower-case letters and digits is a code, S and digits a checklist, anything else a name. An identifier of another scheme is refused before any call.

What a taxon holds

id (ebird:amerob), label (its scientific name), domain taxonomy, kind (its category: species, issf, spuh, slash, hybrid, form...), synonyms (its common name), url, related (parent: the species a subspecies group is reported as), and: taxon_name, common_name (in the locale), category, taxonomic_order, order, family, family_common_name, banding_codes, report_as.

Occurrences

OccurrenceQuery eBird
place: a Point, radius data/obs/geo/recent?lat=&lng=&dist= (km, 50 at most; radius_km, 25, by default); latitude and longitude to two decimals, as eBird asks
place: none, filter region (FR, US-NY, US-NY-109) data/obs/{region}/recent
from back: the days from now to it, 1 to 30 (14 when none); further back refused
to none or today; a period ending before yesterday is refused, but for a region's single day:
from and to on one date, with region data/obs/{region}/historic/{y}/{m}/{d} (that day's observations)
OccurrenceQuery::TAXON a species code or an Identifier of ebird: .../recent/{code}; on a historic day, the page filtered
EbirdSource::PROVISIONAL (provisional) includeProvisional (false by default)
limit maxResults (10,000 at most); one page, no cursor

Refused before any call: a BBox, both or neither of a point and a region, a radius over 50 km, a sort, any other filter. eBird's recent observations are the latest of each species at each location, not every one. An observation holds: id (its checklist, ebird.checklist:S...: eBird gives a line of a checklist no public id of its own, so the observations of one checklist share it - occurrences stay out of the Merger), kind observation, time (its day at midnight UTC: eBird writes the place's local time without an offset), place, url (its checklist), related (taxon), and taxon_name, common_name, observed_at (the local time as eBird wrote it), count, location, location_id, location_private, valid, reviewed.

A checklist holds observed_at, location_id, protocol, duration (minutes), species_count, observers, recorded_by, complete, and its species related (species); its place is not read (eBird gives its location's id; the hotspot's coordinates would be a second call).

Example

<?php

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

use Omnilab\Ebird\EbirdSourceFactory;
use Omnilab\Exception\InvalidConfigException;
use Omnilab\Model\Point;
use Omnilab\Source\OccurrenceQuery;
use Symfony\Component\HttpClient\HttpClient;

$ebird = (new EbirdSourceFactory(HttpClient::create()))->create([
    'key' => getenv('EBIRD_API_KEY') ?: null,   // the user's key; none: the taxonomy only
    'locale' => 'fr',
    'mailto' => 'you@example.org',
]);

$robin = $ebird->entity('eurrob1');   // keyless: the taxonomy
echo $robin->label, ' (', $robin->id, '): ', $robin->value('common_name'), ', ', $robin->value('family'), ', ', $robin->value('category'), "\n";

try {
    $page = $ebird->occurrences(new OccurrenceQuery(new Point(48.5734, 7.7521), new DateTimeImmutable('-7 days'), radius: 10));
    foreach ($page as $observation) {
        echo '  ', $observation->value('observed_at'), ' ', $observation->value('common_name'), ' x', $observation->value('count'), "\n";
    }
} catch (InvalidConfigException $e) {
    echo $e->getMessage(), "\n";
}

Without a key (run on 2026-10-27: one call, the taxonomy; the observations refused before any call):

Erithacus rubecula (ebird:eurrob1): Rougegorge familier, Muscicapidae, species
[ebird] An eBird API key (the "key" option, from ebird.org/api/keygen) is needed to list observations.

With a key the observations of the last week within 10 km of Strasbourg follow: not run here (no key).

# config/packages/omnilab.yaml
omnilab:
    sources:
        ebird: { factory: ebird, options: { key: '%env(default::EBIRD_API_KEY)%', locale: en, mailto: '%env(OMNILAB_MAILTO)%' } }

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.

Before any call: a call that needs the key without one (InvalidConfigException); an identifier of another scheme, a box, both or neither of a point and a region, a radius over 50 km, a sort, a filter not read, a period that ended before yesterday (but a region's single day), more than 30 days back (NotSupportedException); a taxon filter that is no species code (\InvalidArgumentException). eBird's 403 (no key, a wrong one) is a ProviderException; an unknown code is its [], null.

Verified, and not

Against the real service on 2026-10-16, without a key: the American Robin by its code (Turdus migratorius, Turdidae, banding code AMRO); the European Robin in French (Rougegorge familier); two codes in one taxonomy call; an unknown code ([]); the 403 of the calls that need a key.

Not tested live: everything that needs a key - a taxon by its name, the search, the checklists, the observations around a point or in a region, a region's day. Their tests answer responses constructed after eBird's documentation (Tests/Constructed/, whose README says so: the taxa real, the places, counts, times and checklist ids placeholders). A user with a key should check the shape of these answers before relying on them.

On 2026-10-27, without a key: the European Robin in French by its code, and the observations refused before any call (the example above).

Responsibility

This package is an independent, unofficial client: it is not affiliated with, endorsed by or sponsored by the Cornell Lab of Ornithology or eBird. The key is the user's, under the eBird API Terms of Use: a use that is not commercial unless the Lab agrees, eBird and the Cornell Lab credited wherever the data are shown, no redistribution as a dataset, reasonable volumes of calls, 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.

Proposer une modification docs/index.md