~$ avb/blog

Build a Solana Portfolio Tracker with Google Apps Script and Replit

Graphic showing Replit and Google logos shaking hands for an automated Google Apps Script workflow

Introduction

If you track multiple speculative Solana tokens—memecoins, early-stage projects, narrative plays—you've felt this pain:

  • CoinMarketCap and Co. are great for browsing, not so great for monitoring. You're stuck refreshing tabs manually.

  • SaaS portfolio trackers cost money, and most don't cover low-cap Solana tokens.

  • Spreadsheets are flexible but dumb. A basic Google Sheet can't alert you when a token crosses the $1M market cap at 2 AM.

  • Writing a backend is overkill. A price alert shouldn't require a server, database, scheduler, and deployment pipeline.

The result: most DIY crypto investors either overpay for tools they barely use or miss opportunities because they're not glued to a screen.

I built a solution with Replit and Google Apps Script. Everything runs inside Google Sheets.

What I Built

Google Sheets interface displaying a custom Google Apps Script menu with automated script options

The Jupiter Crypto Portfolio Tracker is a Google Sheets add-on built with Google Apps Script for automation that:

  • Fetches live metrics for any Solana token—price, market cap, liquidity, holder count, and Jupiter's "organic score"—directly from the Jupiter API

  • Tracks multiple portfolios simultaneously: a main portfolio, a GTA6 thematic basket, and a Watchlist

  • Logs a daily snapshot of every token to a history tab, building a time series you can chart or analyze

  • Sends hourly or daily alerts that check price targets and percentage moves, then emails you when conditions are met

  • Creates recurring ±10% price alerts automatically for any token added to the Watchlist tab

  • Sends a weekly digest every Friday evening: a performance table, top gainers and losers, holder milestones, and an AI-written summary from Gemini

  • Delivers a dark-themed, CoinMarketCap-style UI inside Google Sheets, with modal dialogs for setting alerts and a sidebar for managing them—no separate app needed

Zero cost. Lives entirely in tools you already have.

The Stack

Layer

Technology

Role

Data source

Jupiter Lite API

Real-time Solana token metrics

Compute

Google Apps Script (V8 runtime)

All logic, scheduling, email

Storage

Google Sheets tabs

Portfolios, history, alerts, settings

UI

HTML Service (GAS)

In-sheet modal dialogs + sidebar

Email

MailApp (GAS built-in)

Alert emails + weekly digest

AI narrative

Gemini API

Weekly digest analyst commentary

Dev environment

Replit

Editor, version control, AI pair programming

Deployment bridge

