curl --request POST \
--url https://data.apartmentiq.io/apartmentiq/api/v2/markets/bulk_forecasts \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"account_id": "{account_id}",
"time_period": "next_12_months",
"interval": "quarter"
}
'import requests
url = "https://data.apartmentiq.io/apartmentiq/api/v2/markets/bulk_forecasts"
payload = {
"account_id": "{account_id}",
"time_period": "next_12_months",
"interval": "quarter"
}
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify({account_id: '{account_id}', time_period: 'next_12_months', interval: 'quarter'})
};
fetch('https://data.apartmentiq.io/apartmentiq/api/v2/markets/bulk_forecasts', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://data.apartmentiq.io/apartmentiq/api/v2/markets/bulk_forecasts",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'account_id' => '{account_id}',
'time_period' => 'next_12_months',
'interval' => 'quarter'
]),
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>",
"Content-Type: application/json"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://data.apartmentiq.io/apartmentiq/api/v2/markets/bulk_forecasts"
payload := strings.NewReader("{\n \"account_id\": \"{account_id}\",\n \"time_period\": \"next_12_months\",\n \"interval\": \"quarter\"\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Authorization", "Bearer <token>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://data.apartmentiq.io/apartmentiq/api/v2/markets/bulk_forecasts")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"account_id\": \"{account_id}\",\n \"time_period\": \"next_12_months\",\n \"interval\": \"quarter\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://data.apartmentiq.io/apartmentiq/api/v2/markets/bulk_forecasts")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"account_id\": \"{account_id}\",\n \"time_period\": \"next_12_months\",\n \"interval\": \"quarter\"\n}"
response = http.request(request)
puts response.read_body{
"rows": [
{
"name": "Madison, WI Metro Area",
"geo_boundary_id": 4127,
"geo_type": "MSA",
"msa_name": null,
"submarket_name": null,
"period": "2026-Q3",
"rent_growth_yoy": 0.0211,
"rent_growth_yoy_downside": 0.0098,
"rent_growth_yoy_upside": 0.0342,
"rent_growth_yoy_bed_0": 0.0184,
"rent_growth_yoy_bed_0_downside": 0.0065,
"rent_growth_yoy_bed_0_upside": 0.031,
"rent_growth_yoy_bed_1": 0.0202,
"rent_growth_yoy_bed_1_downside": 0.0089,
"rent_growth_yoy_bed_1_upside": 0.0331,
"rent_growth_yoy_bed_2": 0.0237,
"rent_growth_yoy_bed_2_downside": 0.0114,
"rent_growth_yoy_bed_2_upside": 0.0376,
"rent_growth_yoy_bed_3": 0.0196,
"rent_growth_yoy_bed_3_downside": 0.0071,
"rent_growth_yoy_bed_3_upside": 0.0324,
"rent_growth_yoy_class_a": 0.0252,
"rent_growth_yoy_class_a_downside": 0.0128,
"rent_growth_yoy_class_a_upside": 0.0394,
"rent_growth_yoy_class_b": 0.0201,
"rent_growth_yoy_class_b_downside": 0.0087,
"rent_growth_yoy_class_b_upside": 0.0322,
"rent_growth_yoy_class_c": 0.0157,
"rent_growth_yoy_class_c_downside": 0.0038,
"rent_growth_yoy_class_c_upside": 0.0289,
"occupancy": 0.9433,
"occupancy_downside": 0.9275,
"occupancy_upside": 0.9581,
"absorption": 148,
"absorption_downside": 96,
"absorption_upside": 201,
"deliveries": 132,
"deliveries_downside": 74,
"deliveries_upside": 186
},
{
"name": "Downtown Madison",
"geo_boundary_id": 90114,
"geo_type": "Submarket",
"msa_name": "Madison, WI Metro Area",
"submarket_name": null,
"period": "2026-Q3",
"rent_growth_yoy": 0.0289,
"rent_growth_yoy_downside": 0.0142,
"rent_growth_yoy_upside": 0.0441,
"rent_growth_yoy_bed_0": 0.0251,
"rent_growth_yoy_bed_0_downside": 0.0107,
"rent_growth_yoy_bed_0_upside": 0.0402,
"rent_growth_yoy_bed_1": 0.0278,
"rent_growth_yoy_bed_1_downside": 0.0131,
"rent_growth_yoy_bed_1_upside": 0.0435,
"rent_growth_yoy_bed_2": 0.0314,
"rent_growth_yoy_bed_2_downside": 0.0168,
"rent_growth_yoy_bed_2_upside": 0.0479,
"rent_growth_yoy_bed_3": null,
"rent_growth_yoy_bed_3_downside": null,
"rent_growth_yoy_bed_3_upside": null,
"rent_growth_yoy_class_a": 0.0335,
"rent_growth_yoy_class_a_downside": 0.0188,
"rent_growth_yoy_class_a_upside": 0.0502,
"rent_growth_yoy_class_b": 0.0246,
"rent_growth_yoy_class_b_downside": 0.0104,
"rent_growth_yoy_class_b_upside": 0.0395,
"rent_growth_yoy_class_c": null,
"rent_growth_yoy_class_c_downside": null,
"rent_growth_yoy_class_c_upside": null,
"occupancy": 0.9504,
"occupancy_downside": 0.9312,
"occupancy_upside": 0.9648,
"absorption": 32,
"absorption_downside": 18,
"absorption_upside": 47,
"deliveries": 24,
"deliveries_downside": 0,
"deliveries_upside": 42
}
],
"pagination": {
"current_page": 1,
"total_pages": 4,
"total_count": 100,
"per_page": 25
}
}{
"error": "<string>",
"message": "<string>",
"quota_limit": 123,
"quota_usage": 123
}{
"error": "<string>",
"message": "<string>",
"quota_limit": 123,
"quota_usage": 123
}You have exceeded the rate limit for this API.
Bulk Market Forecasts
Retrieve bulk market forecast rows for geographic boundaries using optional geography filters, requested forecast fields, and pagination.
property_filters and filters are not supported.curl --request POST \
--url https://data.apartmentiq.io/apartmentiq/api/v2/markets/bulk_forecasts \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"account_id": "{account_id}",
"time_period": "next_12_months",
"interval": "quarter"
}
'import requests
url = "https://data.apartmentiq.io/apartmentiq/api/v2/markets/bulk_forecasts"
payload = {
"account_id": "{account_id}",
"time_period": "next_12_months",
"interval": "quarter"
}
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify({account_id: '{account_id}', time_period: 'next_12_months', interval: 'quarter'})
};
fetch('https://data.apartmentiq.io/apartmentiq/api/v2/markets/bulk_forecasts', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://data.apartmentiq.io/apartmentiq/api/v2/markets/bulk_forecasts",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'account_id' => '{account_id}',
'time_period' => 'next_12_months',
'interval' => 'quarter'
]),
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>",
"Content-Type: application/json"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://data.apartmentiq.io/apartmentiq/api/v2/markets/bulk_forecasts"
payload := strings.NewReader("{\n \"account_id\": \"{account_id}\",\n \"time_period\": \"next_12_months\",\n \"interval\": \"quarter\"\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Authorization", "Bearer <token>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://data.apartmentiq.io/apartmentiq/api/v2/markets/bulk_forecasts")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"account_id\": \"{account_id}\",\n \"time_period\": \"next_12_months\",\n \"interval\": \"quarter\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://data.apartmentiq.io/apartmentiq/api/v2/markets/bulk_forecasts")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"account_id\": \"{account_id}\",\n \"time_period\": \"next_12_months\",\n \"interval\": \"quarter\"\n}"
response = http.request(request)
puts response.read_body{
"rows": [
{
"name": "Madison, WI Metro Area",
"geo_boundary_id": 4127,
"geo_type": "MSA",
"msa_name": null,
"submarket_name": null,
"period": "2026-Q3",
"rent_growth_yoy": 0.0211,
"rent_growth_yoy_downside": 0.0098,
"rent_growth_yoy_upside": 0.0342,
"rent_growth_yoy_bed_0": 0.0184,
"rent_growth_yoy_bed_0_downside": 0.0065,
"rent_growth_yoy_bed_0_upside": 0.031,
"rent_growth_yoy_bed_1": 0.0202,
"rent_growth_yoy_bed_1_downside": 0.0089,
"rent_growth_yoy_bed_1_upside": 0.0331,
"rent_growth_yoy_bed_2": 0.0237,
"rent_growth_yoy_bed_2_downside": 0.0114,
"rent_growth_yoy_bed_2_upside": 0.0376,
"rent_growth_yoy_bed_3": 0.0196,
"rent_growth_yoy_bed_3_downside": 0.0071,
"rent_growth_yoy_bed_3_upside": 0.0324,
"rent_growth_yoy_class_a": 0.0252,
"rent_growth_yoy_class_a_downside": 0.0128,
"rent_growth_yoy_class_a_upside": 0.0394,
"rent_growth_yoy_class_b": 0.0201,
"rent_growth_yoy_class_b_downside": 0.0087,
"rent_growth_yoy_class_b_upside": 0.0322,
"rent_growth_yoy_class_c": 0.0157,
"rent_growth_yoy_class_c_downside": 0.0038,
"rent_growth_yoy_class_c_upside": 0.0289,
"occupancy": 0.9433,
"occupancy_downside": 0.9275,
"occupancy_upside": 0.9581,
"absorption": 148,
"absorption_downside": 96,
"absorption_upside": 201,
"deliveries": 132,
"deliveries_downside": 74,
"deliveries_upside": 186
},
{
"name": "Downtown Madison",
"geo_boundary_id": 90114,
"geo_type": "Submarket",
"msa_name": "Madison, WI Metro Area",
"submarket_name": null,
"period": "2026-Q3",
"rent_growth_yoy": 0.0289,
"rent_growth_yoy_downside": 0.0142,
"rent_growth_yoy_upside": 0.0441,
"rent_growth_yoy_bed_0": 0.0251,
"rent_growth_yoy_bed_0_downside": 0.0107,
"rent_growth_yoy_bed_0_upside": 0.0402,
"rent_growth_yoy_bed_1": 0.0278,
"rent_growth_yoy_bed_1_downside": 0.0131,
"rent_growth_yoy_bed_1_upside": 0.0435,
"rent_growth_yoy_bed_2": 0.0314,
"rent_growth_yoy_bed_2_downside": 0.0168,
"rent_growth_yoy_bed_2_upside": 0.0479,
"rent_growth_yoy_bed_3": null,
"rent_growth_yoy_bed_3_downside": null,
"rent_growth_yoy_bed_3_upside": null,
"rent_growth_yoy_class_a": 0.0335,
"rent_growth_yoy_class_a_downside": 0.0188,
"rent_growth_yoy_class_a_upside": 0.0502,
"rent_growth_yoy_class_b": 0.0246,
"rent_growth_yoy_class_b_downside": 0.0104,
"rent_growth_yoy_class_b_upside": 0.0395,
"rent_growth_yoy_class_c": null,
"rent_growth_yoy_class_c_downside": null,
"rent_growth_yoy_class_c_upside": null,
"occupancy": 0.9504,
"occupancy_downside": 0.9312,
"occupancy_upside": 0.9648,
"absorption": 32,
"absorption_downside": 18,
"absorption_upside": 47,
"deliveries": 24,
"deliveries_downside": 0,
"deliveries_upside": 42
}
],
"pagination": {
"current_page": 1,
"total_pages": 4,
"total_count": 100,
"per_page": 25
}
}{
"error": "<string>",
"message": "<string>",
"quota_limit": 123,
"quota_usage": 123
}{
"error": "<string>",
"message": "<string>",
"quota_limit": 123,
"quota_usage": 123
}You have exceeded the rate limit for this API.
Authorizations
Bearer authentication header of the form Bearer <token>, where <token> is your auth token.
Body
ApartmentIQ account ID used to set the request account context.
Forecast horizon.
next_12_months, next_3_years, next_5_years Forecast period bucket.
month, quarter, year Forecast metric field names to include in each market row.
rent_growth_yoy, rent_growth_yoy_downside, rent_growth_yoy_upside, rent_growth_yoy_bed_0, rent_growth_yoy_bed_0_downside, rent_growth_yoy_bed_0_upside, rent_growth_yoy_bed_1, rent_growth_yoy_bed_1_downside, rent_growth_yoy_bed_1_upside, rent_growth_yoy_bed_2, rent_growth_yoy_bed_2_downside, rent_growth_yoy_bed_2_upside, rent_growth_yoy_bed_3, rent_growth_yoy_bed_3_downside, rent_growth_yoy_bed_3_upside, rent_growth_yoy_class_a, rent_growth_yoy_class_a_downside, rent_growth_yoy_class_a_upside, rent_growth_yoy_class_b, rent_growth_yoy_class_b_downside, rent_growth_yoy_class_b_upside, rent_growth_yoy_class_c, rent_growth_yoy_class_c_downside, rent_growth_yoy_class_c_upside, occupancy, occupancy_downside, occupancy_upside, absorption, absorption_downside, absorption_upside, deliveries, deliveries_downside, deliveries_upside Market selection filters. Omit to page through all supported market boundaries.
Show child attributes
Show child attributes
Page number. Values less than 1 are treated as 1.
x >= 1Matched markets per page. Values above 100 are capped at 100. Interval-based rows expand each matched market into one row per period, so the number of rows returned can be greater than per_page.
1 <= x <= 100Response
Bulk market forecast rows and pagination metadata.
Paginated market forecast export results.
Market forecast rows for the matched markets on the current page. Each matched market is expanded into one row per interval period, so the number of rows returned can be greater than the per_page value. Each row includes market identity fields, an output period, and any requested forecast fields.
Show child attributes
Show child attributes
Pagination details for matched markets, not the expanded interval-period rows.
Show child attributes
Show child attributes