Claude CodeでOpenAlex Sources APIを引き、投稿先のAPCとOA区分を比べる
投稿先候補のISSNからOpenAlex Sources APIを引き、掲載料(APC)とOA区分を1枚の比較表にする手順です。実行結果の読み方と、数字を鵜呑みにできない場面も載せています。
投稿先の候補が3〜5誌まで絞れたら、次に知りたいのは掲載料(APC)と、その雑誌が完全なオープンアクセス(OA)誌かどうかです。OpenAlexのSourcesエンドポイントは、ISSNを渡すとこの2つを返します。Claude Codeにはスクリプトを書かせ、候補のISSNを渡して比較表にまとめさせます。
この記事では、ISSNの一覧から1回のリクエストで表を作るところまでを扱います。実行結果の読み方と、APCの数字が実際の支払額ではない点もあわせて載せます。論文単位の被引用から読む順を決める手順は、OpenAlex APIで被引用と参考文献を辿る記事にあります。
ISSNから引ける項目は何か
OpenAlexでいう「ソース」は、論文が載る場所のことです。雑誌のほか、会議録のシリーズ、プレプリントや機関のリポジトリ、書籍シリーズも含みます。ソースはISSNで識別され、印刷版と電子版のISSNは、リンクISSN(issn_l)で1件にまとまります。
投稿先の比較に使う項目は、次の5つです。
投稿先の比較に使うソースの項目
apc_usd
APCの定価をUSDに換算した整数です。通貨の違う雑誌も同じ軸で並べられます。
apc_prices
通貨ごとの定価の一覧です。
priceとcurrencyの組で返ります。is_oa
全論文がOAの雑誌なら真です。DOAJに載っていない小さな雑誌も含みます。
is_in_doaj
DOAJ(オープンアクセス誌のディレクトリ)に載っているかどうかです。真なら
is_oaも真になります。type
journalやconferenceなど、ソースの種類です。投稿先が雑誌か会議かの確認に使えます。
会議への投稿を比べたいときは、type の値に注意します。会議はシリーズ単位で1つのソースになり、年ごとの開催回は別のソースになりません。ただし論文集がLecture Notes in Computer Scienceのような一般の書籍シリーズに載る場合は、会議ではなくそのシリーズがソースになります。
単体の取得は https://api.openalex.org/sources/issn:0028-0836 のようにISSNを直接書きます。複数誌を比べるなら、一覧のフィルタで | を使い、ISSNを並べて1回で引くほうが速く済みます。OR条件に並べられる値は100個までです。
候補のISSNを1リクエストで引くスクリプト
次のスクリプトは、コマンドラインに並べたISSNをまとめて引き、APCの安い順に表示します。応答から必要な項目だけを select で絞るので、応答は小さく済みます。
import json, os, sys, urllib.parse, urllib.request
FIELDS = ("display_name,issn_l,issn,type,is_oa,is_in_doaj,"
"oa_flip_year,apc_usd,apc_prices,host_organization_name")
def fetch(issns):
params = {
"filter": "issn:" + "|".join(issns),
"select": FIELDS,
"per_page": 100,
}
key = os.environ.get("OPENALEX_API_KEY")
if key:
params["api_key"] = key
url = ("https://api.openalex.org/sources?"
+ urllib.parse.urlencode(params, safe=":|,"))
with urllib.request.urlopen(url, timeout=30) as res:
return json.load(res)["results"]
def main(issns):
rows = fetch(issns)
found = {i for r in rows for i in (r.get("issn") or [])}
rows.sort(key=lambda r: (r["apc_usd"] is None, r["apc_usd"] or 0))
for r in rows:
apc = r["apc_usd"] if r["apc_usd"] is not None else "-"
print(f'{r["display_name"]}\t{r["issn_l"]}\t{r["type"]}\t'
f'oa={r["is_oa"]}\tdoaj={r["is_in_doaj"]}\tapc_usd={apc}')
for i in issns:
if i not in found:
print(f"NOT FOUND\t{i}")
if __name__ == "__main__":
main(sys.argv[1:])最後のループは、渡したISSNのうち1件も当たらなかったものを NOT FOUND と出します。ISSNの打ち間違いは、エラーではなく「結果が減る」形で現れるためです。
python3 apc_compare.py 0028-0836 1932-6203 2041-17232026年10月27日に手元で実行した結果は次のとおりです(APCは定価をUSDにしたもの)。
| 雑誌 | リンクISSN | is_oa | is_in_doaj | apc_usd |
|---|---|---|---|---|
| PLoS ONE | リンクISSN1932-6203 | is_oatrue | is_in_doajtrue | apc_usd2382 |
| Nature Communications | リンクISSN2041-1723 | is_oatrue | is_in_doajtrue | apc_usd6990 |
| Nature | リンクISSN0028-0836 | is_oafalse | is_in_doajfalse | apc_usd12690 |
同じ呼び出しの応答には、apc_prices として通貨ごとの定価も入っています。Nature Communicationsなら5,890ユーロや5,290ポンドといった値で、USDだけでなく自分の支払う通貨でも確かめられます。
結果の読み方 — OA誌とハイブリッド誌を混ぜない
上の表で目を引くのは、Natureです。is_oa が偽なのに、apc_usd に12690が入っています。
これは矛盾ではありません。is_oa が真なのは全論文がOAの雑誌で、購読型の雑誌が著者の希望でOAにする場合(ハイブリッド)は偽のままです。APCの欄は「OAにするなら必要な定価」を表すので、購読型の雑誌にも値が入ります。
同じAPCでも意味が違う2つの雑誌
PLoS ONE
is_oa も is_in_doaj も真です。掲載されればすべてOAで、APCは掲載の条件になります。
Nature
is_oa は偽です。APCを払わず購読型として載せる選択肢も残っています。
比較表を作るときは、is_oa と is_in_doaj の2列を必ず残します。APCの安い順に並べるだけだと、APCを払わずに済む購読型の雑誌が、払う前提の雑誌と同列に並んでしまいます。
DOAJの列には、もう一つ役割があります。OpenAlexは雑誌の質に基準を設けておらず、独自の審査もしません。公式の説明では、正当性が気になるときは is_in_doaj を使うよう案内されています。
表の仕上げをClaude Codeに任せる
スクリプトの出力はタブ区切りの数行なので、次の仕上げはClaude Codeに頼めます。候補誌のISSNを渡し、「apc_compare.py を実行して、OA区分の列つきのMarkdown表にして」と頼みます。毎回同じ形で返してもらうために、CLAUDE.mdに取り決めを置いておくと安定します。
## 投稿先の比較(OpenAlex Sources)
- 候補のISSNは `python3 apc_compare.py <ISSN...>` でまとめて引く
- 表の列は 雑誌名 / リンクISSN / OA区分 / DOAJ / APC(USD) の順にする
- OA区分は is_oa が真なら「完全OA」、偽でapc_usdがあれば「ハイブリッド(定価あり)」、
どちらも無ければ「APC情報なし」と書く
- 数字は応答の値をそのまま書き、換算や推測の補完をしない
- NOT FOUND が出たISSNは表に入れず、末尾に一覧で報告する実行の許可は、読み取りだけの呼び出しなので範囲を絞って足せます。プロジェクトの .claude/settings.json に、スクリプトの実行だけを書きます。
{
"permissions": {
"allow": ["Bash(python3 apc_compare.py:*)"]
}
}このルールなら、任意のコマンドやほかのURLへのアクセスまでは許可されません。APIキーを使う場合は、環境変数の OPENALEX_API_KEY に置き、CLAUDE.mdやスクリプトには書きません。
APCの数字を鵜呑みにしない場面
APCの値は、投稿の見積もりの出発点にはなりますが、請求額とは限りません。ドキュメントに書かれている制約を表にまとめます。
| 制約 | 比較への影響 |
|---|---|
| 定価であり、著者が実際に払った額ではない | 比較への影響機関の割引や免除は反映されない |
| 1誌につき現在年の定価が1つだけ | 比較への影響過去の年に当てはめると実態とずれる |
| 定価の出所はDOAJと手作業の補完 | 比較への影響ハイブリッド誌は情報が薄い |
| 品質の審査はしない | 比較への影響安いAPCは良い雑誌の証拠にならない |
3行目の「ハイブリッド誌は情報が薄い」は、実務で効きます。APCの支出が多いのはハイブリッド誌のほうなのに、DOAJ由来の価格は完全OA誌に偏っています。apc_usd が空欄(スクリプトの表示では -)の購読型の雑誌は、「無料」ではなく「OpenAlexに定価が無い」という意味です。投稿規程のページで直接確かめてください。
たとえばScienceのISSN(0036-8075)を同じスクリプトに渡すと、手元ではAPCが空欄で返りました。
落とし穴
APCが空欄の雑誌を「掲載無料」と読んでしまう
空欄は、OpenAlexに定価の記録が無いことを示します。投稿が無料という意味ではありません。投稿先の公式ページの料金表で、必ず最終確認をします。
年ごとの定価を追いたいときは、ソースに apc_usd_by_year という項目があります。手元でNatureを引くと、2021年から2025年までの値が並んで返りました。ただし項目の説明では、年別の値は「年別のデータがある場合」だけ入るとされています。比較表のメイン列には、一覧のフィルタや並べ替えに使える apc_usd を使うのが安全です。
候補を広げたいときは絞り込みで探す
ISSNが決まっていない段階でも、Sourcesの一覧はフィルタと並べ替えで探せます。たとえば、DOAJに載る雑誌のうちAPCが0ドルより高く3,000ドル未満のものを、掲載論文数の多い順に3件出す呼び出しは次のとおりです。
curl -s "https://api.openalex.org/sources?filter=is_in_doaj:true,apc_usd:%3C3000,apc_usd:%3E0,type:journal&select=display_name,apc_usd&sort=works_count:desc&per_page=3"同日に手元で実行すると、PLoS ONE(2382)、Scientific Reports(2690)、IEEE Access(2075)の順で返りました。条件に当てはまる雑誌は4,795件ありました。フィルタの < と > はURLの中ではエンコードして %3C と %3E にします。
件数が多すぎるときは、is_core:true でCWTS Coreの掲載誌に絞る方法もあります。この一覧はライデン大学の研究グループが維持するもので、Web of Scienceの収録誌とは別物です。
つまずきやすい点
- ISSNは印刷版でも電子版でも当たる: 電子版のISSNで引いても、
issn_lには別のリンクISSNが入ります。手元では、Nature Neuroscienceの電子版ISSN(1546-1726)で引くと、リンクISSNは1097-6256と返りました。表にはリンクISSNを残すと、重複に気づけます - ISSNを打ち間違えても、エラーにならないことがある: 手元では、でたらめなISSN(1234-5678)に対して、実在しない名前の雑誌が1件返りました。雑誌名とホスト組織名を目で確かめてから、表に載せてください
- キーなしの予算は共有: キーなしだと1日の予算を同じネットワークのIPアドレスの利用者と分け合う、と応答の
noticeに書かれていました。一覧の呼び出し1回の費用は応答のcost_usdで確かめられます - 一度に引けるのは100誌まで: OR条件の値が100個を超えるときは、ISSNを分割して複数回引きます
まとめ
投稿先の比較は、ISSNの一覧をSourcesエンドポイントに渡し、is_oa・is_in_doaj・apc_usd の3列で並べれば形になります。いちばん外しやすいのは、apc_usd の空欄を無料と読むことと、ハイブリッド誌の定価を完全OA誌と同列に並べることです。表はあくまで一次選別の道具として使い、最終的な料金と条件は投稿先の規程で確かめます。研究者本人の業績リストから出発する場合は、ORCID Public APIの業績取り込みが入口になります。機関リポジトリにすでに載っているかは、Unpaywall APIの記事の方法で調べられます。
参照した一次ソース
この記事の記述は、次の一次ソースと照合しています。更新時に参照先が変わった場合は一覧も更新します。