Skip to content

Commit d874f51

Browse files
aborrusoclaude
andcommitted
openalex skill v0.2: integrate API guide for LLMs
Add filter syntax (OR/NOT/ranges), batch pipe lookup, two-step entity lookup, group_by/sample/seed params, per-page=200 default, error handling with backoff, endpoint costs table. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent 78c21a8 commit d874f51

2 files changed

Lines changed: 82 additions & 3 deletions

File tree

LOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
# LOG
22

3+
## 2026-02-24
4+
5+
- `openalex` SKILL.md v0.2: added filter syntax (OR/NOT/ranges), batch pipe lookup, two-step entity lookup, `group_by`/`sample`/`seed` params, `per-page=200` default, error handling with backoff, endpoint costs table
6+
37
## 2026-02-15
48

59
- Saved reference documents in `docs/`: [testing-agent-skills-with-evals.md](docs/testing-agent-skills-with-evals.md) and [agent-skills-specification.md](docs/agent-skills-specification.md)

skills/openalex/SKILL.md

Lines changed: 78 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ description: Query OpenAlex API from the command line with curl and jq for publi
44
compatibility: Requires curl, jq, bash, OPENALEX_API_KEY environment variable, and internet access.
55
license: CC BY-SA 4.0 (Creative Commons Attribution-ShareAlike 4.0 International)
66
metadata:
7-
version: "0.1"
7+
version: "0.2"
88
author: "Andrea Borruso <aborruso@gmail.com>"
99
tags: [api, research, scholarly, bibliometrics, open-access, curl, jq, pdf]
1010
---
@@ -45,7 +45,7 @@ export OPENALEX_API_KEY='...'
4545
2. Run list query (works):
4646

4747
```bash
48-
curl -sS --get 'https://api.openalex.org/works' --data-urlencode 'search="data quality" AND "open government data"' --data-urlencode 'filter=type:article,from_publication_date:2023-01-01' --data-urlencode 'sort=relevance_score:desc' --data-urlencode 'per-page=20' --data-urlencode 'select=id,display_name,publication_year,cited_by_count,doi' --data-urlencode "api_key=$OPENALEX_API_KEY" | jq '.results[] | {title:.display_name, year:.publication_year, cited_by:.cited_by_count, doi}'
48+
curl -sS --get 'https://api.openalex.org/works' --data-urlencode 'search="data quality" AND "open government data"' --data-urlencode 'filter=type:article,from_publication_date:2023-01-01' --data-urlencode 'sort=relevance_score:desc' --data-urlencode 'per-page=200' --data-urlencode 'select=id,display_name,publication_year,cited_by_count,doi' --data-urlencode "api_key=$OPENALEX_API_KEY" | jq '.results[] | {title:.display_name, year:.publication_year, cited_by:.cited_by_count, doi}'
4949
```
5050

5151
## Workflow
@@ -61,11 +61,55 @@ curl -sS --get 'https://api.openalex.org/works' --data-urlencode 'search="data q
6161

