Python interface for the OpenAIRE Graph API, built on top of the bibliofabric framework.
Project description
AIREloom: Asynchronous Python client for the OpenAIRE API
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+asynciowith proper connection pooling - Typed throughout — Pydantic models for all inputs/outputs, PEP 561
py.typedmarker - 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/lastNamefilters — cause HTTP 500 despite being in the spec pageSize=100on Links/Scholix — silently falls back to 10. UsepageSize ≤ 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.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file aireloom-0.5.0a1.tar.gz.
File metadata
- Download URL: aireloom-0.5.0a1.tar.gz
- Upload date:
- Size: 334.4 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: uv/0.11.30 {"installer":{"name":"uv","version":"0.11.30","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
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
4f7bb8cf69ceb39fdfd3aa7a06d3a049232db7db6ca22d639c384cb72f1fffc7
|
|
| MD5 |
343121cb81acd0eeea2cad0571c5fcc3
|
|
| BLAKE2b-256 |
d05e7eb4539c4a3e632654836ebf66a784df1f86ea5c6eff23f85c5202e67f30
|
File details
Details for the file aireloom-0.5.0a1-py3-none-any.whl.
File metadata
- Download URL: aireloom-0.5.0a1-py3-none-any.whl
- Upload date:
- Size: 56.4 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: uv/0.11.30 {"installer":{"name":"uv","version":"0.11.30","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
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
c3bf5a8cb36d719da3102ccdec8ee7b9443e4efe8bb9ead7fbcf45b8ccf7a156
|
|
| MD5 |
43d5220fc0d478d259f8b451b826ece2
|
|
| BLAKE2b-256 |
10f2dd6ba0e07c9016d91c51270203077f15f85a5f359ead280e32d32eca4521
|