Skip to main content

Base URL

All requests use your deployed Web App URL:
https://script.google.com/macros/s/[DEPLOYMENT_ID]/exec

Endpoints Overview

MethodActionDescription
GET-Retrieve balance, expenses, and settings
POSTprocess_textAI-powered transaction processing or Q&A
POSTaddManually add a transaction
POSTdeleteDelete a transaction
POSTsave_settingsSave user settings

GET Request

Retrieve current balance, recent expenses, budget, and privacy settings.

Implementation

google_script.js:41-74
function doGet() {
    const sheet = getTargetSheet();
    const data = sheet.getDataRange().getValues();
    const balance = calculateBalance(sheet);
    const budget = SCRIPT_PROP.getProperty("DAILY_BUDGET") || 350;
    const privacyRaw = SCRIPT_PROP.getProperty("PRIVACY_MODE");
    const privacy = privacyRaw === null ? true : (privacyRaw === "true");

    const expenses = [];
    const startRow = Math.max(1, data.length - 50);
    for (let i = startRow; i < data.length; i++) {
        if (data[i][0]) {
            expenses.push({
                date: data[i][0],
                description: data[i][1],
                amount: data[i][2],
                category: data[i][3],
                type: data[i][4]
            });
        }
    }

    return ContentService.createTextOutput(JSON.stringify({
        balance: balance,
        expenses: expenses,
        budget: budget,
        privacy: privacy
    })).setMimeType(ContentService.MimeType.JSON);
}

Request

curl https://script.google.com/macros/s/YOUR_DEPLOYMENT_ID/exec

Response

balance
number
Current account balance calculated from all transactions
expenses
array
Array of the most recent 50 transactions
budget
string
Daily budget setting (defaults to “350”)
privacy
boolean
Privacy mode status (defaults to true)

Example Response

{
  "balance": 15420.50,
  "expenses": [
    {
      "date": "2026-03-05T10:30:00.000Z",
      "description": "Coffee at Starbucks",
      "amount": 4.5,
      "category": "Food",
      "type": "DEBIT"
    },
    {
      "date": "2026-03-04T15:20:00.000Z",
      "description": "Salary",
      "amount": 5000,
      "category": "Income",
      "type": "CREDIT"
    }
  ],
  "budget": "350",
  "privacy": true
}

POST Request

All POST requests require a JSON body with an action field.

Action: process_text

Use AI (Gemini) to process natural language for adding transactions or answering financial questions.

Implementation

google_script.js:122-208
if (body.action === "process_text") {
    const userText = body.text;
    const mode = body.mode; // 'add' or 'ask'
    
    // Fetches last 40 transactions for context
    const historyData = sheet.getRange(
        Math.max(1, sheet.getLastRow() - 40), 1, 41, 4
    ).getValues();
    
    // Sends to Gemini Flash API with context
    const aiUrl = `https://generativelanguage.googleapis.com/v1beta/models/gemini-flash-latest:generateContent?key=${geminiKey}`;
    
    // Returns parsed transaction or answer
}

Request Body

action
string
required
Must be "process_text"
text
string
required
Natural language input from user
mode
string
required
Processing mode:
  • "add": Extract and save transaction
  • "ask": Answer financial question

Request Examples

curl -X POST https://script.google.com/macros/s/YOUR_DEPLOYMENT_ID/exec \
  -H "Content-Type: application/json" \
  -d '{
    "action": "process_text",
    "text": "Spent 45 bucks on groceries",
    "mode": "add"
  }'

Response (mode: “add”)

status
string
"Success" or "Error"
balance
number
Updated account balance after adding transaction
parsed
object
The extracted transaction details
ai_response
string
User-facing message with insight
Example Response (add)
{
  "status": "Success",
  "balance": 15416.00,
  "parsed": {
    "date": "2026-03-05T14:25:00.000Z",
    "description": "Coffee",
    "amount": 4.5,
    "category": "Food",
    "type": "DEBIT",
    "insight": "Third coffee this week - maybe time for a home brew?"
  },
  "ai_response": "Saved. Third coffee this week - maybe time for a home brew?"
}

