The Airline Travel Demand Tracker

 

Where Is U.S. Air Travel Demand Growing and Shrinking the Fastest?

Tracking throughput growth reveals where U.S. travel demand is actively shifting, as rapidly expanding passenger volume often points to broader economic trends such as emerging business centers, booming regional tourism, or airlines aggressively expanding their domestic route networks. Similarly, shrinking passenger traffic can reflect changes in local economies or shifting airline capacities.

To accurately identify these surging markets, the following section isolates the top and bottom 15 airports based on changes in daily TSA passenger volume. Because a modest percentage increase at a massive international gateway presents a very different operational reality than a major traffic spike at a regional airfield, the data chart isolates TSA throughput changes among primary hub airports.

Following this visual breakdown, a comprehensive table details the throughput growth metrics across the entire tracked aviation landscape. When evaluating these figures, note that data anomalies — such as missing historical baselines at smaller facilities or dramatic, temporary traffic declines caused by TSA staffing and reporting disruptions during the 2026 partial federal government shutdown — often reflect administrative or operational constraints rather than shifts in true consumer demand.

Largest Change in Average Daily TSA Throughput

(Primary Hub Airports)

Percentage Change in Average Daily TSA Throughput

Change in Average Daily TSA Throughput by Airport

function drawAirportsMonthlyChangeDashboard() {
if (!globalTsaData.airportsMonthlyChange) return;

function safeParseFloat(val) {
if (val == null) return null;
if (val === “”) return null;
if (typeof val === ‘number’) return val;
var cleaned = val.toString().replace(/[$,]/g, ”);
var num = parseFloat(cleaned);
if (isNaN(num)) return null;
return num;
}

function safeString(val) {
if (val == null) return “”;
return String(val);
}

function calculateNiceTicks(maxValue) {
if (!maxValue) return { max: 100, ticks: [0, 50, 100] };
if (maxValue <= 0) return { max: 100, ticks: [0, 50, 100] };

var stepCandidates = [1, 2, 5, 10, 20, 50, 100, 200, 250, 300, 400, 500, 1000, 5000, 10000, 50000];
var bestStep = 100;

for (var i = 0; i < stepCandidates.length; i++) {
var step = stepCandidates[i];
if ((maxValue / step) <= 6) {
bestStep = step;
break;
}
}

var ticks = [];
var currentTick = 0;
while (currentTick <= maxValue) {
ticks.push(currentTick);
currentTick += bestStep;
}
if (ticks[ticks.length – 1] < maxValue) {
ticks.push(currentTick);
}
return { max: ticks[ticks.length – 1], ticks: ticks };
}

// BUTTERFLY CHART FOR PRIMARY HUBS
var rawMonth = globalTsaData.metadata.filter(function(metadataItem) {
return metadataItem.Description === ‘Newest_Month’;
})[0].Value;
var cleansedDate = new Date(rawMonth.replace(/-/g, ‘/’));
var formattedDate = cleansedDate.toLocaleDateString(‘en-US’, { month: ‘short’, year: ‘numeric’ });
var prevDate = new Date(cleansedDate);
prevDate.setFullYear(prevDate.getFullYear() – 1);
var formattedDatePrev = prevDate.toLocaleDateString(‘en-US’, { month: ‘short’, year: ‘numeric’ });
var formattedDateRange = formattedDatePrev + ‘–’ + formattedDate;

var primaryHubRows = globalTsaData.airportsMonthlyChange.filter(function(row) {
if (!row) {
return false;
}
if (row.Hub_Size === ‘L’ || row.Hub_Size === ‘M’ || row.Hub_Size === ‘S’) {
return true;
}
return false;
}).map(function(row) {
return {
airport: safeString(row.Airport),
value: safeParseFloat(row.Daily_Pax_YoY_Change)
};
}).filter(function(item) {
return item.value !== null;
});

primaryHubRows.sort(function(a, b) {
return b.value – a.value;
});

var increaseRows = primaryHubRows.slice(0, 15);
var decreaseRows = primaryHubRows.filter(function(item) {
return item.value 0) {
formattedInc = ‘+’ + row.value.toLocaleString(undefined, { minimumFractionDigits: 1, maximumFractionDigits: 1 }) + ‘%’;
} else {
formattedInc = row.value.toLocaleString(undefined, { minimumFractionDigits: 1, maximumFractionDigits: 1 }) + ‘%’;
}
increaseData.addRow([row.airport, {
v: row.value,
f: formattedInc
}]);
});

var decreaseData = new google.visualization.DataTable();
decreaseData.addColumn(‘string’, ‘Airport’);
decreaseData.addColumn(‘number’, ‘Decrease’);
decreaseRows.forEach(function(row) {
var absValue = Math.abs(row.value);
var formattedDec = absValue === 0 ? ‘0%’ : ‘-‘ + absValue.toLocaleString(undefined, { minimumFractionDigits: 1, maximumFractionDigits: 1 }) + ‘%’;
decreaseData.addRow([row.airport, {
v: absValue,
f: formattedDec
}]);
});

var globalMaxValue = 0;
if (increaseData.getNumberOfRows() > 0) {
globalMaxValue = Math.max(globalMaxValue, increaseData.getColumnRange(1).max);
}
if (decreaseData.getNumberOfRows() > 0) {
globalMaxValue = Math.max(globalMaxValue, decreaseData.getColumnRange(1).max);
}

var axisInfo = calculateNiceTicks(globalMaxValue);
var formattedIncreaseTicks = axisInfo.ticks.map(function(val) {
var label = val === 0 ? ‘0%’ : (‘+’ + val.toFixed(0) + ‘%’);
return { v: val, f: label };
});
var formattedDecreaseTicks = axisInfo.ticks.map(function(val) {
var label = val === 0 ? ‘0%’ : (‘-‘ + val.toFixed(0) + ‘%’);
return { v: val, f: label };
});

