All Docs
FeaturesMaking Tax DigitalUpdated February 25, 2026

Faster Tenancy Loading: How We Fixed an N+1 API Call Problem in AgentOS

Faster Tenancy Loading: How We Fixed an N+1 API Call Problem in AgentOS

Release: v1.0.48

In v1.0.48 we resolved a significant performance bottleneck in the AgentOS integration that caused tenancy data to load far more slowly than necessary. This post explains what the problem was, why it happened, and what we did to fix it.


The Problem: N+1 API Calls

When fetching tenancies for a landlord, the listTenancies procedure in src/lib/routers/agentos.ts followed this pattern:

  1. Call agentosClient.getProperties() to retrieve all properties for the landlord.
  2. Loop over each property and call agentosClient.getTenancies(shortName, landlordOID, prop.id)one request per property.

This is the classic N+1 query problem, applied to external HTTP calls rather than a database. For a landlord with 10 properties, the application was making 11 sequential HTTP requests to the AgentOS API before it could return a result.

// Previous behaviour (simplified)
const properties = await agentosClient.getProperties(); // 1 request

for (const prop of properties) {
  // 1 request per property — sequential, blocking
  const tenancies = await agentosClient.getTenancies(shortName, landlordOID, prop.id);
  results.push(...tenancies);
}

Because these calls were sequential, every network round-trip had to complete before the next one started. At typical API latencies, this created a 10–50× slowdown relative to a single bulk request.


The Fix: Bulk Fetch + Client-Side Filtering

The AgentOS API already supports fetching all tenancies for a landlord in one call — agentosClient.getTenancies() accepts an optional propertyId, meaning it can be called without one to return everything.

The fix has two parts:

1. Single Bulk Request (No propertyId Filter)

When no propertyId is requested by the caller, listTenancies now makes a single call to /tenancies and retrieves all tenancies at once. If the caller does supply a propertyId, filtering is applied client-side after the bulk fetch — no extra API call required.

// New behaviour (simplified)
const allTenancies = await agentosClient.getTenancies(shortName, landlordOID);
                                                                    // ↑ no propertyId = bulk fetch

// Filter client-side if the caller requested a specific property
const result = propertyId
  ? allTenancies.filter(t => t.propertyId === propertyId)
  : allTenancies;

2. Parallel Requests with Promise.all()

For landlords who have multiple AgentOS account links, we still need one request per linked account. The previous for...of loop processed these sequentially. We now use Promise.all() so all account requests are dispatched simultaneously and resolved together.

// New behaviour for multiple landlord links
const results = await Promise.all(
  landlordLinks.map(link =>
    agentosClient.getTenancies(link.shortName, link.landlordOID)
  )
);

Impact

ScenarioBeforeAfter
1 landlord link, 10 properties11 sequential requests1 request
1 landlord link, 50 properties51 sequential requests1 request
3 landlord links, 10 properties each33 sequential requests3 parallel requests

Estimated speedup: 5–20× faster tenancy loading, depending on the number of properties and AgentOS API latency.


Why This Matters for Landlords

Tenancy data underpins much of the platform — transaction imports, quarterly reporting, and Making Tax Digital submissions all depend on having an accurate, up-to-date view of which tenancies are active across a portfolio. Slow tenancy loading directly delays these downstream operations. This fix ensures the integration scales cleanly whether a landlord has 1 property or 100.


Affected File

  • src/lib/routers/agentos.tslistTenancies procedure