Skip to main content

AIREloom: Asynchronous Python client for the OpenAIRE API

AIREloom logo

Samuel Mok · s.mok@utwente.nl · 2025–2026

AIREloom is an async Python client for the OpenAIRE Graph API and Scholexplorer API, built on bibliofabric.

Docs: utsmok.github.io/AIREloom · PyPI: aireloom · License: MIT

Features

  • Full API coverage — Research Products (v2), Projects, Organizations, Data Sources, Persons, Research Product Links (v1), Scholexplorer (v3)
  • Async by design — built on httpx + asyncio with proper connection pooling
  • Typed throughout — Pydantic models for all inputs/outputs, PEP 561 py.typed marker
  • Ergonomics layer — computed properties, SafeStr/SafeList defaults, convenience queries, iterator helpers
  • Flexible auth — auto-detection from env vars, static tokens, OAuth2 client credentials, or no auth
  • Resilient — retries with backoff, rate-limit handling (Retry-After), optional client-side caching, hook system

Installation

uv add aireloom

Or with pip: pip install aireloom. Requires Python ≥3.12.

Quick Start

import asyncio
from aireloom import AireloomSession
from aireloom.endpoints import ResearchProductsFilters

async def main():
    async with AireloomSession() as session:
        # Search publications
        filters = ResearchProductsFilters(
            type="article", mainTitle="climate change",
            fromPublicationDate="2024-01-01",
        )
        response = await session.research_products.search(
            filters=filters, page_size=5, sortBy="publicationDate desc",
        )
        for product in response.results:
            print(f"{product.title} — DOI: {product.doi or 'N/A'}")

        # Iterate all results (cursor-based auto-pagination)
        filters2 = ResearchProductsFilters(type="dataset", countryCode="NL")
        async for product in session.research_products.iterate(
            filters=filters2, page_size=50,
        ):
            print(product.title)
            break  # stop when you want

        # Get a single entity
        product = await session.research_products.get("doi:10.1038/s41586-021-03964-9")
        print(product.title, product.doi)

        # Convenience queries
        citations = await session.queries.citing_works("doi:10.1038/s41586-021-03964-9")
        print(f"{len(citations)} citations")

asyncio.run(main())

No authentication required — the OpenAIRE API works without it. For higher rate limits, see Authentication.

Core API

Retrieval methods

Every resource client (session.research_products, session.organizations, etc.) provides:

Method Description
get(id) Retrieve a single entity by ID
search(filters, page, page_size, sortBy) Paginated search
iterate(filters, page_size, sortBy) Auto-paginate all results (cursor-based)
collect(limit, ...) Collect results into a list
count(filters) Count matching entities
first(filters) Get first matching entity or None

Resource clients

Client API Notes
session.research_products Graph API v2 Enriched responses with related entities
session.projects Graph API v1
session.organizations Graph API v1
session.data_sources Graph API v1
session.persons Graph API v1 givenName/lastName filters cause 500s (upstream bug)
session.scholix Scholix v3 search_links() / iterate_links()

Ergonomics

Computed properties on models — derived fields that don't exist in the raw API response:

product.doi              # → "10.1234/example" (extracted from pids)
product.publication_year  # → 2024 (from publicationDate)
product.is_open_access   # → True (from bestAccessRight)
org.ror_id               # → "https://ror.org/..." (from rorId)
project.funder_name      # → "EC" (from funding array)

SafeStr/SafeListNone is never returned for string/list fields; you get "" or [] instead:

product.subjects  # [] instead of None
product.title     # "" instead of None

Convenience queries via session.queries:

session.queries.publications_by_doi("10.1234/example")
session.queries.citing_works("doi:10.1234/example")
session.queries.datasets_by_organization("openaire____::orgID:grid.5522.e")
session.queries.projects_by_funder("EC")

See Ergonomics docs for the full list.

Filters & Sorting

Filters are Pydantic models — import from aireloom.endpoints:

from aireloom.endpoints import ResearchProductsFilters, ProjectsFilters

filters = ResearchProductsFilters(
    type="article",
    mainTitle="machine learning",
    fromPublicationDate="2024-01-01",
    countryCode="NL",
)

Sort with sortBy="field asc" or sortBy="field desc". Valid fields depend on the endpoint (see ENDPOINT_DEFINITIONS).

Authentication