6262
- `title.search=`: searches only in the title — use this by default for focused results. Must be passed inside `filter=`, not as a standalone parameter: `filter=title.search:"your query"`.
6363
- `search=`: full-text search across the entire document — use only when title-only matching is too restrictive.
64+
- `search.semantic=`: semantic/conceptual search (costs $0.001/request; requires API key).
6465
- `filter=`: exact/structured constraints; comma means AND.
6566
- `sort=`: `relevance_score:desc`, `cited_by_count:desc`, `publication_date:desc`, etc.
66-
- `per-page=`: 1..200.
67+
- `per-page=`: 1..200. **Default is 25 — always set `per-page=200` for bulk queries (8× fewer API calls).**
68+
- `page=`: page number for standard pagination.
6769
- `cursor=*`: deep pagination beyond first 10k records.
6870
- `select=`: reduce payload; nested paths are not allowed in `select`.
71+
- `group_by=`: aggregate results by a field (e.g. `group_by=publication_year`, `group_by=topics.id`).
72+
- `sample=`: random sample of N results (e.g. `sample=20`). Add `seed=42` for reproducibility.
73+
74+
## Filter Syntax
75+
76+
Filters are comma-separated AND conditions. Within a single attribute:
77+
78+
| Logic | Syntax | Example |
79+
|-------|--------|---------|
80+
| AND (comma) | `filter=a:x,b:y` | `filter=type:article,is_oa:true` |
81+
| OR (pipe) | `filter=type:article\|book` | multiple values for same field |
82+
| NOT (exclamation) | `filter=type:!journal-article` | negation |
83+
| Greater than | `filter=cited_by_count:>100` | comparison |
84+
| Less than | `filter=publication_year:<2020` | comparison |
85+
| Range | `filter=publication_year:2020-2023` | inclusive range |
86+
87+
## Batch Lookup
88+
89+
Combine up to **50 IDs in one request** using the pipe operator — avoid sequential calls:
90+
91+
```bash
92+
# Batch DOI lookup (up to 50 per request)
93+
curl -sS --get 'https://api.openalex.org/works' --data-urlencode 'filter=doi:https://doi.org/10.1/abc|https://doi.org/10.2/def' --data-urlencode 'per-page=50' --data-urlencode "api_key=$OPENALEX_API_KEY" | jq '.results[] | {title:.display_name, doi}'
94+
```
95+
96+
## Two-Step Entity Lookup
97+
98+
Names are ambiguous; always resolve to an OpenAlex ID first, then filter.
99+
100+
**Step 1 — find the entity ID:**
101+
102+
```bash
103+
curl -sS --get 'https://api.openalex.org/authors' --data-urlencode 'search=Heather Piwowar' --data-urlencode 'per-page=5' --data-urlencode "api_key=$OPENALEX_API_KEY" | jq '.results[] | {id, display_name}'
104+
```
105+
106+
**Step 2 — use the ID in a filter:**
107+
108+
```bash
109+
curl -sS --get 'https://api.openalex.org/works' --data-urlencode 'filter=authorships.author.id:A5023888391' --data-urlencode 'per-page=200' --data-urlencode 'select=id,display_name,publication_year,cited_by_count' --data-urlencode "api_key=$OPENALEX_API_KEY" | jq '.results[] | {title:.display_name, year:.publication_year}'
110+
```
111+
112+
Applies to: authors (`authorships.author.id`), institutions (`authorships.institutions.id`), sources/journals (`primary_location.source.id`). External IDs are also accepted: ORCID, ROR, ISSN, DOI.
69113

70114
## PDF Retrieval
71115

@@ -108,10 +152,41 @@ Rules:
108152
- The header array and data array must have the same number of columns.
109153
- Use `-r` (raw output) so `@csv` produces plain text, not JSON strings.
110154

155+
## Error Handling
156+
157+
Implement exponential backoff on 403 (rate limit) and 500 (server error):
158+
159+
```
160+
attempt 1 → wait 1s → attempt 2 → wait 2s → attempt 3 → wait 4s → attempt 4 → wait 8s
161+
```
162+
163+
HTTP codes:
164+
- `200` — success
165+
- `400` — invalid parameter or filter syntax; fix the query
166+
- `403` — rate limit exceeded; back off and retry
167+
- `404` — entity not found
168+
- `500` — temporary server error; retry with backoff
169+
170+
## Endpoint Costs
171+
172+
With the free $1/day budget:
173+
174+
| Request type | Cost | Daily limit |
175+
|---|---|---|
176+
| Singleton (`/works/W123`) | free | unlimited |
177+
| List / filter | $0.0001 | ~10,000 requests |
178+
| Search (full-text or semantic) | $0.001 | ~1,000 requests |
179+
| PDF download (`content.openalex.org`) | $0.01 | ~100 downloads |
180+
181+
Use `select=` and `per-page=200` to minimize request count.
182+
111183
## Common Pitfalls
112184

113185
- Do not sort by `relevance_score` without a search query.
114186
- Do not use nested fields in `select` (example: use `open_access`, then parse `.open_access.is_oa` with `jq`).
187+
- Do not filter by entity names directly — use the two-step entity lookup to get the ID first.
188+
- Do not use sequential calls for batch ID lookups — batch up to 50 with the pipe operator.
189+
- Do not use `per-page=25` (default) for bulk extraction — always set `per-page=200`.
115190
- Expect some records to have no downloadable PDF.
116191
- `search=` searches full text and can return loosely related results. Use `title.search=` when the topic must appear in the title.
117192
- Always write `curl` commands on a single line — multi-line `\` continuation breaks argument parsing in agent environments.

0 commit comments

Comments
 (0)