# Baton Rouge Property Tax Roll - Owners & 11-Year Values (`j0401/brla-tax-roll`) Actor

East Baton Rouge Parish tax roll (public open data, 4.02M assessment lines, 2015-2025): every property's taxpayer, exemption and value, plus each property's 11-year roll with the year-over-year movement. Covers Baton Rouge, Baker, Central and Zachary.

- **URL**: https://apify.com/j0401/brla-tax-roll.md
- **Developed by:** [Wenhao Yang](https://apify.com/j0401) (community)
- **Categories:** Business
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.06 / 1,000 tax roll records

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

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

## Baton Rouge Property Tax Roll - Owners, Exemptions & 11 Years of Values

Every property in East Baton Rouge Parish - Baton Rouge, Baker, Central and Zachary - with its taxpayer, its exemptions and its eleven-year value trail.

The parish Assessor publishes the full tax roll as open data: **4,019,268 assessment lines** covering the 2015 through 2025 rolls, about 352,000 to 376,000 lines each. A property appears once per line - its acreage, its lot, and one line per improvement standing on it - and every line carries the taxpayer, the exemption, the tax freeze and the value. Because the roll is published year after year in one table, a property's **eleven-year value trail is the source's own data**, not something reconstructed here.

### Low cost

**From $0.0001 per record, down to $0.00006 at Gold** Pay-per-event: you are charged per record delivered, and nothing for the query.

### What you get

| Block | Fields |
|---|---|
| **Identity** | tax year, property number (and the legacy hyphenated form), unit type, ward, subdivision, block, lot, legal description |
| **Taxpayer** | taxpayer name, and the address of record in the three source columns (`taxpayerAddr1` / `2` / `3`) plus a composed `mailingAddress` |
| **Classification** | assessment type, assessment status, unit type, structure use, vacant-lot flag, number of units |
| **Value** | fair market value, total assessed value, taxable value - plus the homestead exemption amount |
| **Exemptions & freeze** | homestead exemption type and amount, tax freeze (NONE / OVER 65 / DISABLED VET / DISABLED, plus a rare OTHER), restoration tax abatement |
| **Transfer** | transfer instrument type, transfer date, deed number |
| **Derived** | composed mailing address, and on a property trail the year-over-year market and assessed movement |

### Eleven years of value, per property

Ask for one property and you get every roll it appears on, oldest first, with its lines summed per year and the movement computed between consecutive rolls:

```json
{ "mode": "property", "propertyNumber": "35" }
```

```
2015   8,000
2016   8,000        0.0%
2017   8,000        0.0%
2018   8,000        0.0%
2019   8,000        0.0%
2020   8,800      +10.0%
2021   8,800        0.0%
2022   8,800        0.0%
2023   8,800        0.0%
2024   9,856      +12.0%
2025   9,856        0.0%
```

Eleven consecutive rolls on one property number - the reassessment years and the flat years in between.

### The parish's movement without shipping a line

`mode=trend` returns one row per tax year for whatever scope you filter to:

```json
{ "mode": "trend" }
```

```
2015   352,093 lines   market 38,689,476,487
2016   354,161 lines   market 39,106,691,062    +1.1%
2017   357,562 lines   market 36,484,308,929    -6.7%
2018   361,539 lines   market 36,511,714,230    +0.1%
2019   364,655 lines   market 38,006,801,946    +4.1%
2020   367,257 lines   market 40,847,800,370    +7.5%
2021   369,632 lines   market 41,884,785,743    +2.5%
2022   372,877 lines   market 43,429,594,730    +3.7%
2023   370,144 lines   market 45,680,406,086    +5.2%
2024   373,713 lines   market 47,958,221,257    +5.0%
2025   375,635 lines   market 49,093,487,523    +2.4%
```

### Exemptions, freezes and homesteads

The roll records who is exempt from what, and it is filterable:

| Tax freeze | 2025 lines |
|---|---|
| NONE | 339,147 |
| OVER 65 | 34,872 |
| DISABLED VET | 936 |
| DISABLED | 680 |

`homesteadExemptType` separates the 202,262 lines carrying a homestead exemption from the 173,373 that do not, and the exemption amount is a column of its own alongside the taxable value - so the difference between what a property is worth and what it is taxed on is visible directly.

### Modes

- **`rows`** (default) - assessment lines matching your filters, from the newest tax year.
- **`property`** - one property's full eleven-year roll, its lines summed per year, with the value movement computed between years. Takes `propertyNumber`.
- **`trend`** - per-tax-year parish totals for a scope: line count and summed market and assessed value, one row per year.
- **`aggregate`** - one count row per group, rolled up by tax year, unit type, assessment type, assessment status, tax freeze, homestead exemption, structure use or ward.

### Inputs

Filter by `taxYear` (2015-2025, defaults to the newest), `assessmentType`, `assessmentStatus`, `unitType`, `taxFreeze`, `homesteadExemptType`, `propertyNumber`, `legacyPropertyNumber`, `ward`, `structureUse`, `subdivision`, `address`, `taxpayerName`, `mailingCity`, `mailingZip`, and value bounds `minMarketValue` / `maxMarketValue` / `minTaxableValue` / `maxTaxableValue`.

A filter that belongs to another mode is rejected outright rather than silently ignored - so a query you narrowed never comes back unnarrowed.

### Example inputs

**Every property owned by a business**

```json
{ "mode": "rows", "taxpayerName": "LLC", "minMarketValue": 1000000,
  "maxResults": 500 }
```

**One property's eleven-year value trail**

```json
{ "mode": "property", "propertyNumber": "35" }
```

**The parish's eleven-year movement**

```json
{ "mode": "trend" }
```

**Senior-frozen properties**

```json
{ "mode": "rows", "taxFreeze": "OVER 65", "maxResults": 500 }
```

**How many lines by unit type**

```json
{ "mode": "aggregate", "groupBy": "unitType" }
```

### Example output

**`mode=rows`, `propertyNumber=19`, `taxYear=2025`** - one assessment line, with its land classification, the taxpayer of record and the three value columns:

```json
{
 "platform": "brla-tax-roll",
 "source": "east-baton-rouge-parish-roll",
 "mode": "rows",
 "recordType": "assessment-line",
 "groupKey": "",
 "groupCount": "",
 "groupBy": "",
 "assessedValueChange": "",
 "assessedValueChangePct": "",
 "assessmentStatus": "ACTUAL",
 "assessmentType": "REAL PROPERTY",
 "block": "",
 "deedNumber": "",
 "fairMarketValue": "51500",
 "homesteadExemptType": "NO",
 "homesteadExemption": "0",
 "legacyPropertyNumber": "000-0001-9",
 "legalDescription": "Ward: 1-1, Subdivision: DEVALL TOWN, Lot: 5 PT, Block: 12.     SOUTHERN PORTION OF LOT 5 SQ. 12 OR67, FRONTING 64 FT. ONTHE NORTH SIDE OF LAUREL ST. X 150FT.",
 "lot": "",
 "mailingAddress": "700 MAIN ST BATON ROUGE, LA 70802",
 "marketValueChange": "",
 "marketValueChangePct": "",
 "parcelAddress": "",
 "prevTaxYear": "",
 "propertyNumber": "19",
 "restorationTaxAbatement": "NO",
 "sourceUpdatedAt": "2026-01-20",
 "structureUse": "",
 "subdivision": "",
 "taxFreeze": "NONE",
 "taxYear": "2025",
 "taxableValue": "5150",
 "taxpayerAddr1": "700 MAIN ST",
 "taxpayerAddr2": "BATON ROUGE, LA 70802",
 "taxpayerAddr3": "",
 "taxpayerName": "LA CAPITOL FEDERAL CREDIT UNION",
 "totalValue": "5150",
 "transferDate": "",
 "transferInstrument": "",
 "unitType": "LOT",
 "units": "1",
 "vacantLot": "NO",
 "ward": ""
}
```

**`mode=aggregate`, `groupBy=taxFreeze`, `taxYear=2025`** - the 2025 roll by freeze category:

```
NONE            339,147
OVER 65          34,872
DISABLED VET        936
DISABLED            680
```

### Notes on the data

- **A property is a set of lines, not a row.** One property number carries its acreage, its lot and one line per improvement. On the 2025 roll that is **375,635 lines against 209,443 properties** - so a line count is not a property count, and this description does not call it one. The `unitType` field tells you which kind of line you are looking at, and `mode=property` sums them per year so a value is never compared line-to-line across years.
- **This source's columns do not mean the same thing every year, and that is left visible rather than smoothed over.** The taxpayer mailing address **moves between columns**: through 2022 the street sits in `taxpayerAddr1` and `taxpayerAddr3` holds "CITY, ST ZIP"; in 2024 the street is in `taxpayerAddr2`; in 2025 it is back in `taxpayerAddr1`. The address parts are therefore returned **verbatim under their source column names**, with `mailingAddress` offered only as a best-effort join - and a `mailingCity` / `mailingZip` search is applied across all three columns rather than to one fixed mapping, which would return nothing for most years. Treat the address as "what the roll recorded that year", not as a field comparable across years.
- **Several columns stop being populated, and they do not all stop in the same year.** `structureUse`, `transferDate`, `deedNumber`, `parcelAddress` and `ward` are filled through the 2023 roll and then drop away (`structureUse`: 369,933 lines in 2023, none in 2024; `parcelAddress`: 370,144 in 2023, 3 in 2024). `subdivision`, `block` and `lot` stop a year **earlier** - they are already empty by 2023. Everything is still returned when present, and the `address` description says which rolls it covers rather than silently returning nothing.
- **Both property-number forms are returned.** The roll also carries the older hyphenated number (`010-0019-5`) in its own column, and either form can be matched - useful when working from an older document that only carries one of them.
- **The source writes the literal string "NULL" where it means empty, and at scale** - 358,245 rows of the taxpayer address column carry it on the 2023 roll alone. It is normalised to empty here, so a `"NULL"` string is never served as though it were an address.
- **Two fields are carried but not offered as filters.** On the 2025 roll `vacant_lot` reads "NO" on every single line and the transfer-instrument column is blank on all but three, so a filter on either could only ever return everything or nothing. They remain in the output, and older rolls may populate them.
- **Two filters only cover part of the eleven-year window.** `taxFreeze` is empty on every roll before 2021 (2,157,267 lines, 54% of the table) - the freeze column is simply not populated earlier. And `unitType` uses different labels over time: `LOT` exists in 2015-2016 and from 2023, but not at all in 2017-2022, where land lines are recorded as `ACREAGE`. Both are honest filters on the rolls that carry them; a query that returns nothing for an early year is the source's record, not a matching failure.

### Output

One JSON record per line, and the same key set whichever mode produced it - so the output loads into a table or a dataframe without a schema union step. Aggregate, trend and property-history records carry their key, count and dimension in the same shape as a line record.

**Pay-per-event** - you are charged per record delivered, never for the query itself. The run stops cleanly if you set a spend limit on your Apify account.

# Actor input Schema

## `mode` (type: `string`):

rows = assessment lines matching your filters. property = one property's full 11-year roll with its lines summed per year and the movement computed. trend = per-year parish totals for a scope. aggregate = server-side count by one dimension.

## `taxYear` (type: `string`):

Roll year. The source currently carries 2015-2025. Defaults to the newest year available.

## `maxResults` (type: `integer`):

Maximum records to return. Default 50; 0 is treated as the default rather than as all records.

## `propertyNumber` (type: `string`):

The property number, exact match (use it with mode=property to follow one property across years).

## `legacyPropertyNumber` (type: `string`):

The older hyphenated property number form, exact match (e.g. 010-0019-5).

## `taxpayerName` (type: `string`):

Partial taxpayer-name match. '%' and '\_' are not allowed.

## `unitType` (type: `string`):

The kind of assessment line. ACREAGE and LOT are land; IMPROVEMENT is a building or improvement. A property is one or more lines. Note: LOT is absent from the 2017-2022 rolls, where land lines are recorded as ACREAGE.

## `assessmentType` (type: `string`):

REAL PROPERTY or PUBLIC SERVICE.

## `assessmentStatus` (type: `string`):

ACTUAL, EXEMPT or ADJUDICATED, plus the rare RS and IE codes the roll also carries.

## `taxFreeze` (type: `string`):

Assessment freeze category: NONE, OVER 65, DISABLED VET, DISABLED or OTHER. Empty on every roll before 2021.

## `homesteadExemptType` (type: `string`):

Whether the property carries a homestead exemption.

## `address` (type: `string`):

Partial match on the property's physical address. Note: this column is populated through the 2023 roll and is empty from 2024 on, so a search here returns nothing on the later rolls.

## `ward` (type: `string`):

Partial ward match (e.g. 3-0). Populated through the 2023 roll; empty from 2024 on.

## `subdivision` (type: `string`):

Partial subdivision-name match. Populated through the 2023 roll.

## `structureUse` (type: `string`):

Partial structure-use match (COMMERCIAL, RESIDENTIAL and so on). Populated through the 2023 roll.

## `mailingCity` (type: `string`):

Partial city match on the taxpayer's mailing address. Matched across the address columns, which move between years.

## `mailingZip` (type: `string`):

ZIP match on the taxpayer's mailing address. Digits and dashes only.

## `minMarketValue` (type: `number`):

Lower bound on the fair market value.

## `maxMarketValue` (type: `number`):

Upper bound on the fair market value.

## `minTaxableValue` (type: `number`):

Lower bound on the taxable value.

## `maxTaxableValue` (type: `number`):

Upper bound on the taxable value.

## `groupBy` (type: `string`):

Dimension for the server-side count. Use only with mode=aggregate - supplying it in another mode is rejected rather than ignored.

## Actor input object example

```json
{
  "mode": "rows",
  "maxResults": 50
}
```

# Actor output Schema

## `recordsUrl` (type: `string`):

Parish tax roll lines, property histories, year trends or aggregates - as JSON

## `datasetUrl` (type: `string`):

No description

## `runUrl` (type: `string`):

No description

# 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 = {};

// Run the Actor and wait for it to finish
const run = await client.actor("j0401/brla-tax-roll").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 = {}

# Run the Actor and wait for it to finish
run = client.actor("j0401/brla-tax-roll").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 '{}' |
apify call j0401/brla-tax-roll --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,j0401/brla-tax-roll"
        }
    }
}
```

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/5qYs1l0cvLswixynf/builds/h1Obh3DVZXXTNEoor/openapi.json
