Properties & Portfolios¶
The Properties API manages real estate assets and portfolios. Portfolios group properties for aggregate analytics and reporting. All endpoints require authentication and are prefixed with /pm.
Portfolios¶
POST /pm/portfolios¶
Create a new portfolio.
| Auth required | Yes |
Request Body¶
| Field | Type | Required | Description |
|---|---|---|---|
name |
string | Yes | Portfolio name |
description |
string | No | Freeform description |
Example Request¶
curl -X POST https://app.meterbase.io/api/v1/pm/portfolios \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"name": "West Coast Holdings",
"description": "All properties in CA, OR, and WA"
}'
Example Response¶
201 Created
{
"id": "pf_abc123",
"name": "West Coast Holdings",
"description": "All properties in CA, OR, and WA",
"owner_id": "usr_a1b2c3d4",
"created_at": "2026-03-25T14:00:00Z"
}
Error Cases¶
| Status | Detail | Cause |
|---|---|---|
401 |
"Not authenticated" |
Missing or expired token |
422 |
"field required" |
Missing name field |
GET /pm/portfolios¶
List all portfolios for the authenticated user.
| Auth required | Yes |
Query Parameters¶
| Parameter | Type | Default | Description |
|---|---|---|---|
page |
integer | 1 |
Page number |
per_page |
integer | 20 |
Items per page (max 100) |
Example Request¶
Example Response¶
200 OK
{
"items": [
{
"id": "pf_abc123",
"name": "West Coast Holdings",
"description": "All properties in CA, OR, and WA",
"property_count": 12,
"created_at": "2026-03-25T14:00:00Z"
}
],
"total": 1,
"page": 1,
"per_page": 20,
"pages": 1
}
GET /pm/portfolios/{id}¶
Retrieve a portfolio with a summary of its properties and costs.
| Auth required | Yes |
Path Parameters¶
| Parameter | Type | Description |
|---|---|---|
id |
string | Portfolio ID |
Example Request¶
Example Response¶
200 OK
{
"id": "pf_abc123",
"name": "West Coast Holdings",
"description": "All properties in CA, OR, and WA",
"owner_id": "usr_a1b2c3d4",
"created_at": "2026-03-25T14:00:00Z",
"summary": {
"property_count": 12,
"total_monthly_cost": 48750.00,
"total_annual_cost": 585000.00,
"avg_cost_per_kwh": 0.187,
"total_sqft": 245000
}
}
Error Cases¶
| Status | Detail | Cause |
|---|---|---|
404 |
"Portfolio not found" |
Invalid or inaccessible portfolio ID |
GET /pm/portfolios/{id}/analytics¶
Retrieve full analytics for a portfolio including cost trends, rate comparisons, and savings opportunities.
| Auth required | Yes |
Path Parameters¶
| Parameter | Type | Description |
|---|---|---|
id |
string | Portfolio ID |
Example Request¶
curl https://app.meterbase.io/api/v1/pm/portfolios/pf_abc123/analytics \
-H "Authorization: Bearer $TOKEN"
Example Response¶
200 OK
{
"portfolio_id": "pf_abc123",
"period": "2025-04-01/2026-03-31",
"total_cost": 585000.00,
"total_usage_kwh": 3128342,
"avg_rate_per_kwh": 0.187,
"cost_by_month": [
{ "month": "2025-04", "cost": 42500.00, "usage_kwh": 227272 },
{ "month": "2025-05", "cost": 44100.00, "usage_kwh": 235828 }
],
"cost_by_property": [
{ "property_id": "prop_001", "name": "Sunset Apartments", "annual_cost": 72000.00 },
{ "property_id": "prop_002", "name": "Harbor View Plaza", "annual_cost": 96000.00 }
],
"potential_savings": {
"total_annual": 34200.00,
"properties_with_savings": 8,
"top_opportunity": {
"property_id": "prop_002",
"current_tariff": "SCE TOU-GS-2",
"recommended_tariff": "SCE TOU-GS-3",
"annual_savings": 8400.00
}
}
}
Properties¶
POST /pm/properties¶
Create a new property.
| Auth required | Yes |
Request Body¶
| Field | Type | Required | Description |
|---|---|---|---|
name |
string | Yes | Property name |
address |
string | Yes | Street address |
city |
string | Yes | City |
state |
string | Yes | Two-letter state code (e.g., CA) |
zip_code |
string | Yes | 5-digit ZIP code |
property_type |
string | Yes | One of: residential, commercial, industrial, mixed_use |
sqft |
integer | No | Total square footage |
unit_count |
integer | No | Number of units (for multifamily) |
year_built |
integer | No | Year the property was built |
portfolio_id |
string | No | Portfolio to assign the property to |
utility_account_number |
string | No | Utility account number |
meter_number |
string | No | Meter identifier |
tariff_id |
string | No | Current tariff ID from the tariff database |
monthly_usage_kwh |
number | No | Estimated average monthly usage in kWh |
monthly_demand_kw |
number | No | Estimated average monthly peak demand in kW |
has_solar |
boolean | No | Whether the property has solar panels (default: false) |
solar_capacity_kw |
number | No | Solar system capacity in kW |
annual_revenue |
number | No | Annual revenue for NOI calculations |
noi |
number | No | Net Operating Income |
energy_pct_of_noi |
number | No | Energy cost as a percentage of NOI (auto-calculated if noi and energy cost are set) |
Example Request¶
curl -X POST https://app.meterbase.io/api/v1/pm/properties \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"name": "Sunset Apartments",
"address": "123 Main St",
"city": "Los Angeles",
"state": "CA",
"zip_code": "90001",
"property_type": "residential",
"sqft": 24000,
"unit_count": 32,
"year_built": 1998,
"portfolio_id": "pf_abc123",
"monthly_usage_kwh": 15000,
"monthly_demand_kw": 45,
"has_solar": false
}'
Example Response¶
201 Created
{
"id": "prop_abc123",
"name": "Sunset Apartments",
"address": "123 Main St",
"city": "Los Angeles",
"state": "CA",
"zip_code": "90001",
"property_type": "residential",
"sqft": 24000,
"unit_count": 32,
"year_built": 1998,
"portfolio_id": "pf_abc123",
"utility_account_number": null,
"meter_number": null,
"tariff_id": null,
"monthly_usage_kwh": 15000,
"monthly_demand_kw": 45,
"has_solar": false,
"solar_capacity_kw": null,
"owner_id": "usr_a1b2c3d4",
"created_at": "2026-03-25T14:00:00Z",
"updated_at": "2026-03-25T14:00:00Z"
}
Error Cases¶
| Status | Detail | Cause |
|---|---|---|
422 |
"field required" |
Missing a required field |
422 |
"Invalid state code" |
State not a valid 2-letter code |
404 |
"Portfolio not found" |
Invalid portfolio_id |
GET /pm/properties¶
List properties with optional filters.
| Auth required | Yes |
Query Parameters¶
| Parameter | Type | Default | Description |
|---|---|---|---|
portfolio_id |
string | - | Filter by portfolio |
state |
string | - | Filter by state code |
city |
string | - | Filter by city name |
property_type |
string | - | Filter by property type |
page |
integer | 1 |
Page number |
per_page |
integer | 20 |
Items per page (max 100) |
Example Request¶
curl "https://app.meterbase.io/api/v1/pm/properties?state=CA&property_type=residential&per_page=10" \
-H "Authorization: Bearer $TOKEN"
Example Response¶
200 OK
{
"items": [
{
"id": "prop_abc123",
"name": "Sunset Apartments",
"address": "123 Main St",
"city": "Los Angeles",
"state": "CA",
"zip_code": "90001",
"property_type": "residential",
"sqft": 24000,
"unit_count": 32,
"monthly_usage_kwh": 15000,
"portfolio_id": "pf_abc123"
}
],
"total": 1,
"page": 1,
"per_page": 10,
"pages": 1
}
GET /pm/properties/{id}¶
Retrieve a property with full details and available tariffs for its service area.
| Auth required | Yes |
Path Parameters¶
| Parameter | Type | Description |
|---|---|---|
id |
string | Property ID |
Example Request¶
Example Response¶
200 OK
{
"id": "prop_abc123",
"name": "Sunset Apartments",
"address": "123 Main St",
"city": "Los Angeles",
"state": "CA",
"zip_code": "90001",
"property_type": "residential",
"sqft": 24000,
"unit_count": 32,
"year_built": 1998,
"portfolio_id": "pf_abc123",
"utility_account_number": null,
"meter_number": null,
"tariff_id": "trf_sce_tou_gs2",
"monthly_usage_kwh": 15000,
"monthly_demand_kw": 45,
"has_solar": false,
"solar_capacity_kw": null,
"owner_id": "usr_a1b2c3d4",
"created_at": "2026-03-25T14:00:00Z",
"updated_at": "2026-03-25T14:00:00Z",
"available_tariffs": [
{
"id": "trf_sce_tou_gs1",
"name": "SCE TOU-GS-1",
"utility_name": "Southern California Edison",
"rate_type": "tou",
"sector": "commercial"
},
{
"id": "trf_sce_tou_gs2",
"name": "SCE TOU-GS-2",
"utility_name": "Southern California Edison",
"rate_type": "tou",
"sector": "commercial"
}
]
}
Error Cases¶
| Status | Detail | Cause |
|---|---|---|
404 |
"Property not found" |
Invalid or inaccessible property ID |
PATCH /pm/properties/{id}¶
Update one or more fields on an existing property. Only include the fields you want to change.
| Auth required | Yes |
Path Parameters¶
| Parameter | Type | Description |
|---|---|---|
id |
string | Property ID |
Request Body¶
Any subset of the fields from POST /pm/properties. All fields are optional.
Example Request¶
curl -X PATCH https://app.meterbase.io/api/v1/pm/properties/prop_abc123 \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"tariff_id": "trf_sce_tou_gs3",
"monthly_usage_kwh": 16500,
"has_solar": true,
"solar_capacity_kw": 50
}'
Example Response¶
200 OK
Returns the full updated property object (same schema as GET /pm/properties/{id}, without available_tariffs).
Error Cases¶
| Status | Detail | Cause |
|---|---|---|
404 |
"Property not found" |
Invalid or inaccessible property ID |
422 |
Validation errors | Invalid field values |
POST /pm/properties/{id}/analyze-rates¶
Run a rate analysis for a property, comparing its current tariff against all available alternatives.
| Auth required | Yes |
Path Parameters¶
| Parameter | Type | Description |
|---|---|---|
id |
string | Property ID |
Example Request¶
curl -X POST https://app.meterbase.io/api/v1/pm/properties/prop_abc123/analyze-rates \
-H "Authorization: Bearer $TOKEN"
Example Response¶
200 OK
{
"property_id": "prop_abc123",
"current_tariff": {
"id": "trf_sce_tou_gs2",
"name": "SCE TOU-GS-2",
"estimated_monthly_cost": 3150.00
},
"alternatives": [
{
"tariff_id": "trf_sce_tou_gs3",
"name": "SCE TOU-GS-3",
"estimated_monthly_cost": 2850.00,
"monthly_savings": 300.00,
"annual_savings": 3600.00,
"savings_percent": 9.5
},
{
"tariff_id": "trf_sce_tou_gs1",
"name": "SCE TOU-GS-1",
"estimated_monthly_cost": 3400.00,
"monthly_savings": -250.00,
"annual_savings": -3000.00,
"savings_percent": -7.9
}
],
"recommendation": "trf_sce_tou_gs3",
"analyzed_at": "2026-03-25T14:30:00Z"
}
Tip
This endpoint requires the property to have monthly_usage_kwh set (and optionally monthly_demand_kw). If usage data is missing, you will receive a 400 error.
Error Cases¶
| Status | Detail | Cause |
|---|---|---|
404 |
"Property not found" |
Invalid property ID |
400 |
"Property has no usage data" |
monthly_usage_kwh is not set |
400 |
"No tariffs available for this service area" |
No tariffs found for the property's ZIP code |
Dashboard¶
GET /pm/dashboard/stats¶
Retrieve aggregate dashboard statistics across all properties.
| Auth required | Yes |
Example Request¶
Example Response¶
200 OK
{
"total_properties": 47,
"total_portfolios": 3,
"total_sqft": 1250000,
"total_monthly_cost": 187500.00,
"total_annual_cost": 2250000.00,
"avg_cost_per_sqft": 1.80,
"avg_cost_per_kwh": 0.172,
"properties_with_savings": 23,
"total_potential_savings": 124000.00,
"alerts_unread": 5,
"bills_this_month": 12
}
GET /pm/dashboard/cost-trend¶
Retrieve monthly cost trend data based on real bill data across all properties. Returns actual billed amounts (not estimates) for dashboard visualization.
| Auth required | Yes |
Query Parameters¶
| Parameter | Type | Default | Description |
|---|---|---|---|
months |
integer | 12 |
Number of months to include (max 36) |
portfolio_id |
string | - | Filter to a specific portfolio |
Example Request¶
curl "https://app.meterbase.io/api/v1/pm/dashboard/cost-trend?months=12" \
-H "Authorization: Bearer $TOKEN"
Example Response¶
200 OK
{
"months": [
{ "month": "2025-04", "total_cost": 42500.00, "total_kwh": 227272, "property_count": 47 },
{ "month": "2025-05", "total_cost": 44100.00, "total_kwh": 235828, "property_count": 47 },
{ "month": "2025-06", "total_cost": 48200.00, "total_kwh": 257754, "property_count": 47 }
],
"trend_direction": "increasing",
"avg_monthly_cost": 44933.33
}
GET /pm/dashboard/global-analytics¶
Cross-portfolio analytics providing a unified view of energy costs, usage patterns, and savings across all properties the user has access to.
| Auth required | Yes |
Example Request¶
curl https://app.meterbase.io/api/v1/pm/dashboard/global-analytics \
-H "Authorization: Bearer $TOKEN"
Example Response¶
200 OK
{
"total_properties": 47,
"total_portfolios": 3,
"total_annual_cost": 2250000.00,
"total_annual_kwh": 12000000,
"avg_cost_per_kwh": 0.1875,
"avg_cost_per_sqft": 1.80,
"cost_by_state": [
{ "state": "CA", "annual_cost": 1200000.00, "property_count": 25 },
{ "state": "OR", "annual_cost": 650000.00, "property_count": 12 }
],
"cost_by_property_type": [
{ "type": "residential", "annual_cost": 1800000.00, "property_count": 38 },
{ "type": "mixed_use", "annual_cost": 450000.00, "property_count": 9 }
],
"total_potential_savings": 124000.00,
"properties_with_savings": 23
}
GET /pm/analytics/benchmarks¶
Benchmark properties against each other and against portfolio/regional averages. Returns rankings, percentiles, and outlier detection.
| Auth required | Yes (Pro tier required) |
Query Parameters¶
| Parameter | Type | Default | Description |
|---|---|---|---|
portfolio_id |
string | - | Filter to a specific portfolio |
metric |
string | cost_per_sqft |
Benchmark metric: cost_per_sqft, cost_per_unit, cost_per_kwh, kwh_per_sqft |
Example Request¶
curl "https://app.meterbase.io/api/v1/pm/analytics/benchmarks?metric=cost_per_sqft" \
-H "Authorization: Bearer $TOKEN"
Example Response¶
200 OK
{
"metric": "cost_per_sqft",
"portfolio_avg": 1.80,
"portfolio_median": 1.65,
"rankings": [
{ "property_id": "prop_005", "name": "Oakwood Residences", "value": 2.45, "percentile": 95, "outlier": true },
{ "property_id": "prop_002", "name": "Harbor View Plaza", "value": 2.10, "percentile": 82, "outlier": false },
{ "property_id": "prop_001", "name": "Sunset Apartments", "value": 1.65, "percentile": 50, "outlier": false }
],
"outlier_threshold": 2.30,
"outlier_count": 1
}
Pro Tier Required
Benchmarking is available only on the Pro and Enterprise tiers. Free tier users will receive a 403 response with an upgrade prompt.
POST /pm/due-diligence¶
Model projected utility costs for a prospective property acquisition. Provide property characteristics and receive estimated energy costs based on comparable properties and local tariff rates.
| Auth required | Yes (Pro tier required) |
Request Body¶
| Field | Type | Required | Description |
|---|---|---|---|
address |
string | Yes | Property street address |
city |
string | Yes | City |
state |
string | Yes | Two-letter state code |
zip_code |
string | Yes | 5-digit ZIP code |
unit_count |
integer | Yes | Number of units |
sqft |
integer | No | Total square footage |
property_type |
string | No | Property type (default: residential) |
annual_revenue |
number | No | Projected annual revenue for NOI calculation |
Example Request¶
curl -X POST https://app.meterbase.io/api/v1/pm/due-diligence \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"address": "456 Oak Ave",
"city": "Portland",
"state": "OR",
"zip_code": "97201",
"unit_count": 64,
"sqft": 72000,
"annual_revenue": 1200000
}'
Example Response¶
200 OK
{
"property_summary": {
"address": "456 Oak Ave",
"city": "Portland",
"state": "OR",
"zip_code": "97201",
"unit_count": 64,
"sqft": 72000
},
"serving_utility": {
"id": 842,
"name": "Portland General Electric"
},
"estimated_costs": {
"annual_energy_cost": 86400.00,
"monthly_energy_cost": 7200.00,
"cost_per_unit": 112.50,
"cost_per_sqft": 1.20
},
"comparable_properties": 8,
"available_tariffs": 5,
"best_tariff": {
"name": "PGE Schedule 32",
"estimated_annual_cost": 78000.00
},
"noi_impact": {
"annual_revenue": 1200000,
"energy_pct_of_revenue": 7.2,
"estimated_noi_impact": 86400.00
}
}
GET /pm/properties/health¶
Retrieve health scores for all properties using a RAG (red/amber/green) status system. Health is determined by factors including bill anomalies, cost trends, savings opportunities, and data completeness.
| Auth required | Yes |
Query Parameters¶
| Parameter | Type | Default | Description |
|---|---|---|---|
portfolio_id |
string | - | Filter to a specific portfolio |
status |
string | - | Filter by health status: green, yellow, red |
Example Request¶
curl "https://app.meterbase.io/api/v1/pm/properties/health?status=red" \
-H "Authorization: Bearer $TOKEN"
Example Response¶
200 OK
{
"summary": {
"green": 32,
"yellow": 10,
"red": 5,
"total": 47
},
"properties": [
{
"property_id": "prop_005",
"name": "Oakwood Residences",
"status": "red",
"score": 35,
"issues": [
"Cost per sqft 36% above portfolio average",
"No bill data for 2+ months",
"Savings opportunity of $8,400/year not acted on"
]
},
{
"property_id": "prop_012",
"name": "Riverside Commons",
"status": "red",
"score": 28,
"issues": [
"Usage anomaly detected (42% above rolling average)",
"Missing tariff assignment"
]
}
]
}