AppFolio API usage: 30 days, and the cut plan

AppFolio's Reports API team flagged Western Slope's account for call volume on 2026-10-27. It is our sync. This page shows every call we made for 30 days, per customer and per report, and proposes a cut of about two thirds with no visible change for renters or staff.

2026-10-28 morning · three changes live in production since ~1–3 AM Denver; measured below · Proposed — pending review

The headline. We make about 6,500 calls a day per customer, flat around the clock. AppFolio saw the tenant directory at ~250 a day at Western Slope; we measured 239. Two thirds of all calls come from three reports we pull every minute: guest cards, work orders and rental applications. The proposed plan keeps guest cards and work orders fast during the day, slows everything overnight, pulls the tenant directory once a day, and lands at about 2,200 a day (−66%).

1. What we pull today, and what it would become

One row per report, per customer. Measured = Western Slope's 30-day average per day. "Day" = 7 AM to 9 PM property-local; "night" = 9 PM to 7 AM. Today nothing slows down at night: 41% of calls land between 9 PM and 7 AM.

ReportWhat it feedsTodayMeasured / dayProposed (day / night)After / day
Guest cardsNew leads Clara answersevery 1 min1,3791 min / 5 min960
Work ordersMaintenance intake and statusevery 1 min1,3041 min / 5 min960
Rental applicationsApplication status, co-signer linkingevery 1 min1,3955 min288
Rent rollMove-ins and move-outs, balancesevery 5 min34215 min / 1 h66
Unit vacancyAdvertised rent Clara quotes; Camellia's availability sourceevery 5 min2425 min where a property uses it (Camellia) / hourly elsewhere24 to 288
Tenant directoryResidents' phone, email, co-signers, future move-insevery 5 min239once a day (see section 7: needs a stored copy first)1
Lease dates (tenant tickler)Renewals, lease conversionsevery 5 min + hourly history466hourly24
Balances (3 reports)What a resident owesevery 5 min each1,038hourly / once48
Hourly and daily jobsRenewal offers, charges, payments, bookshourly / daily~110unchanged~110
Income statement (nightly metrics job)30-day income and spend figure per buildingnightly, ~1 per building76 (corrected: 343 was log lines, most of them budget waits)weekly or from stored charges~11

Measured = count of the sync Lambda's "GET …/reports/<report>.json" log lines for westernslopepm. One-minute jobs read below 1,440 because throttled ticks are skipped. The web app's hourly crons are not in these logs; by design they add at most a few hundred a day (estimate).

2. Totals per customer, before and after
CustomerCalls / day today (30-day avg)Per unit per dayAfter plan ACut
Western Slope (234 units)6,471 sync + 76 metrics = 6,54728~2,200−66%
JP&Co (Camellia, Yale, Willows)6,680—~2,440−63%
Situs Group (connected 17 of 30 days)5,292 sync + 56 metrics—~2,200−59%
3. Other findings from the 30 days
4. Options and recommendation

A. Slow the intervals and sleep at night (recommended). The table above. Three small changes: an off-hours rule per speed class in the sync Lambda, the tenant directory moved to a daily class, the metrics pull moved to monthly. No new mechanism. About −64%. Trade-off: a new resident's phone and email can be up to a day late in our records; a move-in shows up to 15 minutes later (AppFolio's own report lag is already 8 to 28 minutes).

B. A, plus re-pull the directory only when the rent roll shows a new tenant. Closes the one-day contact gap with one more trigger. Fede on Oct 1: "eventually consistent, don't over-engineer." Take B only if the gap bites.

C. A, with guest cards and work orders every 2 minutes in the day. About −76%. Trade-off: a lead's first reply can be up to 2 minutes later than today.

This is a fix to live behavior: no switch, live everywhere on merge, proven as below. The only owner step is picking A, B or C.

