Skip to main content

OpenAlexClient

Struct OpenAlexClient 

Source
pub struct OpenAlexClient { /* private fields */ }
Expand description

Async client for the OpenAlex REST API.

Provides 30 methods covering all OpenAlex endpoints: 10 list, 10 get, 8 autocomplete, and 2 semantic search.

§Creating a client

use papers_openalex::OpenAlexClient;

// Reads API key from OPENALEX_KEY env var (optional but recommended)
let client = OpenAlexClient::new();

// Or pass an explicit API key
let client = OpenAlexClient::with_api_key("your-key-here");

§Example: search and paginate

use papers_openalex::{OpenAlexClient, ListParams};

let client = OpenAlexClient::new();
let params = ListParams::builder()
    .search("machine learning")
    .filter("publication_year:2024,is_oa:true")
    .sort("cited_by_count:desc")
    .per_page(5)
    .build();

let response = client.list_works(&params).await?;
for work in &response.results {
    println!("{}: {} cites",
        work.display_name.as_deref().unwrap_or("?"),
        work.cited_by_count.unwrap_or(0));
}

§Example: cursor pagination

use papers_openalex::{OpenAlexClient, ListParams};

let client = OpenAlexClient::new();
let mut cursor = Some("*".to_string());

while let Some(c) = cursor {
    let params = ListParams {
        cursor: Some(c),
        per_page: Some(200),
        filter: Some("publication_year:2024".into()),
        ..Default::default()
    };
    let response = client.list_works(&params).await?;
    for work in &response.results {
        // process each work
    }
    cursor = response.meta.next_cursor;
}

Implementations§

Source§

impl OpenAlexClient

Source

pub fn new() -> OpenAlexClient

Create a new client, reading the API key from the OPENALEX_KEY environment variable. The key is optional for most endpoints but recommended for higher rate limits.

use papers_openalex::OpenAlexClient;
let client = OpenAlexClient::new();
Source

pub fn with_api_key(api_key: impl Into<String>) -> OpenAlexClient

Create a new client with an explicit API key.

use papers_openalex::OpenAlexClient;
let client = OpenAlexClient::with_api_key("your-key-here");
Source

pub fn with_base_url(self, url: impl Into<String>) -> OpenAlexClient

Override the base URL. Useful for testing with a mock server.

use papers_openalex::OpenAlexClient;
let client = OpenAlexClient::new()
    .with_base_url("http://localhost:8080");
Source

pub fn with_cache(self, cache: DiskCache) -> OpenAlexClient

Enable disk caching of successful responses.

use papers_openalex::{OpenAlexClient, DiskCache};
use std::time::Duration;

let cache = DiskCache::default_location(Duration::from_secs(600)).unwrap();
let client = OpenAlexClient::new().with_cache(cache);
Source

pub async fn list_works( &self, params: &ListParams, ) -> Result<ListResponse<Work>, OpenAlexError>

List scholarly works (articles, books, datasets, preprints). 240M+ records. Supports full-text search across titles, abstracts, and full text. Filter by publication year, OA status, type, citations, author, institution, topic, funder, and 130+ other fields.

GET /works

§Example
use papers_openalex::{OpenAlexClient, ListParams};

let client = OpenAlexClient::new();
let params = ListParams::builder()
    .filter("publication_year:2024,is_oa:true")
    .sort("cited_by_count:desc")
    .per_page(5)
    .build();
let response = client.list_works(&params).await?;
// response.meta.count => total matching works
// response.results    => Vec<Work>
Source

pub async fn list_authors( &self, params: &ListParams, ) -> Result<ListResponse<Author>, OpenAlexError>

List disambiguated author profiles. 110M+ records. Each author has a unified identity across name variants, with linked ORCID, institutional affiliations, publication history, and citation metrics.

GET /authors

§Example
use papers_openalex::{OpenAlexClient, ListParams};

let client = OpenAlexClient::new();
let params = ListParams::builder()
    .search("einstein")
    .per_page(3)
    .build();
let response = client.list_authors(&params).await?;
for author in &response.results {
    println!("{}: {} works",
        author.display_name.as_deref().unwrap_or("?"),
        author.works_count.unwrap_or(0));
}
Source

pub async fn list_sources( &self, params: &ListParams, ) -> Result<ListResponse<Source>, OpenAlexError>

List publishing venues: journals, repositories, conferences, ebook platforms, and book series. Includes ISSN identifiers, OA status, APC pricing, host organization, and impact metrics.

GET /sources

§Example
use papers_openalex::{OpenAlexClient, ListParams};