AIREloom auto-detects auth from environment variables or .env files (prefixed with AIRELOOM_). No auth is the default if nothing is configured.

# Option 1: Static token
AIRELOOM_OPENAIRE_API_TOKEN=your_token

# Option 2: OAuth2 client credentials
AIRELOOM_OPENAIRE_CLIENT_ID=your_id
AIRELOOM_OPENAIRE_CLIENT_SECRET=your_secret

Or pass explicitly:

from bibliofabric.auth import NoAuth, StaticTokenAuth, ClientCredentialsAuth

session = AireloomSession(auth_strategy=StaticTokenAuth(token="..."))

See Authentication docs for details.

Error Handling

from bibliofabric.exceptions import (
    BibliofabricError, APIError, NotFoundError, RateLimitError,
    TimeoutError, NetworkError, AuthError, ValidationError,
)

See Error Handling docs for the full hierarchy.

Examples

All examples in examples/ are dual-purpose — run as scripts or as interactive marimo notebooks:

# As a script
uv run examples/simple_example.py

# As an interactive notebook
uv run marimo edit examples/simple_example.py
Script Description
simple_example.py Search, iterate, get research products
02_scholix_link_discovery.py Discover publication–dataset links via Scholexplorer
03_research_product_analysis.py Deep-dive into product metadata
04_organization_projects.py Organizations and their projects
05_advanced_filtering.py Complex filter combinations
06_persons_discovery.py Person search and co-authorship
07_ergonomics_showcase.py Before/after: raw API vs ergonomics layer
08_iterator_helpers.py collect(), count(), first()
09_computed_fields_and_safe_types.py Computed properties and SafeStr/SafeList
10_convenience_queries.py All convenience query functions

See examples/README.md for marimo embedding and WASM details.

Known OpenAIRE API Issues

Full bug report with reproduction steps: OPENAIRE_BUG_REPORT.md.

  • Persons givenName/lastName filters — cause HTTP 500 despite being in the spec
  • pageSize=100 on Links/Scholix — silently falls back to 10. Use pageSize ≤ 99
  • Undocumented endpoints — Persons has no filtering docs; Links (/v1/researchProducts/links) is entirely undocumented
  • Sort field issues — Persons only accepts relevance; Data Sources error messages say "organizations"

Development

uv sync                  # install dev deps
uv run pytest -x -q      # run tests
uvx ruff check src/      # lint
uvx ty check src/        # type check

Pre-commit hooks for ruff format + lint are configured in .pre-commit-config.yaml.

Contributions welcome — see Contributing.

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

aireloom-0.5.1.tar.gz (336.7 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

aireloom-0.5.1-py3-none-any.whl (56.6 kB view details)

Uploaded Python 3

File details

Details for the file aireloom-0.5.1.tar.gz.

File metadata

  • Download URL: aireloom-0.5.1.tar.gz
  • Upload date:
  • Size: 336.7 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: uv/0.12.10 {"installer":{"name":"uv","version":"0.12.10","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for aireloom-0.5.1.tar.gz
Algorithm Hash digest
SHA256 0e113ceac9053690658846a06ba79bc03cc9e358d0a7d5dfb1c91fd2f399d079
MD5 3b9be0eaafdd7ffb093559c9d93751b3
BLAKE2b-256 333bcf311a3863888d0ea63731b31ac5d43b307d2ab52b20f39862b64036fbbd

See more details on using hashes here.

File details

Details for the file aireloom-0.5.1-py3-none-any.whl.

File metadata

  • Download URL: aireloom-0.5.1-py3-none-any.whl
  • Upload date:
  • Size: 56.6 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: uv/0.12.10 {"installer":{"name":"uv","version":"0.12.10","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for aireloom-0.5.1-py3-none-any.whl
Algorithm Hash digest
SHA256 d269aa4bcdad72696a8b258bc57f540c5efc234865c2086c86638f53bfebaeac
MD5 7f7fc8a26db3a64dbea1b6fca181cbe5
BLAKE2b-256 09d9e71a8d5dfc236ed6efff32722fe7d170dccf27b3d570d7c2b0530daff8ea

See more details on using hashes here.

Release history Release notifications | RSS feed

0.5.2

2 files

This release

0.5.1 This release

2 files

0.5.0

2 files

0.4.0

2 files

0.3.0

2 files

0.2.1

2 files

0.2.0

2 files

0.1.0

2 files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page