Response (mode: “ask”)

status
string
"Success" or "Error"
ai_response
string
AI-generated answer to the question (max 30 words)
Example Response (ask)
{
  "status": "Success",
  "ai_response": "You spent $127.50 on food this week across 8 transactions."
}

Error Response

{
  "status": "Error",
  "message": "AI Failed: Invalid API key"
}

Action: add

Manually add a transaction without AI processing (legacy/direct method).

Implementation

google_script.js:106-119
if (body.action === "add") {
    const date = new Date();
    const type = (body.type === 'income' || body.category === 'Income') 
        ? 'CREDIT' : 'DEBIT';
    sheet.appendRow([date, body.description, body.amount, body.category, type]);
    const newBalance = calculateBalance(sheet);
    
    return ContentService.createTextOutput(JSON.stringify({
        status: "Success",
        balance: newBalance,
        parsed: { date, description: body.description, amount: body.amount, category: body.category, type }
    })).setMimeType(ContentService.MimeType.JSON);
}

Request Body

action
string
required
Must be "add"
description
string
required
Transaction description
amount
number
required
Transaction amount (positive number)
category
string
required
Category: Food, Transport, Tech, Invest, Income, or Misc
type
string
default:"DEBIT"
Transaction type: "income" for CREDIT, anything else for DEBIT

Request Examples

curl -X POST https://script.google.com/macros/s/YOUR_DEPLOYMENT_ID/exec \
  -H "Content-Type: application/json" \
  -d '{
    "action": "add",
    "description": "Lunch at Chipotle",
    "amount": 12.50,
    "category": "Food"
  }'

Response

status
string
"Success"
balance
number
Updated account balance after adding transaction
parsed
object
Echo of the added transaction
Example Response
{
  "status": "Success",
  "balance": 15403.50,
  "parsed": {
    "date": "2026-03-05T14:30:00.000Z",
    "description": "Lunch at Chipotle",
    "amount": 12.50,
    "category": "Food",
    "type": "DEBIT"
  }
}

Action: delete

Delete a transaction by matching description and amount.

Implementation

google_script.js:90-103
if (body.action === "delete") {
    const data = sheet.getDataRange().getValues();
    // Search from bottom to top (most recent first)
    for (let i = data.length - 1; i >= 0; i--) {
        if (data[i][1] === body.description && data[i][2] == body.amount) {
            sheet.deleteRow(i + 1);
            const newBalance = calculateBalance(sheet);
            return ContentService.createTextOutput(JSON.stringify({
                status: "Deleted",
                balance: newBalance
            })).setMimeType(ContentService.MimeType.JSON);
        }
    }
    return ContentService.createTextOutput(JSON.stringify({ 
        status: "Not Found" 
    })).setMimeType(ContentService.MimeType.JSON);
}

Request Body

action
string
required
Must be "delete"
description
string
required
Exact description of transaction to delete
amount
number
required
Exact amount of transaction to delete
The script searches from the most recent transaction backwards and deletes the first match found.

Request Examples

curl -X POST https://script.google.com/macros/s/YOUR_DEPLOYMENT_ID/exec \
  -H "Content-Type: application/json" \
  -d '{
    "action": "delete",
    "description": "Coffee at Starbucks",
    "amount": 4.5
  }'

Response (Success)

status
string
"Deleted"
balance
number
Updated account balance after deletion
{
  "status": "Deleted",
  "balance": 15420.50
}

Response (Not Found)

status
string
"Not Found"
{
  "status": "Not Found"
}

Action: save_settings

Save user settings to Script Properties.

Implementation

