GASでレシート画像をAIで読み取り
経費精算表に自動入力する方法
溜まったレシートを手で打ち込む作業を、Claude APIの画像認識で自動化します。 Googleドライブに保存した写真から利用日・店名・金額・勘定科目を読み取り、 経費精算シートに転記するまでを、GAS(Google Apps Script、Googleサービスを自動化できるスクリプト)だけで実装します。
Table of Contents
レシートの手入力を自動化するメリット
経費精算は、少額のレシートが大量に集まる割に、1枚ずつ日付・店名・金額を目で読んで手入力する地味な作業です。 件数が増えるほど時間を取られ、打ち間違いも起きます。 ここで役立つのが、画像を理解できるAI(Vision対応のClaude API)です。写真を渡すだけで必要な項目を読み取ってくれます。
入力時間の削減
1枚ずつの手打ちをなくし、写真を撮ってドライブに入れるだけにできる
転記ミスの防止
金額の桁ずれや日付の打ち間違いといった人為ミスを減らせる
勘定科目の下書き
店名や品目から勘定科目の候補をAIが提案し、仕訳のたたき台になる
月末の平準化
毎日トリガーで少しずつ処理し、月末にまとめて溜め込まなくて済む
仕組みの全体像
処理の流れはシンプルです。担当者はレシートを撮影して所定のドライブフォルダに入れるだけ。 あとはGASが自動で動きます。
レシートを撮影しドライブに保存
経費を使った人が写真を撮り、決めたフォルダにアップロードする
GASがフォルダの新着画像を取得
トリガーで定期実行し、まだ処理していない画像だけを拾う
Claude APIに画像を渡して読み取り
利用日・店名・金額・勘定科目をJSON形式で受け取る
経費精算シートに転記
読み取った項目と元画像へのリンクを1行として書き込む
使うのはGASの標準機能(DriveApp・UrlFetchApp)とClaude APIだけです。専用のOCRツールやサーバーは要りません。
準備:APIキーの保存と対象フォルダ
まずClaude APIのAPIキーを、コードに直書きせずPropertiesService(スクリプトごとの設定値を保存する仕組み)に登録します。 あわせて、レシート画像を入れるドライブのフォルダIDと、転記先のシート名を定数にしておきます。 フォルダIDはブラウザでフォルダを開いたときのURL末尾の文字列です。
// スクリプトプロパティにAPIキーを保存しておく(コードに直書きしない)
function getApiKey() {
return PropertiesService.getScriptProperties().getProperty("ANTHROPIC_API_KEY");
}
// レシート画像を入れておくGoogleドライブのフォルダID
const RECEIPT_FOLDER_ID = "ここにフォルダIDを貼る";
// 転記先の経費精算シート名
const SHEET_NAME = "経費精算";APIキーの登録は、スクリプトエディタの「プロジェクトの設定 › スクリプト プロパティ」からANTHROPIC_API_KEYという名前で保存します。転記先シートには、あらかじめ「処理日時/利用日/店名/勘定科目/摘要/税込金額/消費税/状態/画像リンク」の見出し行を用意しておきましょう。
レシート画像をAIで読み取るコア関数
中心となるのがこの関数です。ドライブの画像をUtilities.base64Encode(バイナリを文字列に変換する関数)でエンコードし、imageブロックとしてClaude APIに送ります。 返す形をJSONで指定し、勘定科目は候補の中から選ばせるのがポイントです。
// 1枚のレシート画像をClaude APIに渡し、項目をJSONで受け取る
function readReceipt(fileId) {
const blob = DriveApp.getFileById(fileId).getBlob();
const mimeType = blob.getContentType(); // 例: image/jpeg
const base64 = Utilities.base64Encode(blob.getBytes());
const instruction =
"これはレシートまたは領収書の画像です。次の形のJSONだけを返してください。" +
"説明文やコードブロックは付けないでください。" +
'{"date":"YYYY-MM-DD","store":"店名","total":税込合計の数値,' +
'"tax":消費税額の数値,"category":"勘定科目","summary":"品目の要約"}。' +
"categoryは「旅費交通費」「会議費」「消耗品費」「接待交際費」「通信費」" +
"「その他」から必ず1つ選ぶ。読み取れない文字は空文字、金額は0にする。";
const payload = {
model: "claude-haiku-4-5", // 画像1枚の読み取りは軽量モデルで十分
max_tokens: 512,
messages: [
{
role: "user",
content: [
{ type: "image", source: { type: "base64", media_type: mimeType, data: base64 } },
{ type: "text", text: instruction },
],
},
],
};
const res = UrlFetchApp.fetch("https://api.anthropic.com/v1/messages", {
method: "post",
contentType: "application/json",
payload: JSON.stringify(payload),
headers: {
"x-api-key": getApiKey(),
"anthropic-version": "2023-06-01",
},
muteHttpExceptions: true,
});
if (res.getResponseCode() !== 200) {
throw new Error("API error: " + res.getContentText());
}
// content[0].text にJSON文字列が入っているのでパースする
const text = JSON.parse(res.getContentText()).content[0].text;
return JSON.parse(text);
}画像として送れる形式はJPEG・PNG・GIF・WebPです。media_typeにはblob.getContentType()で取得した実際の形式を渡します。 「JSONだけを返す」と明示しても前後に文章が付くことがまれにあるため、次のセクションのように受け取り側でもガードしておくと安定します。
抽出結果を経費精算シートに書き込む
読み取ったJSONをそのままシートの1行に変換します。 経費精算では金額の正確さが命なので、消費税が合計を超えていないかといった簡単な整合チェックを入れ、 あやしい場合は「要確認」の印を付けておきます。あわせて元画像へのリンクを残し、後から人が突き合わせられるようにします。
// 読み取った項目を経費精算シートの末尾に1行追記する
function appendExpenseRow(sheet, data, fileUrl) {
const totalCheck = ok(data.total, data.tax); // 金額の簡易チェック
sheet.appendRow([
new Date(), // 処理日時
data.date, // 利用日
data.store, // 店名
data.category, // 勘定科目(AIの提案)
data.summary, // 摘要
data.total, // 税込金額
data.tax, // うち消費税
totalCheck ? "" : "要確認", // 金額が不自然なら印を付ける
fileUrl, // 元画像へのリンク
]);
}
// 税額が合計を超えていない等、明らかにおかしくないかだけ見る
function ok(total, tax) {
return typeof total === "number" && total > 0 && tax >= 0 && tax <= total;
}このok()は「明らかにおかしい値」をふるい落とすだけの軽いチェックです。 軽減税率(8%)と標準税率(10%)が混在するレシートなど、税額の細かい検算までは行いません。 最終的な金額の確定は人の確認を挟む前提にしておくのが安全です。
二重登録を防ぐメイン関数とトリガー
フォルダを定期的に見に行くと、同じ画像を何度も処理してしまいがちです。 そこで、すでにシートに書き込んだ画像リンクをSetに集め、未処理の画像だけを対象にします。1回の実行は枚数を区切り、実行時間制限に余裕を持たせます。
// フォルダ内の未処理レシートをまとめて処理するメイン関数
function processReceipts() {
const sheet = SpreadsheetApp.getActive().getSheetByName(SHEET_NAME);
const folder = DriveApp.getFolderById(RECEIPT_FOLDER_ID);
// すでに記録済みの画像リンクを集め、二重登録を防ぐ(画像リンクはI列 = index 8)
const done = new Set(
sheet.getDataRange().getValues().map((row) => row[8])
);
const files = folder.getFiles();
let count = 0;
while (files.hasNext()) {
const file = files.next();
const url = file.getUrl();
if (done.has(url)) continue; // 処理済みはスキップ
if (count >= 30) break; // 1回の実行は30枚までに区切る
try {
const data = readReceipt(file.getId());
appendExpenseRow(sheet, data, url);
done.add(url);
count++;
Utilities.sleep(1000); // 連続呼び出しの間隔を空ける
} catch (e) {
Logger.log(file.getName() + " の処理に失敗: " + e.message);
}
}
Logger.log(count + " 件のレシートを処理しました");
}あとは、このメイン関数を毎朝自動で動かすトリガーを登録します。createDailyTriggerは一度実行すればOKです。同名トリガーを先に消してから作ることで、二重登録を防いでいます。
// 毎朝7時に自動でレシートを取り込むトリガーを1回だけ登録する
function createDailyTrigger() {
// 二重登録を防ぐため既存の同名トリガーを削除
ScriptApp.getProjectTriggers()
.filter((t) => t.getHandlerFunction() === "processReceipts")
.forEach((t) => ScriptApp.deleteTrigger(t));
ScriptApp.newTrigger("processReceipts")
.timeBased()
.everyDays(1)
.atHour(7)
.create();
}実務での注意点
1. AIの読み取りは100%ではない
手ブレ・影・感熱紙の退色などで金額や日付を読み違えることがあります。 書き込みは「未確認」の状態にとどめ、担当者が画像と突き合わせて承認するフローにしましょう。 金額のように誤りが致命的になる項目こそ、人の確認を残すことが重要です。
2. 画像形式とサイズに気をつける
iPhone標準のHEICは送れません。JPEG/PNGで撮る設定にするか、変換してから保存してください。 極端に大きい画像はリクエストが重くなるため、長辺1500px程度に縮小すると速度とコストのバランスが良くなります。
3. 枚数が多いときはバッチ処理を検討
画像1枚ごとにAPIを呼ぶため、大量に溜まる運用では時間もコストもかさみます。 1回の処理枚数を区切って次回トリガーに引き継ぐ設計にし、必要ならまとめて投げるMessage Batches APIの利用も検討してください。
4. 機密情報の取り扱いを社内ルールで確認する
レシートには利用店舗や金額など業務情報が含まれます。外部APIに画像を送る前に、 社内の情報取り扱い方針に沿っているかを確認し、対象フォルダの共有範囲も絞っておきましょう。
まとめ
レシートの写真をBase64でClaude APIに渡し、JSONで受け取ってシートに転記する——この一連をGASでつなぐだけで、 経費精算の手入力はほぼ自動化できます。ポイントは、勘定科目を候補から選ばせること、 画像リンクで二重登録を防ぐこと、そして金額は人の確認を残すことです。 請求書PDFや名刺の読み取りにも同じ考え方が応用でき、日々の入力業務を大きく軽くできます。
よくある質問
Googleドライブに保存したレシート画像とClaude APIのAPIキーがあれば始められます。GAS側でDriveApp.getFileByIdで画像を取得し、Utilities.base64Encodeでエンコードしてから、UrlFetchAppでClaude APIのmessagesエンドポイントに画像として送るだけです。専用のOCRサービスやサーバーは不要で、GASとスプレッドシートだけで完結します。
そのままでは送れません。Claude APIが画像として受け取れるのはJPEG・PNG・GIF・WebPです。HEICで撮影される設定の場合は、あらかじめJPEGで撮影する設定に変えるか、アップロード時にJPEG/PNGへ変換してからドライブに保存してください。blob.getContentType()で形式を確認し、対応外なら処理をスキップするガードを入れておくと安全です。
あります。手ブレや影、しわの寄ったレシートでは金額や日付を読み違えることがあります。経費精算は金額の正確さが重要なので、AIの出力をそのまま確定させず「未確認」の状態で書き込み、担当者が画像リンクと突き合わせて承認する運用がおすすめです。合計金額と消費税額の整合をコード側で軽くチェックするのも有効です。
1回の実行につき処理する枚数を区切り、続きは次回のトリガーに任せる設計にすると、GASの6分制限を回避できます。画像1枚ごとにAPIを呼ぶと時間がかかるため、大量に溜まる運用ではまとめて投げるMessage Batches APIの利用も検討してください。
できます。プロンプトで「旅費交通費」「会議費」「消耗品費」など自社で使う勘定科目の候補を渡し、その中から選ばせることで、店名や品目に応じた科目を提案させられます。ただし最終的な仕訳判断は経理担当者が確認する前提にし、AIの提案はあくまで下書きとして扱うのが安全です。
AI×GASで入力業務を
自動化する。
レシート・請求書・名刺などの読み取りから、経費精算や仕訳の自動化まで、 Claude APIとGASを組み合わせた業務自動化をご相談いただけます。