# Rental Ledger Categorizer - Bank CSV to Schedule E (`appetizing_horsefly/rental-ledger-categorizer`) Actor

Bank or credit-card CSV to IRS Schedule E categories per rental property. No bank login, rule-based with confidence and needs-review flags, duplicates free.

- **URL**: https://apify.com/appetizing_horsefly/rental-ledger-categorizer.md
- **Developed by:** [GM Labs](https://apify.com/appetizing_horsefly) (community)
- **Categories:** Real estate, Automation
- **Security:** Limited permissions
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

Pay per event

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#pay-per-event

## What's an Apify Actor?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

## Rental Ledger Categorizer: Bank CSV to IRS Schedule E (no bank login)

**Rental Ledger Categorizer** by GM Labs turns a bank or credit-card **CSV export** into **IRS Schedule E categories** for each rental property. Drop in last year's transactions, get every row categorized (advertising, repairs, utilities, mortgage interest and more), plus a **per-property Schedule E summary** you can hand to your CPA or paste into a spreadsheet.

- **No bank linking, no account login.** You paste a CSV or give a CSV URL. Nothing is stored outside this run's dataset and key-value store.
- **Rule-based, no AI guessing.** Merchant and keyword rules with a **confidence** score. Anything unclear goes to **Needs review**.
- **Duplicates removed** (same date + amount + description) and **never charged**.
- Good for **catch-up bookkeeping** on back years, landlords whose Stessa or Baselane bank feed broke, and bookkeepers handling CSVs from several landlord clients.

### What it does

1. Reads your CSV (comma, semicolon or tab; needs a date column, a description/payee column, and either an Amount column or Debit/Credit columns).
2. Removes duplicate rows.
3. Assigns each row a property (from your property list) and a category:

| Schedule E line | Category code | Examples matched |
|---|---|---|
| 3 | `rents` | Zelle/Venmo/rent deposits (money in) |
| 5 | `advertising` | Zillow, Apartments.com, Craigslist, listing fees |
| 6 | `auto_travel` | Fuel, tolls, parking, Uber/Lyft |
| 7 | `cleaning_maintenance` | Cleaning, lawn, pest control, HOA |
| 8 | `commissions` | Leasing / tenant-placement fees |
| 9 | `insurance` | State Farm, Allstate, landlord policies |
| 10 | `legal_professional` | CPA, attorney, eviction, tenant screening |
| 11 | `management_fees` | Property management fees |
| 12 | `mortgage_interest` | Mortgage payments (flagged: split principal using Form 1098) |
| 13 | `other_interest` | Card interest, HELOC interest |
| 14 | `repairs` | Plumbing, HVAC, handyman, roofing |
| 15 | `supplies` | Home Depot, Lowe's, hardware |
| 16 | `taxes` | Property tax, county treasurer, rental licence |
| 17 | `utilities` | Water, sewer, electric, gas, trash, internet |
| 18 | `depreciation` | Not derived from bank data (n/a) |
| 19 | `other` | Use via custom rules |
| n/a | `transfer`, `security_deposit` | Transfers, card payments, deposits (non-deductible) |
| n/a | `needs_review` | No rule matched |

4. Writes a **Schedule E summary** (property x category totals) as `SCHEDULE_E_SUMMARY.csv` and a JSON `SUMMARY` record.

### Input example (copy-paste)

```json
{
  "csvText": "Date,Description,Amount\n01/05/2026,HOME DEPOT #4521,-86.12\n01/08/2026,CITY WATER UTILITY 12 OAK ST,-61.33\n01/03/2026,ZELLE FROM J SMITH RENT 12 OAK ST,1650.00",
  "properties": [{ "name": "12 Oak St", "match": ["12 oak"] }, { "name": "44 Elm Ave", "match": ["44 elm"] }],
  "customRules": [{ "match": "joe's handyman", "category": "repairs", "property": "44 Elm Ave" }]
}
```

Or set `csvUrl` to a public CSV link (for example a Google Sheet published as CSV). Card exports that show spending as positive numbers: turn on `expensesArePositive`.

### Output example (one dataset row)

```json
{ "date": "01/05/2026", "description": "HOME DEPOT #4521", "amount": -86.12, "property": "12 Oak St",
  "category": "supplies", "categoryLabel": "Supplies", "scheduleELine": "15", "confidence": 0.8,
  "matchedRule": "merchant/keyword: home depot", "needsReview": false, "isDuplicate": false, "charged": true }
```

Export the dataset to CSV, Excel or JSON from the Storage tab, or call it from the Apify API, Make, Zapier or n8n.

### Pricing (pay per event)

- **$0.25 per run** that has at least one usable row.
- **$0.002 per categorized row.** Rows sent to *Needs review*, duplicate rows, unparseable rows and empty input are **free**.
- A typical landlord year of 300 to 1,500 rows costs about **$0.85 to $3.25**. Set a max charge on the run to cap spend; the Actor stops billing when it is reached.

### FAQ

**Is my bank data stored?** Only in this run's own dataset and key-value store in your Apify account, which you control and can delete. GM Labs does not receive or keep it.
**Which banks work?** Any CSV with a header row: Chase, Bank of America, Wells Fargo, Capital One, Amex, credit unions, Relay, Mercury and others.
**Can I teach it my vendors?** Yes, `customRules` run before the built-in rules (confidence 0.95).
**Does it split mortgage principal and interest?** No. Mortgage payments are put on line 12 with confidence 0.6 and flagged for review; use your lender's Form 1098 for the interest amount.

### Disclaimer

**This is not tax, legal or accounting advice.** Categories are produced by simple keyword rules and can be wrong. Review every row, especially *Needs review* and low-confidence rows, and confirm with a qualified tax professional before filing. IRS Schedule E (Form 1040) line numbers are given for convenience only.

Made by GM Labs. Questions or a bank format that doesn't parse? Open an issue on the Actor page.

# Actor input Schema

## `csvText` (type: `string`):

Paste a bank or credit-card CSV export (header row required: a date, a description/payee, and an amount or debit/credit columns). The default is a bundled 21-row sample with one duplicate. Leave empty and set CSV URL instead if you prefer.

## `csvUrl` (type: `string`):

Optional public URL of a CSV file (e.g. a Google Sheets 'publish to web' CSV link or a Dropbox direct link). Rows are added to any pasted CSV text.

## `properties` (type: `array`):

Optional list of your rentals: {"name": "12 Oak St", "match": \["12 oak", "unit a"]}. A row is assigned to the first property whose name or match text appears in its description. With one property, every row goes to it.

## `customRules` (type: `array`):

Optional rules applied before the built-in ones: {"match": "joe's handyman", "category": "repairs", "property": "12 Oak St"}. Categories: rents, advertising, auto_travel, cleaning_maintenance, commissions, insurance, legal_professional, management_fees, mortgage_interest, other_interest, repairs, supplies, taxes, utilities, other, transfer, security_deposit.

## `defaultProperty` (type: `string`):

Label for rows that match no property.

## `expensesArePositive` (type: `boolean`):

Turn on if your single Amount column shows money out as positive (some card exports). Default: money out is negative.

## `maxRows` (type: `integer`):

Stop after this many unique rows. You are never charged beyond it.

## Actor input object example

```json
{
  "csvText": "Date,Description,Amount\n01/03/2026,ZELLE FROM J SMITH RENT 12 OAK ST,1650.00\n01/04/2026,ROCKET MORTGAGE PMT 12 OAK ST,-1123.45\n01/05/2026,HOME DEPOT #4521,-86.12\n01/06/2026,STATE FARM INSURANCE PREMIUM,-94.00\n01/08/2026,CITY WATER UTILITY 12 OAK ST,-61.33\n01/09/2026,ZILLOW RENTAL MANAGER LISTING,-29.99\n01/10/2026,ABC PLUMBING REPAIR 44 ELM AVE,-240.00\n01/11/2026,MERRY MAIDS TURNOVER CLEAN 44 ELM,-150.00\n01/12/2026,COUNTY TREASURER PROPERTY TAX 44 ELM,-1210.88\n01/14/2026,SMITH & LEE CPA TAX PREP,-350.00\n01/15/2026,GREYSTAR PROPERTY MGMT FEE 44 ELM,-132.00\n01/16/2026,SHELL OIL 57442,-42.10\n01/17/2026,LOWES #1180 SUPPLIES,-37.45\n01/18/2026,PG&E ELECTRIC 44 ELM AVE,-77.21\n01/20/2026,ONLINE TRANSFER TO SAVINGS,-500.00\n01/21/2026,VENMO PAYMENT M LOPEZ RENT 44 ELM,1400.00\n01/22/2026,LEASING AGENT COMMISSION 44 ELM,-700.00\n01/23/2026,CHASE CARD INTEREST CHARGE,-18.40\n01/24/2026,LAWN PRO LANDSCAPING 12 OAK ST,-55.00\n01/25/2026,BLUE BOTTLE COFFEE,-6.50\n01/05/2026,HOME DEPOT #4521,-86.12\n",
  "properties": [
    {
      "name": "12 Oak St",
      "match": [
        "12 oak"
      ]
    },
    {
      "name": "44 Elm Ave",
      "match": [
        "44 elm"
      ]
    }
  ],
  "defaultProperty": "Unassigned",
  "expensesArePositive": false,
  "maxRows": 20000
}
```

# Actor output Schema

## `results` (type: `string`):

Every row with category, Schedule E line, property and confidence

## `scheduleE` (type: `string`):

Totals per property and category

## `summary` (type: `string`):

Counts, charges and per-property totals

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "csvText": `Date,Description,Amount
01/03/2026,ZELLE FROM J SMITH RENT 12 OAK ST,1650.00
01/04/2026,ROCKET MORTGAGE PMT 12 OAK ST,-1123.45
01/05/2026,HOME DEPOT #4521,-86.12
01/06/2026,STATE FARM INSURANCE PREMIUM,-94.00
01/08/2026,CITY WATER UTILITY 12 OAK ST,-61.33
01/09/2026,ZILLOW RENTAL MANAGER LISTING,-29.99
01/10/2026,ABC PLUMBING REPAIR 44 ELM AVE,-240.00
01/11/2026,MERRY MAIDS TURNOVER CLEAN 44 ELM,-150.00
01/12/2026,COUNTY TREASURER PROPERTY TAX 44 ELM,-1210.88
01/14/2026,SMITH & LEE CPA TAX PREP,-350.00
01/15/2026,GREYSTAR PROPERTY MGMT FEE 44 ELM,-132.00
01/16/2026,SHELL OIL 57442,-42.10
01/17/2026,LOWES #1180 SUPPLIES,-37.45
01/18/2026,PG&E ELECTRIC 44 ELM AVE,-77.21
01/20/2026,ONLINE TRANSFER TO SAVINGS,-500.00
01/21/2026,VENMO PAYMENT M LOPEZ RENT 44 ELM,1400.00
01/22/2026,LEASING AGENT COMMISSION 44 ELM,-700.00
01/23/2026,CHASE CARD INTEREST CHARGE,-18.40
01/24/2026,LAWN PRO LANDSCAPING 12 OAK ST,-55.00
01/25/2026,BLUE BOTTLE COFFEE,-6.50
01/05/2026,HOME DEPOT #4521,-86.12`,
    "properties": [
        {
            "name": "12 Oak St",
            "match": [
                "12 oak"
            ]
        },
        {
            "name": "44 Elm Ave",
            "match": [
                "44 elm"
            ]
        }
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("appetizing_horsefly/rental-ledger-categorizer").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = {
    "csvText": """Date,Description,Amount
01/03/2026,ZELLE FROM J SMITH RENT 12 OAK ST,1650.00
01/04/2026,ROCKET MORTGAGE PMT 12 OAK ST,-1123.45
01/05/2026,HOME DEPOT #4521,-86.12
01/06/2026,STATE FARM INSURANCE PREMIUM,-94.00
01/08/2026,CITY WATER UTILITY 12 OAK ST,-61.33
01/09/2026,ZILLOW RENTAL MANAGER LISTING,-29.99
01/10/2026,ABC PLUMBING REPAIR 44 ELM AVE,-240.00
01/11/2026,MERRY MAIDS TURNOVER CLEAN 44 ELM,-150.00
01/12/2026,COUNTY TREASURER PROPERTY TAX 44 ELM,-1210.88
01/14/2026,SMITH & LEE CPA TAX PREP,-350.00
01/15/2026,GREYSTAR PROPERTY MGMT FEE 44 ELM,-132.00
01/16/2026,SHELL OIL 57442,-42.10
01/17/2026,LOWES #1180 SUPPLIES,-37.45
01/18/2026,PG&E ELECTRIC 44 ELM AVE,-77.21
01/20/2026,ONLINE TRANSFER TO SAVINGS,-500.00
01/21/2026,VENMO PAYMENT M LOPEZ RENT 44 ELM,1400.00
01/22/2026,LEASING AGENT COMMISSION 44 ELM,-700.00
01/23/2026,CHASE CARD INTEREST CHARGE,-18.40
01/24/2026,LAWN PRO LANDSCAPING 12 OAK ST,-55.00
01/25/2026,BLUE BOTTLE COFFEE,-6.50
01/05/2026,HOME DEPOT #4521,-86.12
""",
    "properties": [
        {
            "name": "12 Oak St",
            "match": ["12 oak"],
        },
        {
            "name": "44 Elm Ave",
            "match": ["44 elm"],
        },
    ],
}

# Run the Actor and wait for it to finish
run = client.actor("appetizing_horsefly/rental-ledger-categorizer").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print(f"💾 Check your data here: https://console.apify.com/storage/datasets/{run.default_dataset_id}")
for item in client.dataset(run.default_dataset_id).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "csvText": "Date,Description,Amount\\n01/03/2026,ZELLE FROM J SMITH RENT 12 OAK ST,1650.00\\n01/04/2026,ROCKET MORTGAGE PMT 12 OAK ST,-1123.45\\n01/05/2026,HOME DEPOT #4521,-86.12\\n01/06/2026,STATE FARM INSURANCE PREMIUM,-94.00\\n01/08/2026,CITY WATER UTILITY 12 OAK ST,-61.33\\n01/09/2026,ZILLOW RENTAL MANAGER LISTING,-29.99\\n01/10/2026,ABC PLUMBING REPAIR 44 ELM AVE,-240.00\\n01/11/2026,MERRY MAIDS TURNOVER CLEAN 44 ELM,-150.00\\n01/12/2026,COUNTY TREASURER PROPERTY TAX 44 ELM,-1210.88\\n01/14/2026,SMITH & LEE CPA TAX PREP,-350.00\\n01/15/2026,GREYSTAR PROPERTY MGMT FEE 44 ELM,-132.00\\n01/16/2026,SHELL OIL 57442,-42.10\\n01/17/2026,LOWES #1180 SUPPLIES,-37.45\\n01/18/2026,PG&E ELECTRIC 44 ELM AVE,-77.21\\n01/20/2026,ONLINE TRANSFER TO SAVINGS,-500.00\\n01/21/2026,VENMO PAYMENT M LOPEZ RENT 44 ELM,1400.00\\n01/22/2026,LEASING AGENT COMMISSION 44 ELM,-700.00\\n01/23/2026,CHASE CARD INTEREST CHARGE,-18.40\\n01/24/2026,LAWN PRO LANDSCAPING 12 OAK ST,-55.00\\n01/25/2026,BLUE BOTTLE COFFEE,-6.50\\n01/05/2026,HOME DEPOT #4521,-86.12\\n",
  "properties": [
    {
      "name": "12 Oak St",
      "match": [
        "12 oak"
      ]
    },
    {
      "name": "44 Elm Ave",
      "match": [
        "44 elm"
      ]
    }
  ]
}' |
apify call appetizing_horsefly/rental-ledger-categorizer --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,appetizing_horsefly/rental-ledger-categorizer"
        }
    }
}
```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/f8lN2eNmxkXTDWXVq/builds/M4LvgPgSAPxkchUgO/openapi.json