clasp (Google's CLI)

Sync code from Replit → Google Apps Script

Secrets

PropertiesService (GAS)

Secure API key storage (never in code)

The Replit + Google Apps Script Workflow

This is what makes the project worth studying for Replit developers and Google Sheets power users alike.

The problem with the default GAS (=Google Apps Script) editor: Google's built-in Apps Script editor is functional but primitive. No Git, no real autocomplete, no multi-file workflow, no AI assistance, no proper terminal. For anything beyond a toy script, it becomes painful fast.

The solution: use Replit as your development environment and push code to GAS via clasp.

Step 1 — Project Setup in Replit

Create the project in Replit and install clasp as a dev dependency:

npm install --save-dev @google/clasp

The package.json defines two scripts:

"scripts": {
 "push": "bash scripts/push.sh",
 "pull": "bash scripts/pull.sh"
}

npm run push deploys local code to the live Google Apps Script project. npm run pull syncs changes made in the GAS editor back to Replit.

Step 2 — Authentication with CLASPRC

clasp authenticates via a credential file (.clasprc.json). Rather than committing it to the repo, store it as a Replit Secret (CLASPRC_JSON). The push script writes the secret to disk at runtime, executes the push, then deletes it—keeping credentials out of the codebase entirely.

Step 3 — Code, Iterate, Push

The development loop:

  1. Edit code.js, AlertDialog.html, or ManageAlerts.html in Replit's editor with AI pair programming available

  2. Run npm run push in the Replit shell

  3. Open the Google Sheet, reload, and test via the custom menu

  4. Repeat.

Replit as a Google Apps Script IDE is an underexplored workflow almost no one has documented.

Step 4 — Trigger Installation

Once the code is live in GAS, running ⚙️ Set Up Sheets (first run) from the custom menu creates all necessary tabs. Then ⏰ Install Triggers wires up three automated jobs:

  • Daily at 1 PM — snapshots every portfolio and runs alert checks

  • Every hour — evaluates all active alerts without logging history

  • Weekly (configurable day/hour) — sends the Gemini-powered digest email

These triggers run on Google's infrastructure indefinitely, at no cost, with no server.

Feature Deep-Dive

Jupiter API Integration

Every token is identified by its Solana mint address. The script calls Jupiter's search endpoint:

https://lite-api.jup.ag/tokens/v2/search?query=<mint_address>

This returns price, market cap, liquidity, holder count, and organic score in a single call. A 2-attempt retry with 2-second backoff handles transient API errors—if a token isn't found, it logs an error rather than crashing the run.

Multi-Portfolio Architecture

Three portfolio types ship out of the box, each as a named Google Sheet tab:

  • Main portfolio — primary holdings with a full History Log

  • GTA6 Tracker — a separate thematic basket with its own history

  • Watchlist — metrics-only monitoring (no history log) with auto-alert generation

Adding a portfolio requires one object in the PORTFOLIOS array. The generic logPortfolioHistory_() function handles all of them without duplication.

The Data-Driven Alert Engine

This was the most significant engineering decision in the project.

The original design used hardcoded if/else blocks—fixed thresholds, specific price targets per token. Every change required editing and redeploying code.

The rebuilt engine uses a hidden "Alerts" sheet as its database. Each alert has 14 columns:

ID · Token · Mint · Metric · Condition · Target · Recurring flag · Cooldown hours · Window hours · Status · Last fired time · Baseline value · Baseline time · Created date

This means:

  • Create, edit, pause, resume, and delete alerts without touching code

  • Four condition types: above, below, pct_increase, pct_decrease

  • Recurring alerts re-arm automatically after a configurable cooldown

  • One-time alerts flip to FIRED after triggering

Concurrency is managed by LockService. If two hourly triggers overlap, only one runs the alert checks—no duplicate emails.

Single-fetch optimization: Each alert check fetches a token's metrics once from the Jupiter API, then shares those metrics across every alert for that token. API calls scale with unique tokens, not total alerts.

Rolling Baseline for Percentage Alerts

A "+10% in 24 hours" alert sounds simple but "baseline" is ambiguous when the trigger runs hourly.

The solution: each percentage alert stores a baselineValue and baselineTime in the Alerts sheet. On every check:

  1. If no baseline exists, record the current value and skip (seeding the window)

  2. If the baseline is older than the configured window (e.g., 24 hours), reset it to the current value

  3. Otherwise, compute the change from the stored baseline and fire if the threshold is crossed

When a recurring percentage alert fires, the baseline resets to that point so the next +10% move measures from the new position, not the original seed.

Watchlist Auto-Alerts

Drop a token name and mint address into the Watchlist tab. On the next hourly run, syncWatchlistDefaultAlerts_() checks the tab and creates ±10% recurring price alert pairs for any token missing them. The threshold is configurable in the Settings sheet.

Gemini AI Weekly Digest

Every Friday morning (configurable), the script:

  1. Reads 7 days of history from all portfolio log sheets

  2. Calculates per token: price change %, market cap change %, holder count delta, organic score delta, and milestones crossed (1K, 5K, 10K, 25K, 50K, 100K holders)

  3. Identifies the top 3 gainers and top 3 losers by 7-day price performance

  4. Pulls fired alerts from the Alert History sheet

  5. Assembles everything into a prompt and calls the Gemini API

  6. Gemini writes a 2–3 paragraph analyst commentary: biggest movers, divergences (price up + holders down = possible distribution; holders up + price flat = accumulation), risks, and what to watch next week

  7. Sends a dark-themed HTML email with a performance table, movers section, milestones, alert recap, and the AI narrative

The Gemini API key lives in GAS PropertiesService script properties—never in code or the spreadsheet.

Dark mode email preview of a Google Apps Script automated portfolio digest showing AI crypto analysis

Custom Sheets UI

Google Apps Script for Google Sheets embeds two HTML interfaces directly into the spreadsheet:

Alert Dialog (modal): A dark-themed popup with two tabs—"Alert by Price" (absolute target) and "Alert by %" (percentage move with direction toggle). It displays the token's current live value, infers whether the alert is above or below that value, and submits directly to the Apps Script backend.

Dark mode custom HTML popup interface built with Google Apps Script to set percentage price alerts

Manage Alerts (sidebar): A panel listing all alerts with status badges (ACTIVE, PAUSED, FIRED), a condition summary, last fired time, and action buttons: pause, resume, edit target, and delete. Editing opens an inline input without navigating away.

Manage Alerts custom modal window built with Google Apps Script

Problems Solved Along the Way

Problem

Solution

GAS editor has no version control or AI assistance

Replit as dev environment + clasp for deployment

clasp credentials can't live in the repo

Stored as Replit Secret, written to disk at push time, then deleted

Hardcoded alert logic required code changes for every new alert

Rebuilt as a data-driven engine using a hidden Alerts sheet

Percentage alerts needed a meaningful "window" baseline

Rolling baselineValue + baselineTime with per-alert window expiry

Overlapping hourly triggers could fire duplicate emails

LockService.getScriptLock() with 30-second timeout

API calls multiplying with alert count

Single-fetch-per-token cache within each check run

Old hardcoded alerts needed migration to the new engine

migrateLegacyAlerts() one-time migration function in the menu

Gemini API key needed to stay secure

GAS PropertiesService script properties

Adding a new portfolio duplicated function logic

Generic logPortfolioHistory_() + PORTFOLIOS config array

What This Shows About Replit

This project makes a strong case for Replit as a professional development environment, not just a learning platform:

  • Replit as a clasp/GAS IDE: File management, a real terminal, npm, and AI pair programming for a platform that has almost no native tooling. This workflow is underexplored and undervalued.

  • Replit Secrets for credential management: The CLASPRC_JSON secret keeps Google OAuth credentials out of the codebase. The same pattern applies to any sensitive config.

  • Replit AI accelerated the hardest parts: The alert engine refactor, rolling baseline logic, and LockService concurrency pattern. These non-obvious design choices would otherwise cost hours on Stack Overflow.

  • Zero infrastructure footprint: The app runs entirely on Google's infrastructure (Apps Script + Gmail + Sheets). Replit is purely the development environment—no hosting, no server, no deployment target beyond npm run push. Using Google Apps Script SpreadsheetApp as the data layer means there's nothing to provision, maintain, or pay for.

Who This Is For

  • Solana degens and token hunters tracking 10–30 positions across thematic baskets

  • Developer-curious traders comfortable with spreadsheets who want automation without writing a backend

  • Anyone paying for a SaaS portfolio tracker who wants a free, self-owned alternative they fully control

  • Makers and indie developers who want a real-world example of Replit + Google Apps Script as a production workflow