5. How we prove it worked
  1. Day after deploy: re-run the same per-report count on all three databases; every row at or under its "after" number, and real 429 answers at zero.
  2. Lead speed unchanged: time from a guest card appearing in AppFolio to Clara's first reply at Western Slope, the week before versus the week after, read from the conversation records.
  3. A real move-in at Camellia or Western Slope shows in PropFlow within 15 minutes of the rent roll; the resident's phone and email arrive on the next daily pull.
6. For Jason's call tomorrow
7. Second pass (later that night): what actually reads each report, and how residents reach us

Clara never asks AppFolio live when a resident texts or calls. Balance, lease end, work-order status and who-is-this all come from our copy. The balance answer already treats anything under 24 hours old as current. Lease end comes from the rent roll, not the lease-dates report. Unit vacancy feeds the advertised rent Clara quotes to prospects at every property and is Camellia's availability source since Oct 2, so it stays, slower where no property uses the source. The tenant directory is what gives Clara a resident's phone and email, co-signers, and residents who signed but have not moved in yet.

How residents reach us, last 30 days (production conversation records; residents only):

Western SlopeJP&CoSitus
Texts to Clara's number203
Texts to the office's AppFolio line (our copy; JP&Co not copied)70—238
Calls5973
Between 9 PM and 7 AM0014
Balance questions (threads or calls)604
Lease-end questions701
Maintenance30220

So the three balance reports plus the lease-dates report, about 1,500 calls a day per customer, serve roughly ten questions a month per customer, and 3.7% of resident contact happens overnight. Prospects are the daytime traffic, and that is the guest-cards pull.

Revised plan: what is safe tonight and what needs design

ChangeWhy it is safeSaves / day / customerWhen
Rental applications: every minute → every 5 minNothing reacts to an application within a minute; Clara's status answer is already written for lag~1,150tonight, one PR
Balances (3 reports): every 5 min → hourlyRead only when a resident asks; product treats under-24h as current; nothing reads them at night~800tonight, same PR
Unit vacancy: hourly for accounts with no vacancy-source property (Western Slope, Situs); Camellia stays at 5 minChecked live settings: it feeds the advertised rent Clara quotes, so it cannot stop. Needs a per-account last-pulled stamp, same mechanism as the day/night gate~260 (WS, Situs)next, with the gate
30-day income and spend figure: reuse the nightly answer across the moving window, or compute it from the charges we already storeAbout one request per building per night today; a PR to cache only the labor-rate piece was closed (saves ~1.5/day and risks a bad January reading)~65 (Western Slope)next; changes what the daily chart means, so Fede's call
Guest cards and work orders: 1 min by day, 5 min overnightNeeds a property-local day/night gate per job, copied from the inbox-watch pattern~960next, needs a small design
Lease dates: 5 min → hourlyTwo checks inside that job only run on minutes divisible by 5; a slower timer could silently never hit them. Fix that gate first~260next
Rent roll: 5 → 15 minMove-in recognition widens by ~10 min on top of AppFolio's 8 to 28 min lag~190next, with the day/night gate
Tenant directory: once a dayNot simply a cadence change. A tick without directory rows records "no co-signers" and archives them, and loses the moved-in-early status. Needs a stored last-good copy fed on the skipped ticks~230design first

Tonight's PR (applications, balances) takes Western Slope from ~6,550 to ~4,600 a day (−30%) with no visible change. The rest of plan A follows once the day/night gate exists, landing near ~2,200.

8. Decided 2026-10-27 night: the cuts, a live bug, the proof

Baseline. Every figure below is against the Oct 1–22 average at Western Slope: 6,808 calls a day (CloudWatch, one line per request). Counts after Oct 23 are not a baseline; see the bug.

What Fede decided

