EDINET API で会計ソフト4社の財務データを取得・比較
朝、EDINET DB APIのドキュメントを開いた。昼、Windowsシェルの引数長制限に2回ぶつかった。夜、OBCの一人当たり売上高がクラウド会計A社/クラウド会計B社の5倍という数字をスプレッドシートで眺めていた。EDINET APIから有報の財務データを引っ張り出し、会計ソフト4社の損益構造を並べるまでの一日の記録。
対象4社
| 社名 | 証券コード | セグメント |
|---|---|---|
| クラウド会計A社 | - | クラウドSaaS |
| クラウド会計B社 | - | クラウドSaaS |
| オービックビジネスコンサルタント(OBC) | 4733 | パッケージ+クラウド |
| オービック | 4684 | SI+パッケージ |
EDINET APIからのデータ取得
EDINET DB APIのv2エンドポイントで、各社の有価証券報告書からXBRLタグ付き財務データを取得した。
取得項目は売上高・売上原価・販管費・営業利益・経常利益・当期純利益・従業員数。期間は取得可能な全年度分。
User-Agent問題で403
最初のリクエストが 403 Forbidden で返ってきた。原因はUser-Agentヘッダの未設定。EDINET APIはブラウザ以外からのアクセスに対してUser-Agentを要求する。ヘッダーに適当な文字列を設定して再実行し、正常にレスポンスが返った。
APIレスポンス → ローカルJSON → スプレッドシートの2段階方式
当初はAPIレスポンスをそのままスプレッドシートに書き込んでいたが、デバッグのたびにAPIを叩くのは無駄が多い。途中からローカルにJSONファイルとして保存し、スプレッドシートへの書き込みはローカルJSONから行う2段階方式に切り替えた。これでAPI呼び出し回数が減り、書き込みロジックの修正時にAPIを叩かずに済むようになった。
Googleスプレッドシートへの書き込み
Windowsシェルの引数長制限
4社×複数年度の財務データをGoogle Sheets APIに一発で渡そうとしたら、コマンドライン引数がWindowsの8191文字制限を超えた。エラーメッセージが出るわけではなく、引数が途中で切れてJSONパースに失敗するという地味な壊れ方をする。
対処としてデータをチャンク分割し、複数回のAPI呼び出しに分けて書き込むようにした。1チャンクあたり数年度分に収まるサイズに調整。
日本語シート名のエンコーディング問題
シート名を「年次業績」のような日本語にしたところ、Windows環境でcp932エンコーディングとの衝突が発生した。Google Sheets APIに渡すURLエンコード済みの日本語シート名がcp932の制約で化ける。
回避策としてシート名をASCII(Annual, Quarterly等)に統一した。中身のデータは日本語でも問題なく書き込める。シート名だけの制約。
四半期データの追加取得
年次データに加えて四半期データも取得し、別シートに追加。四半期の推移を見ることで、季節性や成長の加速・減速がより細かく見えるようになった。
SaaS vs オンプレの損益構造差
売上高が近い年度同士を並べてPL実額を比較した。
クラウド会計B社 vs OBC(~330億円規模)
両社とも売上高330億円前後の年度を持つ。粗利率はほぼ同水準。ところが販管費がクラウド会計B社はOBCの約2倍に膨らんでおり、営業利益に大きな差がついていた。
クラウド会計A社 vs OBC(~500億円規模)
クラウド会計A社の売上高が500億円に達した年度とOBCの同規模年度を比較しても、構造は同じ。粗利は近いのに販管費がクラウド会計A社は2倍以上、結果としてクラウド会計A社は営業赤字、OBCは営業黒字。
構造差の正体
SaaS企業(クラウド会計A社/クラウド会計B社)は顧客獲得コスト(CAC)を販管費に積み込んで成長を買っている。広告宣伝費・営業人件費が販管費を押し上げる。一方、OBC/オービックはパートナー経由の販売モデルで、販管費を抑えたまま売上を伸ばせる構造になっている。
一人当たり売上高(RPE)の比較
RPE(Revenue Per Employee)を各社で算出し、日米SaaSベンチマークと並べたSVGチャートを作成した。
数字が語ったこと
- OBCのRPEはクラウド会計A社/クラウド会計B社の約5倍の水準から出発している
- クラウド会計A社/クラウド会計B社のRPE改善率(CAGR)はOBCと同程度。改善のペース自体は似ているが、出発点が5倍違う
- 日米SaaSベンチマークと比較すると、クラウド会計A社/クラウド会計B社は米国SaaS企業の中央値にまだ届いていない
GTMモデルの違いがRPEに効く
OBCはパートナー制度(販売マージン+保守契約収入をパートナーに付与)で間接販売を回す。パートナーの営業人員はOBCの従業員数にカウントされないため、RPEが構造的に高くなる。
クラウド会計A社/クラウド会計B社は直販中心のGTMモデル。営業・カスタマーサクセスの人員を自社で抱えるため、従業員数が膨らみRPEが下がる。どちらが「正しい」ではなく、モデルの違いがそのまま数字に表れている。
「SaaSがオンプレを駆逐した」は起きていない
最も意外だった発見。OBCは同期間で+59%成長、オービックは+64%成長。SaaS企業だけが伸びているのではなく、会計ソフト市場全体が拡大し、オンプレ/パッケージ勢も着実に売上を伸ばしている。
ゼロサムの奪い合いではなく、市場拡大の中でSaaSとオンプレが棲み分けている構図が数字から浮かび上がった。中小企業のクラウド移行でSaaSが新規市場を開拓する一方、中堅以上の既存顧客基盤ではOBC/オービックがしっかり維持・成長している。
試行錯誤まとめ
| # | 問題 | 対処 |
|---|---|---|
| 1 | EDINET APIで403 | User-Agentヘッダを設定して解決 |
| 2 | コマンドライン引数が長すぎてJSON壊れる | データをチャンク分割して複数回書き込み |
| 3 | 日本語シート名がcp932で化ける | シート名をASCIIに統一 |
| 4 | APIデバッグのたびに呼び出し回数が増える | ローカルJSON保存の2段階方式に変更 |
学んだこと
- EDINET DB APIは有報のXBRLデータを構造化して返してくれるので、財務データの横比較に向いている。ただしUser-Agentが必要な点はドキュメントに目立つ記載がなく、403で初めて気づいた
- Windowsのコマンドライン引数長制限(8191文字)は、大量データをCLI経由で渡すときに静かに壊れる。エラーではなく「途中で切れる」のが厄介
- SaaS企業の販管費の大きさは「非効率」ではなく、直販GTMモデルのコスト構造がそのまま反映されたもの。パートナーモデルとの構造差を理解しないと、PLの読み方を間違える
- 「SaaS vs オンプレ」を二項対立で捉えていたが、実際は市場拡大の中で両方が伸びている。データを並べて初めて、自分の思い込みが数字に否定された