var numRows = Math.max(increaseData.getNumberOfRows(), decreaseData.getNumberOfRows(), 1);
var rowHeight = window.innerWidth <= 768 ? 55 : 35;
var rankHeight = numRows * rowHeight;
var chartHeight = rankHeight + 60;
var finalRankHeight = Math.max(400, rankHeight);
var finalChartHeight = Math.max(400, chartHeight);

var rankContainer = document.getElementById(‘rank_col_change_top’);
if (rankContainer) {
var rankHTML = ”;
for (var r = 1; r <= numRows; r++) {
rankHTML += ‘

‘ + r + ‘

‘;
}
rankContainer.innerHTML = rankHTML;
rankContainer.style.height = finalRankHeight + ‘px’;
}

var decreaseContainer = document.getElementById(‘chart_dec_change_top’);
if (decreaseContainer) {
if (decreaseData.getNumberOfRows() > 0) {
decreaseContainer.style.height = finalChartHeight + ‘px’;

var decreaseChartOptions = {
legend: ‘none’,
colors: [‘#0f76bf’],
chartArea: { left: 20, top: 20, bottom: 40, right: 10 },
hAxis: {
direction: -1,
viewWindow: { min: 0, max: axisInfo.max },
ticks: formattedDecreaseTicks,
gridlines: { count: 4, color: ‘#f0f0f0’ },
textStyle: { fontSize: 11, color: ‘#666’ }
},
vAxis: {
textPosition: ‘in’,
textStyle: { fontSize: 11, color: ‘#000’ },
gridlines: { color: ‘transparent’ }
},
tooltip: { isHtml: true },
backgroundColor: ‘transparent’
};

var decreaseChart = new google.visualization.BarChart(decreaseContainer);
google.visualization.events.addListener(decreaseChart, ‘ready’, function () {
var labels = decreaseContainer.getElementsByTagName(‘text’);
var bounds = decreaseChart.getChartLayoutInterface().getChartAreaBoundingBox();
for (var i = 0; i bounds.top) {
if (y 0) {
increaseContainer.style.height = finalChartHeight + ‘px’;

var increaseChartOptions = {
legend: ‘none’,
colors: [‘#f79720’],
chartArea: { left: 10, top: 20, bottom: 40, right: 20 },
hAxis: {
viewWindow: { min: 0, max: axisInfo.max },
ticks: formattedIncreaseTicks,
gridlines: { count: 4, color: ‘#f0f0f0’ },
textStyle: { fontSize: 11, color: ‘#666’ }
},
vAxis: {
textPosition: ‘in’,
textStyle: { fontSize: 11, color: ‘#000’ },
gridlines: { color: ‘transparent’ }
},
tooltip: { isHtml: true },
backgroundColor: ‘transparent’
};

var increaseChart = new google.visualization.BarChart(increaseContainer);
google.visualization.events.addListener(increaseChart, ‘ready’, function () {
var labels = increaseContainer.getElementsByTagName(‘text’);
var bounds = increaseChart.getChartLayoutInterface().getChartAreaBoundingBox();
for (var i = 0; i bounds.top) {
if (y = 0 ? ‘+’ : ”) + perVal.toLocaleString(undefined, {minimumFractionDigits: 1, maximumFractionDigits: 1}) + ‘%’
};
}
};
}

function createStringColumn(colIndex, labelText) {
return {
type: ‘string’,
label: labelText,
calc: function(dt, row) {
return safeString(dt.getValue(row, colIndex));
}
};
}

function createHubSizeColumn(colIndex, labelText) {
var hubMap = { ‘L’: ‘Large Hub’, ‘M’: ‘Medium Hub’, ‘S’: ‘Small Hub’, ‘N’: ‘Nonhub Primary’, ‘None’: ‘Nonprimary’ }
return {
type: ‘string’,
label: labelText,
calc: function(dt, row) {
var raw = safeString(dt.getValue(row, colIndex));
if (!raw) return hubMap[‘None’] || ‘Nonprimary’;
return hubMap[raw] || ‘Nonprimary’;
}
};
}

finalTableView.setColumns([
{
type: ‘number’,
label: ‘Rank’,
calc: function(dt, row) {
return row + 1;
}
},
createStringColumn(0, dataTable.getColumnLabel(0) || ‘Airport’),
createStringColumn(1, dataTable.getColumnLabel(1) || ‘Location’),
createPercentColumn(2, dataTable.getColumnLabel(2) || ‘Percentage Change in Average Daily TSA Throughput (1-Year)’),
createPercentColumn(3, dataTable.getColumnLabel(3) || ‘Percentage Change in Average Daily TSA Throughput (2-Year)’),
createIntegerColumn(4, dataTable.getColumnLabel(4) || ‘Average Daily TSA Throughput’),
createHubSizeColumn(5, dataTable.getColumnLabel(5) || ‘Hub Size’)
]);

dashboard.bind(stringFilter, tableChart);
dashboard.draw(finalTableView);
}

  • The Worst Airports for International Travel This Holiday Season
  • The Worst U.S. Airports for International Travel This Summer
  • Airports With the Worst Security Lines Over the Holidays

Data Usage Guidelines

This data is available for public use. We simply request a citation and a link to Upgraded Points for the initial reference in your work. Examples include:

  • “According to the latest data from Upgraded Points, a company that provides advice on credit card reward programs and other financial products …”
  • “Data is provided by Upgraded Points, a travel and finance website.”

Sources

  • Transportation Security Administration. (2026). FOIA Electronic Reading Room [Data Set]. Retrieved weekly from https://www.tsa.gov/foia/readingroom.