ReportDecisionShare of Western Slope's callsWhat changes for anyoneState
Guest cardsStays every minute, day and night21%nothinglocked
Work ordersStays every minute, day and night21%nothinglocked
Rental applicationsevery minute → every 5 min17%an application is seen up to 4 min laterPR #23030 armed to merge
Rent rollStays every 5 min. The amount a resident owes comes from this report's past-due column (not from aged receivables, as section 7 assumed)5%nothinglocked
Aged receivables, receipts, delinquencyevery 5 min → hourly; the owed number is untouched15%for up to an hour after a payment, the "last payment" line may name the previous one, with the hedge Clara already uses; a trigger that re-reads payments when a balance drops removes thatpending Fede: hedge or trigger
Lease dates (tenant tickler)5-min job → hourly, or deleted in favour of the hourly job that already pulls the same report over a wider window4% (the 5-min job's ~288 of 422)a notice or a signed lease is seen up to an hour later; prospect-conversion checks read this data in-tick and are rewired either waypending Fede: move or delete
Tenant directoryevery 5 min → once a day, reused from a stored copy between pulls; a second PR re-pulls the moment the rent roll shows a new tenant4%without PR 2, a new resident's phone is unknown to Clara for up to a dayPR 1 building
Unit vacancyunchanged4%—dropped (not worth the digging)
Work orders slower 1–6 AMrejected3.5%—dropped

Net at Western Slope once everything above lands: about 40% fewer calls a day. Getting past that means touching guest cards or work orders, which Fede ruled out.

A live bug found on the way: the 5-minute tick has been starving itself since Oct 23

A budget change merged Oct 23 reserves part of each account's request budget per cadence class. In every 5-minute tick the balances job spends the budget first; the occupancies job then asks for the tenant directory, is refused by our own budget, and skips the whole tick, taking the unit-vacancy refresh with it. Measured Oct 26: occupancies ran 28 of 288 ticks at Western Slope, 35 at JP&Co, 241 at Situs. Directory refusals in the last 10 days: 4,914. Since Oct 23, new move-ins, contact changes and co-signers have reached Clara about one tick in nine at two customers. This is also why Andrew would see Western Slope's directory calls drop from ~285 to ~35 a day since Oct 23: a throttle, not a cut. Fix in progress (run occupancies before balances, or lower the reserve); it is a fix, so no switch, and it lands together with the daily directory so the directory count does not bounce back up.

What work orders at every minute actually buys today

Last 30 days of "new work order arrived" outcomes in the sync logs: 323 fetched detail with nobody paged (no handyman set up), 155 fetched detail with nobody paged (maintenance module off), 54 skipped (already closed or pre-onboarding backlog). Tech texts went only to Willows, our test property (1 sent, 3 held). No tenant acknowledgements were found in those logs. Fede kept work orders at every minute anyway; the day maintenance paging turns on at a customer is when this matters.

Proof, morning of Oct 28

One CloudWatch Logs Insights query on the sync Lambda's log group: requests per database per report per day (the Oct 1–22 baseline came from the same query), beside the budget-refusal and skipped-tick counts. Expected after all of tonight's merges reach production: rental applications ≤288 a day, tenant directory ≤5 a day, occupancies skipped ticks near 0, guest cards and work orders unchanged near 1,440.

9. Morning after (Oct 28): what is live and what it measures

Live in production: applications every 5 min (PR #23030, merged 03:56Z), the starved 5-minute tick fixed (PR #23050, merged 04:44Z), tenant directory once a day from a stored copy (PR #23055, merged 06:59Z). The sync logs show all three in effect by 09:00Z (3 AM Denver). Every number below is the CloudWatch per-request count for the two full hours 11:00–13:00Z, scaled to a day, against the Oct 1–22 daily average (Situs: against Oct 27, its Oct 1–22 window was partial).

CustomerBefore (calls/day)This morning (calls/day)Cut
Western Slope6,8085,160−24%
JP&Co / Camellia6,2694,716−25%
Situs Group6,672 (Oct 27)5,088−24%

Per report, Western Slope (calls/day, before → this morning): rental applications 1,444 → 288; tenant directory 285 → 0 since 08:00Z after one live pull per account at 07:00Z (the stored copy served 12 ticks an hour since); guest cards 1,423 → 1,440 and work orders 1,423 → 1,464, unchanged by design; rent roll 356 → 312; receipts, aged receivables, delinquency ~350 each → 312–336; lease dates 422 → 312; vacancy 261 → 288.

The starvation bug is gone. Our own budget refused 420 requests across the three accounts between midnight and 09:00Z, then 6 in 09–11Z and 4 in 11–13Z. Occupancies ticks skipped for throttling: 61 at Western Slope and 52 at JP&Co before 09:00Z, none after. New move-ins and contact changes reach Clara every 5 minutes again.

Why 24% and not the ~40% target: the remaining approved cuts (balance side reports hourly, lease dates) wait on Fede's two picks in section 8 and are not built. Guest cards and work orders, 43% of the total, stay at every minute by decision.

Proof query (CloudWatch Logs Insights, log group /aws/lambda/propflow-appfolio-sync-prod):

fields @timestamp
| filter @message like /GET https:\/\/[a-z0-9-]+\.appfolio\.com\/api\/v1\/reports\//
| parse @message /GET https:\/\/(?<db>[a-z0-9-]+)\.appfolio\.com\/api\/v1\/reports\/(?<report>[a-z_]+)\.json/
| stats count(*) as calls by db, report, bin(1h)

Refusals: filter account budget exhausted; skipped ticks: filter portfolio fetch throttled; directory reuse: filter tenant_directory from store / tenant_directory pulled live.

10. Round two: balances and lease dates, shipped and verified (Oct 28 afternoon)

Fede gave the go after three side-effect investigations; both changes merged with no switch.

What changed

ChangePRBeforeAfter
Balance side reports (aging, payments, delinquency)#23212every 5 min with the rent rollhourly, plus an immediate re-pull when anyone's owed amount drops; aging carried forward with an as-of stamp; the "may not be current" hedge clears after a confirming pull; rent roll still every 5 min
Lease dates#23213a 5-minute pull of the tenant tickler reportdeleted; the hourly history job already writes the same fields; signed-applicant promotion reads the stored signed date; onboarding import uses the hourly job; PM countersign reminder starter runs hourly, once per account, only for companies opted in (JP&Co only). It had been silently off since Sep 9 (minute gate).

Side-effect studies

TopicFinding
Balances5 Clara phone lookups + 24 AppFolio inbox texts at Western Slope and Situs (mirror starts Sep 29): 0 of 29 would have gotten a different answer hourly. Every "I paid, why does it still show" case was AppFolio posting late (105 min to 2 days).
Lease datesAn hour's delay touches notice-to-vacate reaching turnover/renewal and dashboard move counts; Fede accepted.
Directory daily0 real misses in 30 days. A pull-time pin before Nov 1 (31 move-ins Nov 1–2) is still parked for Fede.

Live verification (17:46–19:00Z, CloudWatch + DynamoDB + production pages)

CheckResult
tenant_tickler calls1 per database at the 18:21Z hourly tick only; none on 5-min ticks (before: 1 per database per 5 min)
Balance side reportsone full pull per account at 18:28–18:31Z, then rent roll only (1 request instead of 4 per tick); stamp rows written for all three accounts
Errors / Sentry0 error-level lines since either deploy; 0 new Sentry issues
Budget refusals / skipped ticksinside the day's normal range
15 sampled lease rows (5 per customer)unchanged after the hourly tick
Countersign reminderstarted at Camellia for a lease ready since Oct 25 (first in 7 weeks); other companies log "not opted in, no robot walk"
Collections page, Western Slope and Situsloads, aging filled, totals match, tenant detail opens (screenshots in session)
Clara by text at Willowsbalance $1,615.50 unhedged = stored row; lease end Jan 16 2027 = lease row (Willows rows are hand-loaded, so this proves the read path only)
Not yet seensecond hourly pull (~19:25Z), carried aging, hedge clearing: no balance changed since deploy

Expected tomorrow (per customer per day)

OriginalAfter morning (−24%)After round two
~6,800~5,160~4,050 (−40% vs original)

Estimates until the next CloudWatch count.

Side findings, not from today's change, for follow-up

PropFlow Docs