Claude-code-plugins-plus-skills appfolio-cost-tuning

install
source · Clone the upstream repo
git clone https://github.com/jeremylongshore/claude-code-plugins-plus-skills
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/jeremylongshore/claude-code-plugins-plus-skills "$T" && mkdir -p ~/.claude/skills && cp -r "$T/plugins/saas-packs/appfolio-pack/skills/appfolio-cost-tuning" ~/.claude/skills/jeremylongshore-claude-code-plugins-plus-skills-appfolio-cost-tuning && rm -rf "$T"
manifest: plugins/saas-packs/appfolio-pack/skills/appfolio-cost-tuning/SKILL.md
source content

AppFolio Cost Tuning

Overview

AppFolio Stack API pricing is partner-agreement based, with costs scaling by API call volume per managed property. Property management portfolios generate high-frequency reads for tenant lookups, lease status checks, and maintenance requests. Each redundant API call erodes margin on per-unit revenue. Optimizing call patterns directly impacts operational profitability, especially for portfolios managing hundreds or thousands of units where even small per-call costs compound rapidly.

Cost Breakdown

ComponentCost DriverOptimization
Property/unit readsPer-call pricing on tenant and unit endpointsCache with 10-15 min TTL; property data changes infrequently
Lease operationsBulk lease queries across entire portfolioFetch all leases once, filter locally instead of per-unit calls
Maintenance requestsPolling for new work ordersUse webhooks to receive push notifications
Reporting exportsLarge payload downloads for financial reportsSchedule off-peak, cache results for 24h
Vendor/owner lookupsRepeated lookups for the same contactsBuild a local lookup table, refresh daily

API Call Reduction

class AppFolioCache {
  private cache = new Map<string, { data: any; expiry: number }>();

  get(key: string): any | null {
    const entry = this.cache.get(key);
    if (!entry || Date.now() > entry.expiry) return null;
    return entry.data;
  }

  set(key: string, data: any, ttlMs = 600_000): void {
    this.cache.set(key, { data, expiry: Date.now() + ttlMs });
  }

  async fetchWithCache(endpoint: string, ttlMs?: number): Promise<any> {
    const cached = this.get(endpoint);
    if (cached) return cached;
    const response = await fetch(endpoint);
    const data = await response.json();
    this.set(endpoint, data, ttlMs);
    return data;
  }
}

Usage Monitoring

class AppFolioUsageMonitor {
  private calls: Array<{ endpoint: string; timestamp: number }> = [];
  private budgetLimit = 10_000; // daily call budget

  record(endpoint: string): void {
    this.calls.push({ endpoint, timestamp: Date.now() });
    const todayCalls = this.getTodayCount();
    if (todayCalls > this.budgetLimit * 0.8) {
      console.warn(`AppFolio API budget 80% consumed: ${todayCalls}/${this.budgetLimit}`);
    }
  }

  getTodayCount(): number {
    const startOfDay = new Date().setHours(0, 0, 0, 0);
    return this.calls.filter(c => c.timestamp > startOfDay).length;
  }
}

Cost Optimization Checklist

  • Cache property and unit data with 10-15 min TTL
  • Replace polling loops with webhook-driven event handling
  • Batch lease queries — fetch all, filter locally
  • Use incremental sync with
    modified_since
    parameter
  • Schedule report exports during off-peak hours
  • Build local lookup tables for vendors and owners
  • Set daily API call budget alerts at 80% threshold
  • Audit unused integrations consuming API quota

Error Handling

IssueCauseFix
429 Too Many RequestsExceeded rate limitImplement exponential backoff with jitter
Stale cache serving old dataTTL too long for volatile dataReduce TTL for maintenance/lease endpoints to 2-5 min
Budget alerts firing dailyPolling loop running on short intervalSwitch to webhook-driven architecture
Duplicate API callsMultiple services fetching same dataCentralize through shared cache layer
Large payload timeoutsFetching full portfolio in single callPaginate requests, process in batches of 100

Resources

Next Steps

See

appfolio-performance-tuning
.