OpenAlex API Skill
Query the OpenAlex open scholarly metadata catalog via its REST API using curl.
Base URL: https://api.openalex.org
Auth: API key required (free at https://openalex.org/settings/api). Pass as ?api_key=KEY.
Store in env var OPENALEX_API_KEY and use api_key=$OPENALEX_API_KEY in queries.
Entity Endpoints
| Endpoint | Description |
|---|---|
/works |
Scholarly documents (articles, books, datasets) |
/authors |
Researcher profiles with disambiguated identities |
/sources |
Journals, repositories, conferences |
/institutions |
Universities, research organizations |
/topics |
Subject classifications (3-level hierarchy) |
/publishers |
Publishing organizations |
/funders |
Funding agencies |
content.openalex.org/works/{id}.pdf |
PDF download ($0.01 each) |
Singleton lookups are free (e.g., /works/W2741809807). List/filter queries cost $0.0001; search queries cost $0.001.
Critical: Two-Step ID Resolution
Names are ambiguous — never filter by display name directly. Resolve to an OpenAlex ID first, then use that ID in filters.
# WRONG
/works?filter=author_name:Einstein
# CORRECT — two steps
# 1. Search for author → get ID
curl "https://api.openalex.org/authors?search=Einstein&api_key=$OPENALEX_API_KEY"
# Response includes: id = "A5012345678"
# 2. Filter works by that ID
curl "https://api.openalex.org/works?filter=authorships.author.id:A5012345678&api_key=$OPENALEX_API_KEY"
This applies to all entities: authors, institutions, sources, topics, publishers, funders.
Query Parameters
api_key= Required. Free key from openalex.org/settings/api
filter= Filter results (see syntax below)
search= Full-text search across title/abstract/fulltext
sort= Sort results (e.g., cited_by_count:desc)
per_page= Results per page (default 25, max 100)
page= Page number
sample= Random sample size (max 10,000)
seed= Reproducible sampling seed
select= Limit returned fields (e.g., select=id,title,publication_year)
group_by= Aggregate results by a field
All parameters use snake_case.
Filter Syntax
# Single filter
?filter=publication_year:2024
# Multiple filters (AND) — comma-separated
?filter=publication_year:2024,is_oa:true
# Multiple values (OR) — pipe-separated, up to 100 values
?filter=type:article|book|dataset
# Negation
?filter=type:!paratext
# Comparison operators
?filter=cited_by_count:>100
?filter=publication_year:<2020
?filter=publication_year:2020-2024
Common Filter Fields
Works
authorships.author.id Author's OpenAlex ID
authorships.institutions.id Institution's OpenAlex ID
primary_location.source.id Journal/source OpenAlex ID
topics.id Topic ID
publication_year Year (integer)
cited_by_count Citation count (integer)
is_oa Open access (boolean)
type article, book, dataset, etc.
has_fulltext Has searchable fulltext (boolean)
Authors
last_known_institutions.id Current institution
works_count Number of works
cited_by_count Total citations
Common Patterns with curl Examples
Find works by author (two-step)
# 1. Find the author
curl -s "https://api.openalex.org/authors?search=Heather+Piwowar&api_key=$OPENALEX_API_KEY" | jq '.results[0] | {id, display_name, works_count}'
# 2. Get their works
curl -s "https://api.openalex.org/works?filter=authorships.author.id:A5023888391&per_page=10&select=id,title,publication_year,cited_by_count&api_key=$OPENALEX_API_KEY" | jq '.results[] | "\(.publication_year) [\(.cited_by_count)] \(.title)"'
Find works from an institution
# 1. Find the institution
curl -s "https://api.openalex.org/institutions?search=MIT&api_key=$OPENALEX_API_KEY" | jq '.results[0] | {id, display_name}'
# 2. Get highly-cited works
curl -s "https://api.openalex.org/works?filter=authorships.institutions.id:I63966007,cited_by_count:>100&sort=cited_by_count:desc&per_page=10&select=id,title,publication_year,cited_by_count&api_key=$OPENALEX_API_KEY" | jq '.results[]'
Bulk DOI lookup (up to 100)
curl -s "https://api.openalex.org/works?filter=doi:10.1234/a|10.1234/b|10.1234/c&per_page=100&api_key=$OPENALEX_API_KEY"
Random sample with seed
curl -s "https://api.openalex.org/works?sample=100&seed=42&select=id,title,publication_year&api_key=$OPENALEX_API_KEY"
Aggregate by field
curl -s "https://api.openalex.org/works?filter=publication_year:2024&group_by=topics.id&api_key=$OPENALEX_API_KEY"
Single work by OpenAlex ID
curl -s "https://api.openalex.org/works/W2741809807?api_key=$OPENALEX_API_KEY" | jq '{title, publication_year, cited_by_count, type}'
Limits
| Limit | Value |
|---|---|
| OR values per filter | 100 |
per_page max |
100 |
sample max |
10,000 |
| Basic paging limit | 10,000 results |
For larger result sets, use cursor-based pagination (pass cursor=* on the first request, then use the returned next_cursor value).
Error Handling
Implement exponential backoff for 429 (rate limit) and 500 (server error) responses. A simple retry pattern:
# In practice, just retry the curl command after a brief pause if you get a non-200 response.
# For scripting, use the Python pattern:
# response = requests.get(url, timeout=30)
# if response.status_code in [429, 500]: time.sleep(2 ** attempt)
Deprecated Features (Avoid)
| Old | Replacement |
|---|---|
| Concepts | Topics |
/text endpoint |
Do not use |
host_venue |
primary_location |
grants |
funders and awards |
Choosing Between OpenAlex and Semantic Scholar
Both are academic metadata APIs. Choose based on what the user needs:
| Need | Best choice |
|---|---|
| Institution/funder/topic metadata | OpenAlex (S2 doesn't have these) |
Open access filtering (is_oa) |
OpenAlex |
Aggregation (group_by) |
OpenAlex |
| Citation graphs (who cites whom) | Semantic Scholar (dedicated endpoints) |
| Abstract text in results | Semantic Scholar (richer abstract coverage) |
| Boolean search (AND/OR/NOT in query) | Semantic Scholar bulk search |
| Author h-index | Semantic Scholar |
| User says "OpenAlex" | OpenAlex |
| User says "Semantic Scholar" or "S2" | Semantic Scholar |
When in doubt and the user doesn't specify, prefer Semantic Scholar for paper search and OpenAlex for institutional/funder/topic analytics.
Workflow Tips
- Use
select=to request only the fields you need — smaller responses, lower cost. - Set
per_page=100when you need more than the default 25 results. - For "papers about X" queries, combine
search=withfilter=cited_by_count:>50to surface impactful work. - When displaying results, format as a table with title, year, and citation count.
- Batch DOI lookups with the pipe operator instead of making one request per DOI.