let client = OpenAlexClient::new();
let params = ListParams::builder()
    .filter("is_oa:true,type:journal")
    .sort("cited_by_count:desc")
    .per_page(5)
    .build();
let response = client.list_sources(&params).await?;
Source

pub async fn list_institutions( &self, params: &ListParams, ) -> Result<ListResponse<Institution>, OpenAlexError>

List research organizations: universities, hospitals, companies, government agencies. Linked to ROR identifiers. Includes geographic location, parent/child relationships, and affiliated repositories.

GET /institutions

§Example
use papers_openalex::{OpenAlexClient, ListParams};

let client = OpenAlexClient::new();
let params = ListParams::builder()
    .filter("country_code:US,type:education")
    .sort("cited_by_count:desc")
    .per_page(10)
    .build();
let response = client.list_institutions(&params).await?;
Source

pub async fn list_topics( &self, params: &ListParams, ) -> Result<ListResponse<Topic>, OpenAlexError>

List research topics organized in a 3-level hierarchy: domain > field > subfield > topic. AI-generated descriptions and keywords. Each work is assigned up to 3 topics with relevance scores.

GET /topics

§Example
use papers_openalex::{OpenAlexClient, ListParams};

let client = OpenAlexClient::new();
let params = ListParams::builder()
    .search("machine learning")
    .per_page(5)
    .build();
let response = client.list_topics(&params).await?;
Source

pub async fn list_publishers( &self, params: &ListParams, ) -> Result<ListResponse<Publisher>, OpenAlexError>

List publishing organizations (e.g. Elsevier, Springer Nature). Includes parent/child hierarchy, country of origin, and linked sources. Some publishers also act as funders or institutions (see roles).

GET /publishers

§Example
use papers_openalex::{OpenAlexClient, ListParams};

let client = OpenAlexClient::new();
let params = ListParams::builder()
    .sort("works_count:desc")
    .per_page(10)
    .build();
let response = client.list_publishers(&params).await?;
Source

pub async fn list_domains( &self, params: &ListParams, ) -> Result<ListResponse<Domain>, OpenAlexError>

List research domains (broadest level of topic hierarchy). 4 domains total: Life Sciences, Social Sciences, Physical Sciences, Health Sciences. Each domain contains multiple academic fields.

GET /domains

§Example
use papers_openalex::{OpenAlexClient, ListParams};

let client = OpenAlexClient::new();
let response = client.list_domains(&ListParams::default()).await?;
for domain in &response.results {
    println!("{}: {} works",
        domain.display_name.as_deref().unwrap_or("?"),
        domain.works_count.unwrap_or(0));
}
Source

pub async fn list_fields( &self, params: &ListParams, ) -> Result<ListResponse<Field>, OpenAlexError>

List academic fields (second level of topic hierarchy). 26 fields total (e.g. Computer Science, Medicine, Mathematics). Each field has a parent domain and contains multiple subfields.

GET /fields

§Example
use papers_openalex::{OpenAlexClient, ListParams};

let client = OpenAlexClient::new();
let response = client.list_fields(&ListParams::default()).await?;
for field in &response.results {
    println!("{}: {} works",
        field.display_name.as_deref().unwrap_or("?"),
        field.works_count.unwrap_or(0));
}
Source

pub async fn list_subfields( &self, params: &ListParams, ) -> Result<ListResponse<Subfield>, OpenAlexError>

List research subfields (third level of topic hierarchy). ~252 subfields total (e.g. Artificial Intelligence, Organic Chemistry). Each subfield has a parent field and domain, and contains multiple topics.

GET /subfields

§Example
use papers_openalex::{OpenAlexClient, ListParams};

let client = OpenAlexClient::new();
let response = client.list_subfields(&ListParams::default()).await?;
for subfield in &response.results {
    println!("{}: {} works",
        subfield.display_name.as_deref().unwrap_or("?"),
        subfield.works_count.unwrap_or(0));
}
Source

pub async fn list_funders( &self, params: &ListParams, ) -> Result<ListResponse<Funder>, OpenAlexError>

List research funding organizations (e.g. NIH, NSF, ERC). Linked to Crossref funder registry. Includes grant counts, funded works, and impact metrics.

GET /funders

§Example
use papers_openalex::{OpenAlexClient, ListParams};

let client = OpenAlexClient::new();
let params = ListParams::builder()
    .filter("country_code:US")
    .sort("works_count:desc")
    .per_page(5)
    .build();
let response = client.list_funders(&params).await?;
Source

pub async fn get_work( &self, id: &str, params: &GetParams, ) -> Result<Work, OpenAlexError>

