glitchr / omnischolar
Omnischolar: one contract for a researcher's publications - author profiles, works, books and their editions, citations - read from OpenAlex, HAL, Crossref, ORCID, Open Library, arXiv and INSPIRE, merged and deduplicated, exported as BibTeX, RIS or CSL-JSON; and its Symfony bundle.
Requires
- php: >=8.2
- symfony/http-client-contracts: ^3.0
Requires (Dev)
- phpunit/phpunit: ^11.0
- symfony/config: ^6.4|^7.0|^8.0
- symfony/dependency-injection: ^6.4|^7.0|^8.0
- symfony/http-client: ^6.4|^7.0|^8.0
- symfony/http-kernel: ^6.4|^7.0|^8.0
Suggests
- ext-intl: Better title matching across accents in the Merger
- omnischolar/arxiv: arXiv: preprints
- omnischolar/crossref: Crossref: reference metadata by DOI
- omnischolar/hal: HAL: French open archive, filtered by domain (shs.droit)
- omnischolar/inspire: INSPIRE-HEP: high-energy physics literature and citations
- omnischolar/openalex: OpenAlex: profiles, works, citations, h-index
- omnischolar/openlibrary: Open Library: books, editions, covers
- omnischolar/orcid: ORCID: identity, positions and degrees, declared works
Provides
None
Conflicts
None
Replaces
None
This package is auto-updated.
Last update: 2026-10-03 16:46:46 UTC
README
One contract for a researcher's publications: their profile, their works, their books and the editions of each, their citations - read from the services that know them, merged into one list, exported as BibTeX, RIS or CSL-JSON.
$works = $collector->collect([ 'openalex' => 'A5108007452', // Keitaro Nakatani on OpenAlex 'hal' => 'Keitaro Nakatani', // his deposits in HAL, by the name he signs 'crossref' => '0009-0005-1387-7295', // the works deposited with his ORCID ]); $works[0]->title; // one record per work, each field from the source trusted for it $works[0]->pdfUrl; // the full text deposited in HAL $export->bibtex($works); // @article{chocron2024acid, ...} $books = (new Merger())->merge($openlibrary->works('Monica Neagoy')); $books[0]->editions; // the editions of one book, newest first
This package holds the contract (Source\SourceInterface, Source\SourceFactory, Registry),
the models (Work, Author, Contributor, Identifier, Venue, Metrics, Affiliation), the
Merger, the Collector, the Export and the Symfony bundle. It requires nothing but
symfony/http-client-contracts. Each source is a package of its own:
| Package | Source | Key |
|---|---|---|
omnischolar/openalex |
OpenAlex: profiles and counts (h-index), works, citations, abstracts | none (mailto; a free key raises the daily budget) |
omnischolar/hal |
HAL: French open archive, full texts, filter by domain (shs.droit) |
none |
omnischolar/crossref |
Crossref: reference metadata by DOI, works by ORCID | none (mailto) |
omnischolar/orcid |
ORCID: identity, CV (positions, degrees, distinctions), declared works | none (public API) |
omnischolar/openlibrary |
Open Library: books, one record per edition, covers | none |
omnischolar/arxiv |
arXiv: preprints | none |
omnischolar/inspire |
INSPIRE-HEP: physics literature, citation summary | none |
A source answers what it can - capabilities() says what - and throws NotSupportedException
for the rest. Unknown is null or an empty page; a service down or rate limited is an
UnavailableException (RateLimitedException), which a caller never caches as "nothing".
The Merger takes one record per work: same DOI, then same ISBN (books), arXiv id, HAL id,
then same title in the same year - a different DOI on both sides always keeps two records apart.
Each field comes from the source preferred for it (configurable), identifiers are united, and
the editions of a book are grouped under the newest one.
Documentation
- Installation and first calls
- Sources: what each can and cannot do
- Models and identifiers
- Merging: the rules and the preferences
- Export: BibTeX, RIS, CSL-JSON
- Symfony
- The Docker harness
Symfony
omnischolar: sources: openalex: { factory: openalex, options: { mailto: '%env(OMNISCHOLAR_MAILTO)%' } } hal: { factory: hal } droit: { factory: hal, options: { domains: [shs.droit] } } openlibrary: { factory: openlibrary, options: { mailto: '%env(OMNISCHOLAR_MAILTO)%' } } merger: preferences: { abstract: [hal, openalex] }
public function __construct(SourceInterface $openalex, Collector $collector, Export $export) {}
Docker: every source, for real
cd docker && cp .env.dist .env docker compose run --rm omnischolar sources docker compose run --rm omnischolar works openalex A5108007452 --from 2024 --to 2024 docker compose run --rm omnischolar merge openlibrary:"Monica Neagoy" crossref:"Monica Neagoy" docker compose run --rm omnischolar test
License: LGPL-3.0-or-later.