1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

図書館APIを読んだら、format=jsonがJSONを返さなかった【kurashi-skill Day 6】

1
Posted at

読みたい本がある。買う前に、エージェントに聞いた。「近所の図書館にある?」。数秒で返ってきた。「世田谷の中央図書館にはありますが、貸出中です」。蔵書の有無だけじゃなく、いま借りられるかどうかまで分かる。図書館はタダだ。この一手間で本代が浮く。

kurashi-skillの6つ目、calil-booksは、カーリル図書館APIで全国の図書館の蔵書と貸出状況を調べるスキルだ。公式APIで、利用は無料。公共の蔵書データが、図書館と連携する民間の無料APIを通じて市民に届く。僕はこの形が好きだ。

起: まず館を探す

蔵書照会には、図書館システムのID(systemid)が要る。都道府県や市区町村で図書館を検索して手に入れる。

curl -s "https://api.calil.jp/library?appkey=${CALIL_APPKEY}&pref=東京都&city=世田谷区&format=json&callback=no"

APIキーはカーリルのサイトで無料発行できる(最初の1回だけ)。キーはチャットやコミットに書かず、環境変数に入れる。検索結果には systemid(例: Tokyo_Setagaya)と、館の略名 libkey、正式名 formal が並ぶ。

承: 本の貸出状況を聞く

curl -s "https://api.calil.jp/check?appkey=${CALIL_APPKEY}&isbn=9784478025819&systemid=Tokyo_Setagaya&format=json&callback=no"

実際に打ってみた(2026-09-14実測)。初回の応答はこれだ。

{"session": "0a52...", "continue": 1, "books": {"9784478025819": {"Tokyo_Setagaya": {"status": "Running"}}}}

continue: 1 は「まだ集計中」の意味。返ってきた session を付けて2秒以上あけて再取得する。1回のポーリングで continue: 0 になり、館ごとの結果が出た。中央図書館は貸出中、梅丘図書館も貸出中……と並ぶ。状態は「貸出可」「蔵書なし」「貸出中」「予約中」「休館中」などで、reserveurl に予約ページのURLが入ることもある。

「貸出中」は少し残念だが、予約できる。情報があるのとないのとでは週末の過ごし方が変わる。

ちなみに systemid はカンマ区切りで複数指定できる。東京在住で「近くの図書館」とだけ言われたときのため、スキルには既定の照会先として、東京都立図書館(Tokyo_Pref)と23区全部の区立図書館のsystemid表を載せた(APIで実測確認済み)。例えば都立・世田谷・渋谷をまとめて1回で聞くならこうだ。

curl -s "https://api.calil.jp/check?appkey=${CALIL_APPKEY}&isbn=9784478025819&systemid=Tokyo_Pref,Tokyo_Setagaya,Tokyo_Shibuya&format=json&callback=no"

24館分を1回のポーリングで見られるので、区をまたいで「どこかにいる本」を探すのが一気に楽になる。

転: 2つの罠

このAPI、素直に見えて罠が2つある。

1つ目: format=json なのにJSONが返ってこない。 上の図書館検索から callback=no を外すと、応答は callback([...]) でラップされたJSONPになる。実際に試した。format=json と書いたのにだ。カーリル自身がJSONPは非推奨と案内しているが、既定はそちら側だ。jq にそのまま食わせると構文エラーになる。「JSONをください」と書いたのにJSONでないものが返る。仕様とはそういうものだと、最初の数バイトを自分の目で見るまで信じないことにした。

2つ目: 答えが非同期。 初回に continue: 1 が返る仕組みを知らないと、「status: Running」をエラーと勘違いする。かといって、終わるまで無限にポーリングするのも失格だ。スキルには上限を書いた。最大10回・合計2分程度。それを超えたら打ち切って、「図書館APIの照会が混み合っています。時間をおいてもう一度」とユーザーに伝える。粘りすぎないのも礼儀だ。

そして「蔵書なし」はエラーではない。その館に本がないという、正常な結果だ。空や否定を失敗と混同しない。

結: 持ち帰れる教訓3つ

  1. format=json は約束ではない。 応答の最初の数バイトを自分で確認する。JSONPの罠はいまでも生きている。
  2. 非同期APIには上限つきポーリングを。 session を受け取り、間隔をあけ、回数と時間の上限を決め、打ち切ったときの言葉まで用意する。
  3. 「ない」も結果。 蔵書なし・空配列・0件は、失敗ではなく答え。取得失敗と分けて伝える。

kurashi-skillは公開開発中。リポジトリはここ → https://github.com/tahodev/kurashi-skill

結局その本は、返ってくるのを予約で待つことにした。本は買う前に、図書館に聞く。タダのAPIで、タダの本が見つかる。

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?