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 (v3), Projects, Organizations, Data Sources, Persons, Research Product Links, and Scholexplorer (v1/v2/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/SafeList — None 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.

Metadata

Release files for aireloom 0.6.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for aireloom 0.6.0
File Size Uploaded Provenance Actions
aireloom-0.6.0.tar.gz 341.5 kB Without provenance Details for aireloom-0.6.0.tar.gz

Built distribution (wheel)

Table of built distributions (wheels) for aireloom 0.6.0
File Interpreter ABI Platform Actions
aireloom-0.6.0-py3-none-any.whl Python 3 none any Details for aireloom-0.6.0-py3-none-any.whl

Total release size: 400.9 kB

Release files / aireloom-0.6.0.tar.gz

Download URL aireloom-0.6.0.tar.gz
Size 341.5 kB
Tags Source
SHA-256 checksum
How to use checksums
103e912e59f148371f7c39fd79737b2091eeeb6832f147c128ff2e277ede39c6
BLAKE2b-256 checksum
How to use checksums
a60db269a41d09cc959f5594d48abd72dc109004cf100776ab9dfb0e1c23a926
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via uv/0.13.6 {"installer":{"name":"uv","version":"0.13.6","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}

Release files / aireloom-0.6.0-py3-none-any.whl

Download URL aireloom-0.6.0-py3-none-any.whl
Size 59.4 kB
Tags Python 3
SHA-256 checksum
How to use checksums
65b7971c1e14e5d5a29b83905f08a4a4016c8e1a61e57cc18e69d68c20bd7145
BLAKE2b-256 checksum
How to use checksums
ef405d2d0f6f34906c225c97175941a4f1f84a245e2b4b9bf197c61a0602ad1f
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via uv/0.13.6 {"installer":{"name":"uv","version":"0.13.6","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}

Security for aireloom 0.6.0

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

No provenance found

None of the files in this release were uploaded with provenance metadata.

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

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