サブスクリプションの売上が前月比で伸びているのに、手元に残る額がほとんど増えていない月がありました。
RevenueCat のダッシュボードでは MRR が右肩上がりです。それなのに、Gemini API の請求と App Store の手数料を引くと、増えた分がきれいに消えていました。売上を追いかけていたつもりが、原価を一緒に増やしていただけだったのです。
AI アプリの収益化が、それ以前の広告収益アプリと決定的に違うのはここでした。従来のアプリは、ユーザーが 1 人増えても追加コストはほぼゼロです。AI アプリは違います。使われるほど API 原価が積み上がり、熱心なユーザーほど赤字を生みます。
そのため、料金モデルの設計は「いくら取れるか」ではなく「1 人あたりいくら原価がかかるか」から始めるべきだと考えるようになりました。以下では、Rork で作った AI アプリを対象に、原価率から逆算して料金を決める手順と、RevenueCat での実装、運用中に毎週見ている指標までを順にまとめます。
1 人あたりの AI 原価を先に把握する
料金を決める前に、アクティブユーザー 1 人が 1 か月にいくら API を消費するかを測ります。
計測は難しくありません。AI 呼び出しの直前に、モデル名・入出力トークン数・ユーザー ID をログに残すだけです。Rork で生成したアプリなら、API を叩くラッパー関数を 1 つ作り、そこに集約するのが最短です。
// lib/aiClient.ts — 全ての AI 呼び出しをここに通す
type UsageLog = {
userId : string ;
model : string ;
inputTokens : number ;
outputTokens : number ;
costUsd : number ;
};
// 1M トークンあたりの単価(自分が使うモデルの実額に置き換える)
const PRICE_PER_M = {
'flash' : { input: 0.075 , output: 0.30 },
'pro' : { input: 1.25 , output: 5.00 },
} as const ;
export async function callAi (
userId : string ,
model : keyof typeof PRICE_PER_M ,
prompt : string ,
) {
const res = await generate (model, prompt); // 実際の SDK 呼び出し
const p = PRICE_PER_M [model];
const costUsd =
(res.inputTokens / 1_000_000 ) * p.input +
(res.outputTokens / 1_000_000 ) * p.output;
await recordUsage ({
userId,
model,
inputTokens: res.inputTokens,
outputTokens: res.outputTokens,
costUsd,
} satisfies UsageLog );
return res;
}
なぜ呼び出しごとに保存するかというと、後述する「上限到達時の扱い」と「解約予兆の検知」が、どちらもこのログを土台にするからです。月末に請求書の総額だけを見ても、誰がいくら使っているかは分かりません。ユーザー単位で持っておくと、上位 5% のヘビーユーザーが原価の大半を占めている、といった構造が見えてきます。
1 週間分のログが溜まったら、有料ユーザーの 1 か月あたり原価の中央値と 90 パーセンタイルを出します。この 2 つの数字が、料金設計の出発点になります。中央値で価格を決めると、ヘビーユーザーが集まった月に赤字化します。90 パーセンタイルを基準にすると安全ですが、価格が高くなりすぎます。実務では中央値と 90 パーセンタイルの間、やや 90 パーセンタイル寄りに置いています。
3 つの課金モデルを、粗利率の観点で比べる
課金モデルの解説は世の中にたくさんありますが、AI アプリに限れば「原価の変動が誰のリスクになるか」で選ぶのがいちばん納得のいく整理でした。
モデル
原価変動を負うのは
向いている AI アプリ
粗利率の目安
フリーミアム
開発者(無料枠の分だけ丸ごと持ち出し)
1 回の処理が軽い。画像リサイズ、短文要約など
50〜70%
サブスクリプション
開発者(使用量に上限を設けない限り)
利用頻度が読める。日課になる系のアプリ
60〜80%
クレジット制
ユーザー(使った分だけ支払う)
1 回が重い。動画生成、長文の一括処理など
75〜85%
粗利率の目安は、App Store / Google Play の手数料(多くの個人開発者は Small Business Program の 15%)を引いたうえで、AI 原価も引いた後の数字として置いています。
私はサブスクリプションを主軸に、重い処理だけクレジット制を重ねる形に落ち着きました。理由は単純で、サブスクだけだとヘビーユーザーで粗利が溶け、クレジット制だけだと「いくらかかるか分からない」という心理的な障壁で最初の課金が起きにくかったからです。月額で使い放題の安心感を売りつつ、原価が跳ねる機能だけ従量にすると、両方の弱点が補えます。
サブスクリプションの価格帯そのものは、AI アプリでも一般的なアプリと大きくは変わりません。
ティア
月額(日本)
月額(米国)
想定する内容
ライト
¥700〜¥1,500
$4.99〜$9.99
基本機能の上限拡張
スタンダード
¥1,500〜¥3,000
$9.99〜$19.99
全機能・高品質モデル
プロ
¥4,500〜¥7,500
$29.99〜$49.99
業務利用・エクスポート・API
年額は月額の 50〜60% 相当に置くのが一般的です。年払いのユーザーは解約率が低く、キャッシュフローも前倒しで入ります。AI アプリの場合は、年払いで受け取った金額に対して原価が 12 か月かけて発生する点だけ意識しておくと、資金繰りの見誤りを避けられます。
無料と有料の境界線をどこに引くか
「無料枠は十分な価値があり、かつ物足りない量に」という原則は正しいのですが、AI アプリではそこに原価の制約が加わります。無料枠を広げるほど、コンバージョンしないユーザーの原価を全額かぶることになるからです。
そこで、無料枠は「回数」ではなく「品質」で区切るようにしました。
切り方
無料ユーザー 1 人あたり月間原価
体験の質
回数制限(例: 1 日 3 回まで、上位モデル使用)
高い(上位モデル単価 × 90 回)
良いが、すぐ壁に当たる
品質制限(例: 回数は緩め、軽量モデル固定)
低い(軽量モデル単価 × 回数)
触り続けられ、差分が課金理由になる
軽量モデルを無料枠に充てると、原価は 1 桁下がります。そのうえで、有料プランでは上位モデルに切り替わる設計にすると、「同じ操作なのに結果が明らかに違う」という体験が課金の理由になります。回数で止められた人は不満を抱えて離れますが、品質差を見た人は納得して支払ってくれることが多い、というのが手元での実感です。
ペイウォールが審査で弾かれない条件
App Store のガイドラインで問題になりやすいのは、次の 2 点です。
基本機能に触れる前にアカウント作成を必須にすること
意味のあるコア機能を一切提供せずにサブスクリプションを強制すること
逆に、次の設計は問題なく通ります。
試用期間後の機能制限(フリーミアム)
AI の使用回数や生成品質によるティア分け
プレミアム機能・コンテンツへのアクセス制限
前述の「品質で区切る」方式は、コア機能そのものは無料で触れるため、この観点でも安全側に寄っています。加えて、アカウント作成をアプリ起動直後ではなく、生成結果を保存しようとしたタイミングまで遅らせると、審査リスクと初回離脱の両方が下がりました。
RevenueCat でペイウォールを実装する
Rork で生成したアプリは Expo ベースなので、RevenueCat の React Native SDK がそのまま使えます。App Store と Google Play の課金処理とレシート検証をまとめて任せられるため、個人開発では自前実装より確実です。
// RevenueCat の基本実装(Expo / React Native)
import Purchases from 'react-native-purchases' ;
await Purchases. configure ({ apiKey: 'YOUR_REVENUECAT_API_KEY' });
const offerings = await Purchases. getOfferings ();
const packages = offerings.current?.availablePackages;
const purchasePackage = async ( pkg ) => {
try {
const { customerInfo } = await Purchases. purchasePackage (pkg);
if (customerInfo.entitlements.active[ 'premium' ]) {
enablePremiumFeatures ();
}
} catch (error) {
if (error.userCancelled) return ; // キャンセルはエラー扱いしない
console. error ( 'Purchase failed:' , error);
}
};
error.userCancelled を素通しにしている点が地味に大事です。ここをエラーとして扱うと、ユーザーが購入シートを閉じただけでエラーダイアログが出て、次の購入意欲まで削いでしまいます。
上限に達したユーザーを、止めずに扱う
無料枠を使い切ったユーザーに「上限に達しました。プレミアムにアップグレードしてください」とだけ返す実装は、書くのは簡単ですが体験としては最悪の部類です。ユーザーは自分の作業を中断させられたと感じます。
代わりに、上限到達後は軽量モデルへフォールバックし、結果は返したうえで差分を提示する形にしました。
// lib/quota.ts — 上限到達時に止めずに品質を落とす
export async function generateWithQuota ( userId : string , prompt : string ) {
const { isPro , usedThisMonth , limit } = await getQuota (userId);
if (isPro) {
return { result: await callAi (userId, 'pro' , prompt), degraded: false };
}
if (usedThisMonth < limit) {
return { result: await callAi (userId, 'pro' , prompt), degraded: false };
}
// 上限到達: 止めずに軽量モデルで返す
const result = await callAi (userId, 'flash' , prompt);
return { result, degraded: true };
}
UI 側では degraded が true のときだけ、結果の下に控えめなバナーを出します。「今回は軽量モードで生成しました。高品質モードに戻す」という一文と、ペイウォールへの導線です。
止めるのではなく落とす。この変更を入れてから、上限到達をきっかけとしたコンバージョンが目に見えて増えました。理由を考えると、上限で止められたユーザーは「使えない」と判断してアプリを閉じますが、品質が落ちた結果を見たユーザーは「戻したい」と考えるからだと思います。前者は離脱、後者は購入意欲です。
検索流入を取りにいく
有料広告に踏み出す前に、App Store 検索から取れる分を取り切るほうが費用対効果は高いです。
アプリ名(30 文字)は、ブランド名に最重要キーワードを 1〜2 個添える形にします。サブタイトル(30 文字)は機能名ではなくベネフィットを書きます。キーワードフィールド(100 文字)は名詞のみ・カンマ区切り・スペースなしが基本で、スペースを入れると 1 文字分を無駄にします。
キーワードの選定では、検索ボリュームが中程度で競合が少ない語を狙います。App Store 内の検索サジェスト、競合アプリのレビュー本文に繰り返し出てくる語、AppFollow や Sensor Tower のようなツールが手がかりになります。AI アプリの場合、「AI」という語そのものは競合が多すぎて取れないので、解決する課題側の語(「背景 削除」「議事録 文字起こし」など)に寄せるほうが現実的でした。
スクリーンショットは、アイコンとアプリ名の次にダウンロード判断を左右します。1 枚目でアプリ最大の価値を伝え、2〜3 枚目で主要機能、4〜5 枚目で Before / After や実績を見せる構成が基本です。全画像にキャプションを入れておくと、プレビューを開かずスクロールするユーザーにも意図が伝わります。
有料獲得に踏み出す前に確認する数字
広告を回す前に計算しておくべきは、AI 原価を差し引いた実質 LTV です。ここを粗利ではなく売上で計算すると、CAC の上限を大きく見誤ります。
月額サブスク: ¥1,200
平均継続期間: 6 か月
売上 LTV = 1,200 × 6 = ¥7,200
ストア手数料 15% を控除 = ¥6,120
AI 原価(1 人 ¥250/月 × 6 か月)を控除 = ¥4,620 ← 実質 LTV
目標 CAC = 実質 LTV ÷ 3 = ¥1,540
広告の 1 クリック単価 ¥200 / クリックからの有料転換率 3% の場合:
CAC = 200 ÷ 0.03 = ¥6,667 → 目標の 4 倍以上で赤字
打ち手は 2 つ:
(a) 転換率を上げる(ペイウォール改善・トライアル導入)
(b) 実質 LTV を上げる(年払い誘導・原価の軽いモデルへ切替)
売上 LTV の ¥7,200 で判断すると目標 CAC は ¥2,400 になり、実際の上限より 1.5 倍以上高く見積もることになります。この差が、広告を回しながらじわじわ資金を減らす典型的な原因でした。
LTV / CAC は最低 3 を目標にします。3 を切る状態で予算を増やすのは、赤字の速度を上げているだけです。
解約を減らすために効いたこと
解約予兆の検知は、前述の使用ログがそのまま使えます。
最終利用日から 7 日以上経過している
週次の利用回数が直近 4 週間で 50% 以上落ちている
コア機能の利用回数だけが選択的に減っている
3 つ目が特に有効でした。アプリ全体は開いているのにコア機能だけ使われなくなっているケースは、他のアプリに乗り換えている途中であることが多く、この段階なら声をかける余地があります。
解約フローに引き止めオファーを差し込むのも効果があります。最終確認の前に「3 か月 50% オフで継続」「1 か月無料」といった選択肢を出すと、解約意向のユーザーの 2〜4 割が留まることがあります。ただし、オファーを繰り返し出しすぎると不誠実な印象になるため、1 ユーザーにつき 1 回までに制限しています。
毎週見る指標を 5 つに絞る
指標は増やすほど見なくなります。手元では次の 5 つだけを毎週確認しています。
指標
意味
閾値の目安
MRR
月次経常収益
前月比プラスを維持
粗利率
(売上 − ストア手数料 − AI 原価)÷ 売上
60% を下回ったら要調査
チャーン率
月内に解約した有料ユーザーの割合
5% 以下
無料 → 有料転換率
無料ユーザーのうち課金した割合
AI アプリなら 5% 前後を目安
ARPU 対原価比
ユーザー 1 人の売上 ÷ 同じ人の AI 原価
4 倍以上
粗利率と ARPU 対原価比の 2 つが、AI アプリならではの指標です。この 2 つが下がり始めたときは、たいてい上位モデルを使う機能を増やしすぎているか、ヘビーユーザーの比率が上がっています。前者なら軽量モデルで足りる処理を戻し、後者ならクレジット制の対象を広げる、という順で対処してきました。
A/B テストで改善するときは、1 回のテストで変える変数を 1 つに絞ります。価格と UI を同時に変えると、どちらが効いたのか永久に分かりません。バリアントあたり 300〜500 ユーザーを確保し、有意性を確認してから採用します。
個人開発で収益化に向き合って思うこと
長く個人でアプリを作ってきて、収益化について考え方が変わった点が 1 つあります。
以前は、収益化を「作り終えた後にやること」だと思っていました。機能を完成させ、リリースし、それから課金を載せる。その順番が自然に感じられたのです。
けれど AI アプリでは、その順番だと手遅れになります。どのモデルをどの機能で使うかという設計判断が、そのまま原価構造を決めてしまうからです。全機能を上位モデルで作り込んでからサブスクを載せると、価格を上げるか機能を削るかの二択しか残りません。どちらもリリース後には痛みを伴います。
だから今は、機能を設計する段階で「この処理は軽量モデルで足りるか」を必ず一度考えるようにしています。地味な習慣ですが、これが後の粗利率を決めていました。
Rork を使うと実装のスピードは確かに上がります。その速さは、余った時間を設計に回せるという意味だと捉えています。作れる速度が上がったぶん、何をどう作るかを考える時間が増えたのは、個人開発にとって大きな変化でした。
次の一手
まだ AI 呼び出しのログを取っていないなら、そこから始めるのが最短です。ラッパー関数を 1 つ作り、ユーザー ID・モデル・トークン数・概算コストを保存する。それだけで、1 週間後には「有料ユーザー 1 人あたりの月間原価」という、料金設計のすべての土台になる数字が手に入ります。
価格表を作るのも、広告を出すのも、その数字が出てからで間に合います。
原価から逆算する考え方が、どこか一箇所でもお役に立てば嬉しく思います。最後までお付き合いいただき、ありがとうございました。