Get a single scholarly work by ID. Returns full metadata including title, authors, abstract (as inverted index), citations, topics, OA status, locations, funding, and bibliographic data.

GET /works/{id}

Accepts: OpenAlex ID (W...), DOI, PMID (pmid:...), PMCID (pmcid:...), MAG (mag:...).

§Example
use papers_openalex::{OpenAlexClient, GetParams};

let client = OpenAlexClient::new();

// By OpenAlex ID
let work = client.get_work("W2741809807", &GetParams::default()).await?;

// By DOI
let work = client.get_work("https://doi.org/10.7717/peerj.4375", &GetParams::default()).await?;

// With field selection
let params = GetParams::builder().select("id,display_name,cited_by_count").build();
let work = client.get_work("W2741809807", &params).await?;
Source

pub async fn get_author( &self, id: &str, params: &GetParams, ) -> Result<Author, OpenAlexError>

Get a single author profile. Returns disambiguated identity with name variants, institutional affiliations over time, publication/citation counts, h-index, and topic expertise.

GET /authors/{id}

Accepts: OpenAlex ID (A...), ORCID.

§Example
use papers_openalex::{OpenAlexClient, GetParams};

let client = OpenAlexClient::new();
let author = client.get_author("A5023888391", &GetParams::default()).await?;
println!("{}: h-index {}",
    author.display_name.as_deref().unwrap_or("?"),
    author.summary_stats.as_ref().and_then(|s| s.h_index).unwrap_or(0));
Source

pub async fn get_source( &self, id: &str, params: &GetParams, ) -> Result<Source, OpenAlexError>

Get a single publishing venue. Returns ISSNs, OA status, DOAJ membership, APC pricing, host publisher hierarchy, impact metrics, and publication year range.

GET /sources/{id}

Accepts: OpenAlex ID (S...), ISSN.

§Example
use papers_openalex::{OpenAlexClient, GetParams};

