Install any skill in seconds. Free to start, no credit card required.
Get Started Free →Query AppFolio properties, units, and tenants via REST API. Trigger: "appfolio hello world".
| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-02 | ✗→✓ | ▲ Improved | -27% | 0% |
| case-05 | ✗→✓ | ▲ Improved | -29% | 0% |
| case-06 | ✗→✓ | ▲ Improved | 8% | 0% |
| case-08 | ✗→✓ | ▲ Improved | 31% | 0% |
| case-09 | ✗→✓ | ▲ Improved | -7% | 0% |
Get started with the AppFolio Property Manager API by authenticating with your client credentials and making your first API calls. This skill walks through connecting to the REST API, fetching a property listing, retrieving tenant details, and creating a basic work order — the essential operations for any AppFolio integration.
APPFOLIO_CLIENT_ID and APPFOLIO_CLIENT_SECRET environment variables settypescriptconst APPFOLIO_BASE = process.env.APPFOLIO_BASE_URL || "https://yourcompany.appfolio.com/api/v1"; async function appfolioFetch(path: string) { const credentials = Buffer.from( `${process.env.APPFOLIO_CLIENT_ID}:${process.env.APPFOLIO_CLIENT_SECRET}` ).toString("base64"); const res = await fetch(`${APPFOLIO_BASE}${path}`, { headers: { Authorization: `Basic ${credentials}`, Accept: "application/json" }, }); if (!res.ok) throw new Error(`AppFolio ${res.status}: ${await res.text()}`); return res.json(); }
typescriptconst properties = await appfolioFetch("/properties?page_size=10"); console.log(`Found ${properties.length} properties`); properties.forEach((p: any) => console.log(` ${p.id}: ${p.address_line1}, ${p.city}`));
typescriptconst tenants = await appfolioFetch(`/tenants?property_id=${properties[0].id}`); tenants.forEach((t: any) => console.log(` ${t.name} — Unit ${t.unit_number}`));
typescriptconst workOrder = await fetch(`${APPFOLIO_BASE}/work_orders`, { method: "POST", headers: { Authorization: `Basic ${Buffer.from(`${process.env.APPFOLIO_CLIENT_ID}:${process.env.APPFOLIO_CLIENT_SECRET}`).toString("base64")}`, "Content-Type": "application/json", }, body: JSON.stringify({ property_id: properties[0].id, description: "Leaky faucet in kitchen", priority: "normal", }), }); console.log("Work order created:", (await workOrder.json()).id);
A successful run produces authenticated API responses: a list of properties with addresses, tenant details for the first property, and a new work order ID confirming write access.
| Error | Cause | Solution | |-------|-------|----------| | 401 Unauthorized | Invalid client_id or secret | Verify credentials in environment variables | | 403 Forbidden | API scope not granted | Check Stack Partner permissions for the endpoint | | 404 Not Found | Wrong base URL or endpoint | Confirm your company subdomain and API version | | 422 Unprocessable | Missing required fields | Validate property_id and required body params | | 429 Too Many Requests | Rate limit exceeded | Add backoff delay, batch requests |
See appfolio-core-workflow-a for full property and tenant management workflows.
Other measured skills in the registry, with their headline benchmark lift.