APIリファレンス
暫定版現在のAPIは暫定版です。2027年中に、長期的な運用を前提にしたバージョン1のリリースを目指しており、それまでは予告なく仕様を変更することがあります。
書誌の書名・巻次・著者名と目録のヨミから、語ごとのルビを返します。
https://furikake.calil.jpOpenAPI 形式の仕様: /openapi.json
クイックスタート
curl -s https://furikake.calil.jp/v1/ruby \
-H 'Content-Type: application/json' \
-d '{"records": [{
"title": "源氏物語", "volume": "上巻",
"title_yomi": ["げんじものがたり", "じょうかん"],
"author": "紫式部∥著/与謝野 晶子∥訳",
"author_yomi": ["ムラサキシキブ", "ヨサノアキコ"]}]}'
import requests
r = requests.post(
"https://furikake.calil.jp/v1/ruby",
json={"records": [{
"title": "源氏物語", "volume": "上巻",
"title_yomi": ["げんじものがたり", "じょうかん"],
"author": "紫式部∥著/与謝野 晶子∥訳",
"author_yomi": ["ムラサキシキブ", "ヨサノアキコ"],
}]},
timeout=1, # 応答が遅ければルビなしで表示を続ける
)
r.raise_for_status()
for rec in r.json()["records"]:
# ルビのある区間は「表記(ルビ)」、無い区間は表記のまま
print("".join(f"{s['text']}({s['ruby']})" if s["ruby"] else s["text"] for s in rec["title"]))
const res = await fetch("https://furikake.calil.jp/v1/ruby", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ records: [{
title: "源氏物語", volume: "上巻",
title_yomi: ["げんじものがたり", "じょうかん"],
author: "紫式部∥著/与謝野 晶子∥訳",
author_yomi: ["ムラサキシキブ", "ヨサノアキコ"],
}] }),
signal: AbortSignal.timeout(1000), // 応答が遅ければルビなしで表示を続ける
});
const { records } = await res.json();
// ルビのある区間は「表記(ルビ)」、無い区間は表記のまま。<ruby> での描画は「表示の例」の renderRuby を参照
console.log(records[0].title.map((s) => (s.ruby ? `${s.text}(${s.ruby})` : s.text)).join(""));
{
"version": "0.2.34+data-2026-11-05",
"records": [{
"state": "done",
"title": [
{"text": "源氏", "ruby": "げんじ", "yomi": "ゲンジ", "grade": 6},
{"text": "物語", "ruby": "ものがたり", "yomi": "モノガタリ", "grade": 3}],
"volume": [
{"text": "上巻", "ruby": "じょうかん", "yomi": "ジョウカン", "grade": 6}],
"author": [
{"text": "紫", "ruby": "むらさき", "yomi": "ムラサキ", "grade": 7, "person": 0},
{"text": "式部", "ruby": "しきぶ", "yomi": "シキブ", "grade": 3, "person": 0},
{"text": "∥著/", "ruby": null, "yomi": null, "grade": null, "person": null},
{"text": "与謝野", "ruby": "よさの", "yomi": "ヨサノ", "grade": 7, "person": 1},
{"text": " ", "ruby": null, "yomi": null, "grade": null, "person": null},
{"text": "晶子", "ruby": "あきこ", "yomi": "アキコ", "grade": 7, "person": 1},
{"text": "∥訳", "ruby": null, "yomi": null, "grade": null, "person": null}]
}]
}
POST/v1/ruby
書誌の配列 records を受け取り、同じ順で結果を返します。1回100件まで。
リクエスト
本文はJSONで送り、Content-Type: application/json を付けてください。無いと 422 になります。
records の要素の項目。すべて省略可。
| 項目 | 型 | 内容 |
|---|---|---|
title | 文字列 | 書名 |
volume | 文字列 | 巻次 |
title_yomi | 文字列 / 配列 | 書名・巻次の目録のヨミ。叢書名などのヨミは別の要素に |
title_yomi_fallback | 文字列 / 配列 | 補助のヨミ(NDLなど)。title_yomi で読めない部分に使う |
author | 文字列 | 著者の表記。複数人・役割語(∥著、/訳)を含んでよい |
author_yomi | 文字列 / 配列 | 著者の目録のヨミ。1人1要素を推奨 |
author_yomi_fallback | 文字列 / 配列 | 補助のヨミ(NDLなど) |
- ヨミはカタカナ・ひらがなのどちらでも可
- 配列は10要素まで。文字列は1,000字まで(
volumeは200字まで) - キャッシュは入力の形ごとに持つため、同じ書誌は毎回同じ形で送ってください
レスポンス
書誌ごとの結果が、送った順に records に入ります。書名・巻次・著者名は、それぞれ送った文字列を「区間」(語や姓・名の単位)に区切った配列で返します。区間の text をつなぐと、送った文字列そのものに戻ります。役割語や区切り記号もルビの無い区間として入るので、配列を先頭から順に表示するだけでルビ付きの文字列になります。
{
"version": "0.2.34+data-…", // 処理のバージョン
"records": [ // 書誌ごと(送った順)
{
"state": "done", // 書誌全体の状態
"title": [区間, …], // 書名
"volume": [区間, …], // 巻次
"author": [区間, …] // 著者の表記(区間に person が付く)
}
]
}
区間 = { "text", "ruby", "yomi", "grade" }
| 項目 | 内容 |
|---|---|
version | 処理のバージョン。変わると同じ入力でも結果が変わることがある |
records | 書誌ごとの結果の配列。リクエストの records と同じ順・同じ件数 |
records の要素(書誌ごと)| 項目 | 内容 |
|---|---|
state | 書誌全体の状態(state) |
title | 書名の区間の配列。送らなければ空の配列 |
volume | 巻次の区間の配列。送らなければ空の配列 |
author | 著者の表記の区間の配列。送らなければ空の配列 |
| 項目 | 内容 |
|---|---|
text | 表記。配列の text をつなぐと送った文字列になる |
ruby | 表示用の読み。漢字はひらがな、外来語と英字はカタカナ(鎮魂歌=レクイエム)。ルビが無ければ null |
yomi | 目録のヨミ(カタカナ)。ルビが無ければ null |
grade | ルビを表示する上限の学年(学年でルビを間引く)。ルビが無ければ null |
person | author の区間だけ。何人目の名前か(0から)。同じ人の姓と名は同じ番号。役割語・区切りなど名前でない区間は null |
漢字を含まない名前(さとうふみや など)は、名前としては扱わずルビの無い区間になります。person の番号もそうした名前を数えません。
// 区間の配列を、ルビ付きで要素に入れる
function renderRuby(el, segs) {
el.textContent = "";
for (const s of segs) {
if (!s.ruby) { el.append(s.text); continue; }
const ruby = document.createElement("ruby");
const rt = document.createElement("rt");
rt.textContent = s.ruby;
ruby.append(s.text, rt);
el.append(ruby);
}
}
state
書誌全体で1つです。書名・著者名のどこか1つでも未完了なら partial になります。
確定
一部が未完了。確定したルビだけ返しています。バックエンドで計算して、最大で1〜2時間で確定します。同じ入力で呼び直すと、確定した結果を返します。
ヨミが無いため、どこにもルビなし
エラー
| ステータス | 内容 |
|---|---|
413 | 101件以上を送った |
422 | 入力が不正(型・長さ・要素数)、または Content-Type が application/json でない。1件でも不正ならリクエスト全体が失敗します |
5xx | 一時的な障害(再起動や混雑など)。本文は JSON とは限りません。ルビなしで表示を続け、時間をおいて呼び直してください |
学年でルビを間引く
区間の grade は、そのルビを表示する上限の学年です。読者の学年が grade 以下の区間にだけルビを付けると、既に習った読みのルビを省けます。APIは常にすべてのルビを返すので、学年を切り替えるときも再取得は不要です。
// g: 読者の学年(1〜6 小学生, 7 中学生, 8 高校生, 9 一般)
function showsRuby(p, g) {
if (!p.ruby) return false;
if (p.grade === null) return true; // 英字など
return g <= p.grade;
}
| 学年 | 省くルビ | 例 |
|---|---|---|
| 指定なし | なし | 源氏物語 上巻 |
1〜6 小学生 | 前の学年までに習う読み | 6年生源氏物語 上巻 |
7 中学生 | 小学校で習う読み | 源氏物語 上巻 |
8 高校生 | 中学校までに習う読み | 源氏物語 上巻 |
9 一般 | 常用漢字表の音訓・付表の読み | 源氏物語 上巻 |
- 区間に含まれる漢字の読みのうち、最も遅く習うもので判定します。同じ字でも読みによって段階が異なります(外科の「げ」は中学校)
- 当て字や特殊な読み(本気=まじ、丙午=ひのえうま)は一般向けでも省きません
- 著者名にも
gradeは付きますが、人名の読みは学年と関係しないため、間引かずに表示することを推奨します - Yahoo!ルビ振りAPIとは 1〜7 が対応し、Yahoo! の 8(一般)はこのAPIの 9 にあたります
注意事項
- タイムアウトを短く(1秒程度)設定し、失敗したらルビなしで表示してください
- ルビは自動で生成しているため、誤りを含むことがあります
versionが変わると、同じ入力でも結果が変わることがあります
変更履歴
仕様の変更やバージョンアップの記録はここに掲載します。
- 図書館総合展2026のブース展示において紹介
- カーリルの書誌個別ページにてルビの表示を開始
- 0.2.38+data-2026-11-05リリース候補版
使用データ
- 国立国会図書館「NDL振り仮名コーパス(書誌データ版)」(CC BY 4.0)を加工した書名の辞書
- デジタル庁「氏名漢字カナ突合モデル」の辞書(MIT)
- 形態素解析はMeCab、辞書はUniDic(BSD 3条項)
- SudachiDict(Apache-2.0)から取り出した英字の語の読み
- 文部科学省「学年別漢字配当表」(2017年告示)、「常用漢字表の音訓の小・中・高等学校段階別割り振り表」(2017年)、文化庁「常用漢字表」(2010年内閣告示)
素材データ
ルビの表示を切り替えるボタンなどに使えるアイコンです。CC0 1.0で公開しています。クレジットの表記や連絡は要りません。図書館システムやウェブサイトに自由に組み込み、改変してお使いください。
furigana-on-frame.svgダウンロード(SVG)furigana-off-frame.svgダウンロード(SVG)furigana-on.svgダウンロード(SVG)furigana-off.svgダウンロード(SVG)- 64×64の座標で描いたSVGです。どの大きさでも使えます。枠あり・枠なしで字の位置は同じなので、差し替えても位置がずれません
- 色はファイルの中の
fill・strokeを書き換えて変えられます(枠#c9ccd2、字#333333、ルビ#1c85fb、ルビなしの字と点線#9aa1ab)
使用例
カーリルの学校図書館支援プログラムの蔵書検索では、検索結果の右上のボタンでふりがなを切り替えます。ボタンのアイコンがルビの有無を示し、押すと読む人の学年を選べます。