let client = OpenAlexClient::new();
let source = client.get_source("S137773608", &GetParams::default()).await?;
println!("{} ({})", source.display_name.as_deref().unwrap_or("?"),
    source.r#type.as_deref().unwrap_or("unknown"));
Source

pub async fn get_institution( &self, id: &str, params: &GetParams, ) -> Result<Institution, OpenAlexError>

Get a single research institution. Returns ROR ID, geographic coordinates, parent/child institution relationships, hosted repositories, and research output metrics.

GET /institutions/{id}

Accepts: OpenAlex ID (I...), ROR.

§Example
use papers_openalex::{OpenAlexClient, GetParams};

let client = OpenAlexClient::new();
let inst = client.get_institution("I136199984", &GetParams::default()).await?;
// Also accepts ROR:
let inst = client.get_institution("https://ror.org/03vek6s52", &GetParams::default()).await?;
Source

pub async fn get_topic( &self, id: &str, params: &GetParams, ) -> Result<Topic, OpenAlexError>

Get a single research topic. Returns AI-generated description, keywords, position in domain > field > subfield hierarchy, sibling topics, and work counts.

GET /topics/{id}

Accepts: OpenAlex ID (T...).

§Example
use papers_openalex::{OpenAlexClient, GetParams};

let client = OpenAlexClient::new();
let topic = client.get_topic("T10001", &GetParams::default()).await?;
println!("{}: {} works",
    topic.display_name.as_deref().unwrap_or("?"),
    topic.works_count.unwrap_or(0));
Source

pub async fn get_publisher( &self, id: &str, params: &GetParams, ) -> Result<Publisher, OpenAlexError>

Get a single publisher. Returns hierarchy level, parent publisher, country codes, linked sources, and citation metrics.

GET /publishers/{id}

Accepts: OpenAlex ID (P...).

§Example
use papers_openalex::{OpenAlexClient, GetParams};

let client = OpenAlexClient::new();
let publisher = client.get_publisher("P4310319965", &GetParams::default()).await?;
println!("{}: {} works",
    publisher.display_name.as_deref().unwrap_or("?"),
    publisher.works_count.unwrap_or(0));
Source

pub async fn get_domain( &self, id: &str, params: &GetParams, ) -> Result<Domain, OpenAlexError>

Get a single research domain. Returns description, child fields, sibling domains, and work/citation counts.

GET /domains/{id}

Accepts: numeric ID (e.g. "3" for Physical Sciences).

§Example
use papers_openalex::{OpenAlexClient, GetParams};

let client = OpenAlexClient::new();
let domain = client.get_domain("3", &GetParams::default()).await?;
println!("{}: {} fields",
    domain.display_name.as_deref().unwrap_or("?"),
    domain.fields.as_ref().map(|f| f.len()).unwrap_or(0));
Source

pub async fn get_field( &self, id: &str, params: &GetParams, ) -> Result<Field, OpenAlexError>

Get a single academic field. Returns parent domain, child subfields, sibling fields, and work/citation counts.

GET /fields/{id}

Accepts: numeric ID (e.g. "17" for Computer Science).

§Example
use papers_openalex::{OpenAlexClient, GetParams};

let client = OpenAlexClient::new();
let field = client.get_field("17", &GetParams::default()).await?;
println!("{}: {} subfields",
    field.display_name.as_deref().unwrap_or("?"),
    field.subfields.as_ref().map(|s| s.len()).unwrap_or(0));
Source

pub async fn get_subfield( &self, id: &str, params: &GetParams, ) -> Result<Subfield, OpenAlexError>

Get a single research subfield. Returns parent field and domain, child topics, sibling subfields, and work/citation counts.

GET /subfields/{id}

Accepts: numeric ID (e.g. "1702" for Artificial Intelligence).

§Example
use papers_openalex::{OpenAlexClient, GetParams};

let client = OpenAlexClient::new();
let subfield = client.get_subfield("1702", &GetParams::default()).await?;
println!("{}: {} topics",
    subfield.display_name.as_deref().unwrap_or("?"),
    subfield.topics.as_ref().map(|t| t.len()).unwrap_or(0));
Source

pub async fn get_funder( &self, id: &str, params: &GetParams, ) -> Result<Funder, OpenAlexError>

Get a single funder. Returns Wikidata description, Crossref funder ID, grant/award counts, country, and research impact metrics.

GET /funders/{id}

Accepts: OpenAlex ID (F...).

§Example
use papers_openalex::{OpenAlexClient, GetParams};

let client = OpenAlexClient::new();
let funder = client.get_funder("F4320332161", &GetParams::default()).await?;
println!("{} ({}): {} awards",
    funder.display_name.as_deref().unwrap_or("?"),
    funder.country_code.as_deref().unwrap_or("?"),
    funder.awards_count.unwrap_or(0));
Source

pub async fn autocomplete_works( &self, q: &str, ) -> Result<AutocompleteResponse, OpenAlexError>

Autocomplete for works. Searches titles. Returns up to 10 results sorted by citation count. Hint shows first author name.

GET /autocomplete/works?q=...

§Example
use papers_openalex::OpenAlexClient;

let client = OpenAlexClient::new();
let response = client.autocomplete_works("machine learning").await?;
for result in &response.results {
    println!("{} (by {})", result.display_name,
        result.hint.as_deref().unwrap_or("unknown author"));
}
Source

pub async fn autocomplete_authors( &self, q: &str, ) -> Result<AutocompleteResponse, OpenAlexError>

Autocomplete for authors. Searches display names. Returns up to 10 results sorted by citation count. Hint shows last known institution and country.

GET /autocomplete/authors?q=...

§Example
use papers_openalex::OpenAlexClient;

let client = OpenAlexClient::new();
let response = client.autocomplete_authors("einstein").await?;
for result in &response.results {
    println!("{} — {}", result.display_name,
        result.hint.as_deref().unwrap_or(""));
}
Source

pub async fn autocomplete_sources( &self, q: &str, ) -> Result<AutocompleteResponse, OpenAlexError>

Autocomplete for sources (journals, repositories). Searches display names. Returns up to 10 results sorted by citation count. Hint shows host organization. External ID is ISSN.

GET /autocomplete/sources?q=...

§Example
use papers_openalex::OpenAlexClient;

let client = OpenAlexClient::new();
let response = client.autocomplete_sources("nature").await?;
for result in &response.results {
    println!("{} ({})", result.display_name,
        result.hint.as_deref().unwrap_or(""));
}
Source

pub async fn autocomplete_institutions( &self, q: &str, ) -> Result<AutocompleteResponse, OpenAlexError>

Autocomplete for institutions. Searches display names. Returns up to 10 results sorted by citation count. Hint shows city and country. External ID is ROR.

GET /autocomplete/institutions?q=...

§Example
use papers_openalex::OpenAlexClient;

let client = OpenAlexClient::new();
let response = client.autocomplete_institutions("harvard").await?;
for result in &response.results {
    println!("{} — {}", result.display_name,
        result.hint.as_deref().unwrap_or(""));
}
Source

pub async fn autocomplete_publishers( &self, q: &str, ) -> Result<AutocompleteResponse, OpenAlexError>

Autocomplete for publishers. Searches display names. Returns up to 10 results sorted by citation count. Hint shows country.

GET /autocomplete/publishers?q=...

Note: this endpoint has been observed returning HTTP 500 errors intermittently (server-side issue).

§Example
use papers_openalex::OpenAlexClient;

let client = OpenAlexClient::new();
let response = client.autocomplete_publishers("elsevier").await?;
for result in &response.results {
    println!("{} ({})", result.display_name,
        result.hint.as_deref().unwrap_or(""));
}
Source

pub async fn autocomplete_subfields( &self, q: &str, ) -> Result<AutocompleteResponse, OpenAlexError>

Autocomplete for subfields. Searches display names. Returns up to 10 results sorted by citation count. Hint shows description.

GET /autocomplete/subfields?q=...

Note: autocomplete is only available for subfields, not domains or fields (/autocomplete/domains and /autocomplete/fields return 404).

§Quirks

The subfield autocomplete endpoint returns entity_type: null and short_id: "Nones/..." — these are known API quirks. The AutocompleteResult fields are Option<String> to handle this.

§Example
use papers_openalex::OpenAlexClient;

let client = OpenAlexClient::new();
let response = client.autocomplete_subfields("artificial").await?;
for result in &response.results {
    println!("{} — {}", result.display_name,
        result.hint.as_deref().unwrap_or(""));
}
Source

pub async fn autocomplete_funders( &self, q: &str, ) -> Result<AutocompleteResponse, OpenAlexError>

Autocomplete for funders. Searches display names. Returns up to 10 results sorted by citation count. Hint shows country and description.

GET /autocomplete/funders?q=...

§Example
use papers_openalex::OpenAlexClient;

let client = OpenAlexClient::new();
let response = client.autocomplete_funders("national science").await?;
for result in &response.results {
    println!("{} ({})", result.display_name,
        result.hint.as_deref().unwrap_or(""));
}
Source

pub async fn find_works( &self, params: &FindWorksParams, ) -> Result<FindWorksResponse, OpenAlexError>

Semantic search for works via GET. Sends query as a query parameter. Returns works ranked by AI similarity score (0-1). Maximum 10,000 character query. Requires API key. Costs 1,000 credits per request.

GET /find/works?query=...

§Example
use papers_openalex::{OpenAlexClient, FindWorksParams};

let client = OpenAlexClient::with_api_key("your-key");
let params = FindWorksParams::builder()
    .query("machine learning for drug discovery")
    .count(5)
    .build();
let response = client.find_works(&params).await?;
for result in &response.results {
    println!("Score {:.2}: {}", result.score,
        result.work.get("display_name")
            .and_then(|v| v.as_str())
            .unwrap_or("?"));
}
Source

pub async fn find_works_post( &self, params: &FindWorksParams, ) -> Result<FindWorksResponse, OpenAlexError>

Semantic search for works via POST. Sends query in JSON body as {"query": "..."}, useful for long queries exceeding URL length limits. Same response format as find_works. Requires API key. Costs 1,000 credits per request.

POST /find/works

§Example
use papers_openalex::{OpenAlexClient, FindWorksParams};

let client = OpenAlexClient::with_api_key("your-key");
let long_query = "A very long research question or abstract text...";
let params = FindWorksParams::builder()
    .query(long_query)
    .count(10)
    .filter("publication_year:>2020")
    .build();
let response = client.find_works_post(&params).await?;

Trait Implementations§

Source§

impl Clone for OpenAlexClient

Source§

fn clone(&self) -> OpenAlexClient

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Default for OpenAlexClient

Source§

fn default() -> OpenAlexClient

Returns the “default value” for a type. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T> Instrument for T

Source§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided Span, returning an Instrumented wrapper. Read more
Source§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> PolicyExt for T
where T: ?Sized,

Source§

fn and<P, B, E>(self, other: P) -> And<T, P>
where T: Policy<B, E>, P: Policy<B, E>,

Create a new Policy that returns Action::Follow only if self and other return Action::Follow. Read more
Source§

fn or<P, B, E>(self, other: P) -> Or<T, P>
where T: Policy<B, E>, P: Policy<B, E>,

Create a new Policy that returns Action::Follow if either self or other returns Action::Follow. Read more
Source§

impl<T> Same for T

Source§

type Output = T

Should always be Self
Source§

impl<T> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
Source§

impl<V, T> VZip<V> for T
where V: MultiLane<T>,

Source§

fn vzip(self) -> V

Source§

impl<T> WithSubscriber for T

Source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a WithDispatch wrapper. Read more
Source§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a WithDispatch wrapper. Read more