google_script.js:82-87
if (body.action === "save_settings") {
    SCRIPT_PROP.setProperty("DAILY_BUDGET", body.budget.toString());
    SCRIPT_PROP.setProperty("PRIVACY_MODE", body.privacy.toString());
    if (body.geminiKey) SCRIPT_PROP.setProperty("GEMINI_KEY", body.geminiKey);
    return ContentService.createTextOutput(JSON.stringify({ 
        status: "Saved" 
    })).setMimeType(ContentService.MimeType.JSON);
}

Request Body

action
string
required
Must be "save_settings"
budget
number
required
Daily budget amount
privacy
boolean
required
Privacy mode setting
geminiKey
string
Gemini API key (optional, only if updating)

Request Examples

curl -X POST https://script.google.com/macros/s/YOUR_DEPLOYMENT_ID/exec \
  -H "Content-Type: application/json" \
  -d '{
    "action": "save_settings",
    "budget": 500,
    "privacy": false,
    "geminiKey": "AIzaSy..."
  }'

Response

status
string
"Saved"
{
  "status": "Saved"
}

Helper Functions

calculateBalance()

Calculates the current balance by iterating through all transactions.
google_script.js:22-39
function calculateBalance(sheet) {
    const data = sheet.getDataRange().getValues();
    let bal = 0;
    // Start from row 1 to skip header
    for (let i = 1; i < data.length; i++) {
        const amount = parseFloat(data[i][2]); // Column C
        const type = data[i][4]; // Column E
        
        if (!isNaN(amount)) {
            if (type === 'CREDIT') {
                bal += amount;
            } else {
                bal -= amount; // DEBIT or blank
            }
        }
    }
    return bal;
}
This function is called after every add/delete operation to ensure balance accuracy.

getTargetSheet()

Retrieve the first sheet from the configured Google Sheet.
google_script.js:16-19
function getTargetSheet() {
    const ss = SpreadsheetApp.openById(SHEET_ID);
    return ss.getSheets()[0];
}

AI Prompts

The process_text action uses different prompts based on mode:

Add Mode Prompt

google_script.js:131-152
You are a financial assistant. 
CONTEXT: The user wants to ADD a transaction.
USER INPUT: "{userText}"
HISTORY: {last 40 transactions}

TASK:
1. Extract: Description, Amount, Category (Food, Transport, Tech, Invest, Income, Misc).
2. Analyze: Compare this new expense to the HISTORY. Is it higher than usual? Is it a repeat?
3. Insight: Write a very short, witty, or helpful insight (max 15 words) about this specific spend.

OUTPUT JSON STRICTLY:
{
  "description": "String",
  "amount": Number,
  "category": "String",
  "type": "DEBIT" or "CREDIT",
  "insight": "String"
}

Ask Mode Prompt

google_script.js:154-168
You are a financial analyst.
CONTEXT: The user is ASKING a question about their finances.
USER INPUT: "{userText}"
HISTORY: {last 40 transactions}

TASK: 
Answer the user's question based strictly on the HISTORY data provided. 
Be concise (max 30 words).

OUTPUT JSON STRICTLY:
{
  "answer": "String"
}

Rate Limits & Quotas

Google Apps Script Limits

  • URL Fetch calls per day: 20,000 (for Gemini API calls)
  • Script runtime: 6 minutes per execution
  • Triggers: 90 minutes of total runtime per day

Gemini API Limits

Depends on your API tier. Free tier typically includes:
  • 15 requests per minute
  • 1 million tokens per minute
  • 1,500 requests per day
See Gemini API Quotas for current limits.

Error Handling

All endpoints return JSON responses. Common error patterns:
{
  "status": "Error",
  "message": "Description of error"
}

Common Errors

Error MessageCauseSolution
AI Failed: Invalid API keyInvalid or missing GEMINI_KEYCheck Script Properties
AI Failed: Quota exceededGemini API rate limit hitWait or upgrade API tier
Not FoundDelete action didn’t find matchVerify description/amount
Script function not foundScript not properly deployedRedeploy as Web App

Next Steps

Setup Guide

Complete deployment instructions

Frontend Integration

Connect your app to the API

Build docs developers (and LLMs) love