The U.S. Airfare Watch Index

google.charts.load(‘current’, {‘packages’:[‘corechart’, ‘bar’, ‘controls’, ‘table’]});

var globalMasterData = null;

google.charts.setOnLoadCallback(fetchMasterData);

function fetchMasterData() {
var sheetId = ‘1aNw1yceYjtPZNdFUEaQQOV10jmZ5j8EZFD9CcM9mehA’;
var masterGid = ‘743507343’;
var query = new google.visualization.Query(‘https://docs.google.com/spreadsheets/d/’ + sheetId + ‘/gviz/tq?gid=’ + masterGid + ‘&headers=1&range=A:BM’);
query.send(handleMasterResponse);
}

function handleMasterResponse(response) {
if (response.isError()) {
console.error(‘Error fetching master data: ‘ + response.getMessage());
return;
}

globalMasterData = response.getDataTable();

var drawQueue = [];
if (typeof drawList === ‘function’) drawQueue.push(drawList);
if (typeof drawTimeSeriesDashboard === ‘function’) drawQueue.push(drawTimeSeriesDashboard);
if (typeof drawAirportChartsAndTable === ‘function’) drawQueue.push(drawAirportChartsAndTable);
if (typeof drawRouteChartsAndTable === ‘function’) drawQueue.push(drawRouteChartsAndTable);
if (typeof drawButterflyChart === ‘function’) drawQueue.push(drawButterflyChart);
if (typeof drawHikesTable === ‘function’) drawQueue.push(drawHikesTable);
if (typeof drawDropsTable === ‘function’) drawQueue.push(drawDropsTable);

function processQueue() {
if (drawQueue.length > 0) {
var nextDrawFn = drawQueue.shift();
nextDrawFn();
setTimeout(processQueue, 250);
}
}

processQueue();
}

/* =========================================
GLOBAL CHART & GRID LAYOUT
========================================= */
.charts-grid-container {
display: grid;
gap: 20px;
grid-template-columns: 1fr 1fr;
margin-bottom: 20px;
}

@media (max-width: 768px) {
.charts-grid-container {
grid-template-columns: 1fr;
}
}

.chart-box {
background: #fff;
border: 1px solid #eee;
box-shadow: 0 2px 5px rgba(0,0,0,0.05);
margin-bottom: 20px;
padding: 15px;
}

.chart-title {
color: #2c3d4f;
font-family: ‘Helvetica Neue’, Helvetica, Arial, sans-serif !important;
font-size: 16px;
font-weight: bold;
letter-spacing: 0.5px;
margin-bottom: 10px;
text-align: center;
}

.chart-bottom-label {
color: #999;
font-family: ‘Helvetica Neue’, Helvetica, Arial, sans-serif !important;
font-size: 12px;
font-style: italic;
padding-top: 5px;
text-align: center;
}

.google-chart {
max-height: 800px;
min-height: 400px;
width: 100%;
}

/* =========================================
TABLE CONTAINER
========================================= */
.table_container {
background: #fff;
font-family: ‘Helvetica Neue’, Helvetica, Arial, sans-serif !important;
padding: 0 !important;
position: relative;
}

table, th, td {
font-size: 12px !important;
}

/* =========================================
SEARCH FILTER
========================================= */
.filter_div {
background: #fff !important;
border-bottom: 1px solid #ddd !important;
box-sizing: border-box !important;
margin-bottom: 0 !important;
padding: 15px 15px 15px 0px !important;
position: sticky !important;
top: 0 !important;
width: 100% !important;
z-index: 20 !important;
display: flex !important;
align-items: center !important;
}

.google-visualization-controls-label {
color: #333 !important;
font-weight: bold !important;
font-size: 14px !important;
margin-right: 12px !important;
}

.google-visualization-controls-stringfilter input {
border: 1px solid #ddd !important;
border-radius: 4px !important;
padding: 8px 12px !important;
font-size: 14px !important;
color: #555 !important;
background-color: #fff !important;
box-shadow: none !important;
outline: none !important;
width: 250px !important;
transition: all 0.2s ease !important;
}

.google-visualization-controls-stringfilter input:focus {
border-color: #2c3d4f !important;
box-shadow: 0 0 0 3px rgba(44, 61, 79, 0.1) !important;
}

/* =========================================
TABLE WRAPPER (The Scroll Window)
========================================= */
#table_div {
height: 600px !important;
overflow-x: auto !important;
overflow-y: auto !important;
padding: 0 !important;
width: 100% !important;
}

#table_div .google-visualization-table > div:first-child {
height: auto !important;
overflow: visible !important;
}

/* =========================================
STICKY HEADER & TABLE STYLING
========================================= */
.google-visualization-table-tr-head {
background-color: transparent !important;
}

.google-visualization-table-table .google-visualization-table-th {
background-color: #f1f7fb !important;
background-image: none !important;
border-bottom: 1px solid #ccc !important;
border-top: none !important;
box-shadow: 0 2px 4px rgba(0,0,0,0.05);
height: auto !important;
position: -webkit-sticky !important;
position: sticky !important;
top: 0 !important;
vertical-align: middle !important;
white-space: normal !important;
z-index: 10 !important;
}

.google-visualization-table-table {
border-bottom: none;
border-collapse: separate !important;
border-spacing: 0;
border-top: none;
margin: 0 !important;
width: 100%;
}

.google-visualization-table-td,
.google-visualization-table-th {
padding: 5px 10px !important;
}

.google-visualization-table .google-visualization-table-td.google-visualization-table-type-number {
text-align: center !important;
}

/* =========================================
PAGINATION STYLING
========================================= */
.google-visualization-table-div-page {
background-color: #fff !important;
border-top: 1px solid #eee !important;
padding: 15px !important;
background-image: none !important;
box-shadow: none !important;
}

.google-visualization-table-page-number {
display: inline-block !important;
padding: 6px 12px !important;
margin: 0 4px !important;
border-radius: 4px !important;
text-decoration: none !important;
color: #555 !important;
font-size: 13px !important;
border: 1px solid transparent !important;
transition: all 0.2s ease !important;
}

.google-visualization-table-page-number:hover {
background-color: #f5f5f5 !important;
color: #333 !important;
border-color: #ddd !important;
}

.google-visualization-table-page-number.current {
background-color: #2c3d4f !important;
color: #fff !important;
border-color: #2c3d4f !important;
font-weight: 600 !important;
}

.goog-custom-button {
background: #fff !important;
border: 1px solid #ddd !important;
border-radius: 4px !important;
margin: 0 4px !important;
vertical-align: middle !important;
box-shadow: none !important;
outline: none !important;
height: auto !important;
}

.goog-custom-button-outer-box,
.goog-custom-button-inner-box {
border: none !important;
padding: 0 !important;
margin: 0 !important;
background: transparent !important;
}

.goog-custom-button-inner-box {
padding: 5px 10px !important;
}

.goog-custom-button-hover {
background-color: #f5f5f5 !important;
border-color: #bbb !important;
}

.goog-custom-button-disabled {
opacity: 0.3 !important;
background-color: #fff !important;
border-color: #eee !important;
cursor: not-allowed !important;
}

.goog-custom-button-inner-box span {
vertical-align: middle !important;
}

/* =========================================
CUSTOM TOOLTIP STYLING
========================================= */
div.google-visualization-tooltip {
padding: 0 !important;
margin: 0 !important;
border: 1px solid #e0e0e0 !important;
box-shadow: 0 4px 10px rgba(0,0,0,0.1) !important;
border-radius: 6px !important;
background-color: #ffffff !important;
font-family: ‘Helvetica Neue’, Helvetica, Arial, sans-serif !important;
z-index: 9999 !important;
}

ul.google-visualization-tooltip-item-list {
padding: 10px !important;
margin: 0 !important;
list-style-type: none !important;
}

li.google-visualization-tooltip-item {
margin: 5px 0 !important;
padding: 0 !important;
font-size: 13px !important;
line-height: 1.4 !important;
color: #333 !important;
white-space: normal !important;
}

li.google-visualization-tooltip-item:first-child {
margin-top: 0 !important;
}

span.google-visualization-tooltip-item-label {
font-weight: 600 !important;
color: #555 !important;
margin-right: 0 !important;
}

span.google-visualization-tooltip-item-value {
font-weight: 400 !important;
color: #000 !important;
}

/* =========================================
BUTTERFLY CHART
========================================= */
.bf-container {
display: flex;
width: 100%;
position: relative;
font-family: ‘Helvetica Neue’, Helvetica, Arial, sans-serif !important;
}

.bf-chart-side {
width: calc((100% – 15px) / 2);
height: 100%;
}

.bf-rank-col {
width: 15px;
display: flex;
flex-direction: column;
margin-top: 20px;
}

.bf-rank-item {
flex: 1;
display: flex;
align-items: center;
justify-content: center;
font-size: 12px;
font-weight: normal;
}

/* =========================================
TABLE LAYOUT
========================================= */
#table_1 .google-visualization-table-table,
#table_2 .google-visualization-table-table,
#table_3 .google-visualization-table-table,
#table_4 .google-visualization-table-table,
#table_5 .google-visualization-table-table {
table-layout: fixed !important;
width: 100% !important;
}

#table_1 .google-visualization-table-table { min-width: 480px !important; }
#table_2 .google-visualization-table-table { min-width: 640px !important; }
#table_3 .google-visualization-table-table { min-width: 580px !important; }
#table_4 .google-visualization-table-table,
#table_5 .google-visualization-table-table { min-width: 620px !important; }

/* All cells: prevent content from blowing out fixed columns */
#table_1 .google-visualization-table-table td,
#table_1 .google-visualization-table-table th,
#table_2 .google-visualization-table-table td,
#table_2 .google-visualization-table-table th,
#table_3 .google-visualization-table-table td,
#table_3 .google-visualization-table-table th,
#table_4 .google-visualization-table-table td,
#table_4 .google-visualization-table-table th,
#table_5 .google-visualization-table-table td,
#table_5 .google-visualization-table-table th {
box-sizing: border-box !important;
overflow: hidden !important;
text-overflow: ellipsis !important;
white-space: normal !important;
}

/* =========================================
TABLE 1 — Date / Airfare / Airfare Adjusted
========================================= */
#table_1 .google-visualization-table-table td:nth-child(1),
#table_1 .google-visualization-table-table th:nth-child(1) { width: 18% !important; }

#table_1 .google-visualization-table-table td:nth-child(2),
#table_1 .google-visualization-table-table th:nth-child(2) { width: 41% !important; }

#table_1 .google-visualization-table-table td:nth-child(3),
#table_1 .google-visualization-table-table th:nth-child(3) { width: 41% !important; }

/* =========================================
TABLE 2 — Airport Rankings
========================================= */
#table_2 .google-visualization-table-table td:nth-child(1),
#table_2 .google-visualization-table-table th:nth-child(1) { width: 8% !important; }

#table_2 .google-visualization-table-table td:nth-child(2),
#table_2 .google-visualization-table-table th:nth-child(2) { width: 26% !important; }

#table_2 .google-visualization-table-table td:nth-child(3),
#table_2 .google-visualization-table-table th:nth-child(3) { width: 18% !important; }

#table_2 .google-visualization-table-table td:nth-child(4),
#table_2 .google-visualization-table-table th:nth-child(4) { width: 17% !important; }

#table_2 .google-visualization-table-table td:nth-child(5),
#table_2 .google-visualization-table-table th:nth-child(5) { width: 17% !important; }

#table_2 .google-visualization-table-table td:nth-child(6),
#table_2 .google-visualization-table-table th:nth-child(6) { width: 14% !important; }

/* =========================================
TABLE 3 — Route Rankings
========================================= */
#table_3 .google-visualization-table-table td:nth-child(1),
#table_3 .google-visualization-table-table th:nth-child(1) { width: 13% !important; }

#table_3 .google-visualization-table-table td:nth-child(2),
#table_3 .google-visualization-table-table th:nth-child(2) {
width: 31% !important;
}

#table_3 .google-visualization-table-table td:nth-child(3),
#table_3 .google-visualization-table-table th:nth-child(3) { width: 14% !important; }

#table_3 .google-visualization-table-table td:nth-child(4),
#table_3 .google-visualization-table-table th:nth-child(4) { width: 14% !important; }

#table_3 .google-visualization-table-table td:nth-child(5),
#table_3 .google-visualization-table-table th:nth-child(5) { width: 14% !important; }

#table_3 .google-visualization-table-table td:nth-child(6),
#table_3 .google-visualization-table-table th:nth-child(6) { width: 14% !important; }

/* =========================================
TABLES 4 and 5 — City-Pair Hikes / Drops
========================================= */
#table_4 .google-visualization-table-table td:nth-child(1),
#table_4 .google-visualization-table-table th:nth-child(1),
#table_5 .google-visualization-table-table td:nth-child(1),
#table_5 .google-visualization-table-table th:nth-child(1) { width: 9% !important; }

#table_4 .google-visualization-table-table td:nth-child(2),
#table_4 .google-visualization-table-table th:nth-child(2),
#table_5 .google-visualization-table-table td:nth-child(2),
#table_5 .google-visualization-table-table th:nth-child(2) { width: 27% !important; }

#table_4 .google-visualization-table-table td:nth-child(3),
#table_4 .google-visualization-table-table th:nth-child(3),
#table_5 .google-visualization-table-table td:nth-child(3),
#table_5 .google-visualization-table-table th:nth-child(3) { width: 16% !important; }

#table_4 .google-visualization-table-table td:nth-child(4),
#table_4 .google-visualization-table-table th:nth-child(4),
#table_5 .google-visualization-table-table td:nth-child(4),
#table_5 .google-visualization-table-table th:nth-child(4) { width: 16% !important; }

#table_4 .google-visualization-table-table td:nth-child(5),
#table_4 .google-visualization-table-table th:nth-child(5),
#table_5 .google-visualization-table-table td:nth-child(5),
#table_5 .google-visualization-table-table th:nth-child(5) { width: 16% !important; }

#table_4 .google-visualization-table-table td:nth-child(6),
#table_4 .google-visualization-table-table th:nth-child(6),
#table_5 .google-visualization-table-table td:nth-child(6),
#table_5 .google-visualization-table-table th:nth-child(6) { width: 16% !important; }

The U.S. airlines keep the country together, connecting families, powering commerce, and fueling tourism. Yet for businesses and regular people, it’s essential to keep a constant and scrutinizing eye on what that connectivity costs them — the airfare.

As ticket prices fall or rise, so do household budgets and corporate bottom lines, in turn acting as a bellwether for broader economic health. Upgraded Points’ U.S. Airfare Watch Index tracks these key pricing indicators to provide a clear, data-driven view of where travel costs stand and where they are heading.

This portal serves as a high-authority resource for the airline industry, synthesizing complex datasets from the Department of Transportation and the Bureau of Transportation Statistics. By transforming raw federal data into intuitive charts and interactive tables, travelers, analysts, and researchers get a living snapshot of the commercial-airfare landscape. To maintain peak accuracy, our researchers reanalyze the data every quarter, ensuring insights are always synchronized with the most recent government reports.

Below are several key statistics from the most recent 4 quarters of available data:

function drawList() {
if (!globalMasterData) return;

var columnIndex = 0;

var view = new google.visualization.DataView(globalMasterData);
view.setColumns([columnIndex]);

var filteredRows = view.getFilteredRows([
{
column: 0,
test: function(val) {
if (val == null) { return false; }
if (val === ”) { return false; }
return true;
}
}
])
view.setRows(filteredRows);

var container = document.getElementById(‘list_div’);
container.innerHTML = ”;
var title = document.createElement(‘h2’);
title.textContent = view.getColumnLabel(0);
container.appendChild(title);

var ul = document.createElement(‘ul’);
for (var i = 0; i < view.getNumberOfRows(); i++) {
var li = document.createElement('li');
li.innerHTML = view.getFormattedValue(i, 0);
ul.appendChild(li);
}

container.appendChild(ul);
}

How Much Are Airline Prices Increasing?

Inflation-adjusted average airfare data proves that flying is historically cheap, but it rarely feels that way. This disconnect is largely due to unbundling — a strategy in which airlines charge for perks like checked bags, legroom, and seat selection to keep base fares low. While the nominal airfare has remained in check, the addition of these fees — combined with the rise of low-cost carriers, which have added airline competition — has shifted the cost from a flat rate to an obscured, customizable menu.

To visualize these trends, the following section analyzes data from:

  • U.S. Department of Transportation’s Domestic Airline Consumer Airfare Report
  • Bureau of Labor Statistics’ Consumer Price Index

By indexing average fares from the 1,000 largest city-pair markets to the broader CPI, the chart tracks the raw cost of air travel, excluding baggage fees and other common costs.

The Average Airfare in the U.S.

function drawTimeSeriesDashboard() {
if (!globalMasterData) return;

var view = new google.visualization.DataView(globalMasterData);

view.setColumns([1, 2, 3]);

view.setRows(view.getFilteredRows([
{
column: 0,
test: function(val) {
if (val == null) { return false; }
if (val === ”) { return false; }
return true;
}
}
]));

var data = view.toDataTable();

var distinctQuarters = [‘Q1’, ‘Q2’, ‘Q3’, ‘Q4’];
for (var i = 0; i < data.getNumberOfRows(); i++) {
var dateVal = data.getValue(i, 0);
if (dateVal) {
if (typeof dateVal.getMonth === 'function') {
var month = dateVal.getMonth();
var qIndex = Math.floor(month / 3);
var year = dateVal.getFullYear();
var formattedDate = distinctQuarters[qIndex] + ' ' + year;
data.setFormattedValue(i, 0, formattedDate);
}
}
}

var currencyFormatter = new google.visualization.NumberFormat({
prefix: '$',
fractionDigits: 2
});

currencyFormatter.format(data, 1);
currencyFormatter.format(data, 2);

var chartOptions = {
curveType: 'function',
height: 500,
width: '100%',
backgroundColor: { fill: 'transparent' },
chartArea: { left: 50, top: 40, bottom: 80, right: 10 },
hAxis: { gridlines: { color: 'none' } },
tooltip: { isHtml: true },
vAxis: { format: '$#', textStyle: { color: '#333' } },
legend: { position: 'bottom' },
series: { 0: { color: '#0f76bf' }, 1: { color: '#f79720' } }
};

var chart = new google.visualization.LineChart(document.getElementById('line_chart_1'));
chart.draw(data, chartOptions);

var table = new google.visualization.Table(document.getElementById('table_1'));
var tableOptions = {
showRowNumber: false,
width: '100%',
height: '100%',
page: 'enable',
pageSize: 10
};
table.draw(data, tableOptions);
}

Most and Least Expensive U.S. Airports

The actual airfare paid varies significantly by departure point. This geographic disparity is driven by local market dynamics — specifically the level of competition from other airlines, the presence of low-cost carriers, the volume of direct routes offered, and the availability of cheaper alternatives at nearby airports.

To visualize these trends, the following analysis synthesizes data from the Bureau of Transportation Statistics’ Airline Origin and Destination survey to compare the latest 4 quarters of airfare data among U.S. airports. Average airfares are calculated from domestic itinerary fares, including round-trip or one-way fares for which no return was purchased. Averages include taxes and fees applied at purchase, but exclude anomalous data, bulk fares, and “zero fares”—such as frequent flyer awards and employee travel.

The Most Expensive Large Hub Airports

The Least Expensive Large Hub Airports

The Most Expensive Medium Hub Airports

The Least Expensive Medium Hub Airports

The Most Expensive Small Hub Airports

The Least Expensive Small Hub Airports

Average Itinerary Airfare by Airport

function drawAirportChartsAndTable() {
if (!globalMasterData) 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 = [100, 200, 250, 300, 400, 500, 1000];
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 };
}

var chartsConfig = [
{ cols: [9, 10], id: 'chart_large_hub_top' },
{ cols: [11, 12], id: 'chart_large_hub_bottom' },
{ cols: [13, 14], id: 'chart_medium_hub_top' },
{ cols: [15, 16], id: 'chart_medium_hub_bottom' },
{ cols: [17, 18], id: 'chart_small_hub_top' },
{ cols: [19, 20], id: 'chart_small_hub_bottom' }
];

var loadedViews = {};
var globalMaxValue = 0;

chartsConfig.forEach(function(config) {
var view = new google.visualization.DataView(globalMasterData);

// Use direct column references so the sheet's formatted value (e.g. "$482.31")
// is preserved as the f value — matching the same pattern as the route charts
view.setColumns(config.cols);

view.setRows(view.getFilteredRows([
{
column: 0,
test: function(val) {
if (val == null) return false;
if (val === '') return false;
if (typeof val === 'string') {
if (val.toLowerCase() === 'airport') return false;
}
return true;
}
}
]));

var data = view.toDataTable();
var rows = data.getNumberOfRows();

// setCell with no 4th argument leaves the existing f value intact,
// so the sheet's "$482.31" string is preserved for the tooltip while
// v becomes a clean number for bar-length calculation
for (var i = 0; i 0) {
var currentMax = data.getColumnRange(1).max;
if (currentMax > globalMaxValue) globalMaxValue = currentMax;
}

loadedViews[config.id] = data;
});

var axisInfo = calculateNiceTicks(globalMaxValue);
var formattedTicks = axisInfo.ticks.map(function(val) {
return { v: val, f: ‘$’ + val.toLocaleString() };
});

chartsConfig.forEach(function(config) {
var elementId = config.id;
var data = loadedViews[elementId];
var colLabel = data.getColumnLabel(1) || ‘Average Fare ($)’;

var numRows = data.getNumberOfRows();
var dynamicHeight = (numRows * 35) + 60;
var finalHeight = Math.max(400, dynamicHeight);
var container = document.getElementById(elementId);
container.style.height = finalHeight + ‘px’;

var parentBox = container.parentNode;
var labelId = elementId + ‘_axis_label’;
var labelDiv = document.getElementById(labelId);
if (!labelDiv) {
labelDiv = document.createElement(‘div’);
labelDiv.id = labelId;
labelDiv.className = ‘chart-bottom-label’;
parentBox.appendChild(labelDiv);
}
labelDiv.innerText = colLabel;

var options = {
chartArea: { left: ‘50%’, top: 10, bottom: 40, right: 15 },
height: ‘100%’,
width: ‘100%’,
bar: { groupWidth: ‘70%’ },
legend: { position: ‘none’ },
hAxis: {
title: null,
ticks: formattedTicks,
viewWindow: { min: 0, max: axisInfo.max },
textPosition: ‘out’,
textStyle: { color: ‘#999’, fontSize: 11 },
gridlines: { color: ‘#f0f0f0’ }
},
tooltip: { isHtml: true },
vAxis: {
textStyle: { fontSize: 11, color: ‘#333’ },
showTextEvery: 1
},
colors: [‘#f79720’],
backgroundColor: ‘transparent’
};

var chart = new google.visualization.BarChart(container);
chart.draw(data, options);
});

var tableView = new google.visualization.DataView(globalMasterData);
tableView.setColumns([4, 5, 6, 7, 8]);
tableView.setRows(tableView.getFilteredRows([
{
column: 0,
test: function(val) {
if (val == null) return false;
if (val === ”) return false;
return true;
}
}
]));

var tableData = tableView.toDataTable();

if (tableData.getNumberOfRows() > 0) {
var firstCell = tableData.getValue(0, 0);
if (typeof firstCell === ‘string’) {
if (firstCell.toLowerCase() === ‘airport’) {
tableData.removeRow(0);
}
}
}

var dashboard = new google.visualization.Dashboard(document.getElementById(‘dashboard_div_1’));

var stringFilter = new google.visualization.ControlWrapper({
‘controlType’: ‘StringFilter’,
‘containerId’: ‘filter_div_2’,
‘options’: {
‘filterColumnIndex’: 1,
‘matchType’: ‘any’,
‘ui’: { ‘label’: ‘Search Airport:’, ‘placeholder’: ‘Type an airport name…’ }
}
});

var tableChart = new google.visualization.ChartWrapper({
‘chartType’: ‘Table’,
‘containerId’: ‘table_2’,
‘options’: {
‘showRowNumber’: false,
‘width’: ‘100%’,
‘height’: ‘100%’,
‘page’: ‘enable’,
‘pageSize’: 15,
‘allowHtml’: true
}
});

var finalTableView = new google.visualization.DataView(tableData);

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

function createCurrencyColumn(colIndex, labelText) {
return {
type: ‘number’,
label: labelText,
calc: function(dt, row) {
var val = dt.getValue(row, colIndex);
var numVal = safeParseFloat(val);
if (numVal === null) return { v: null, f: ‘N/A’ };
return {
v: numVal,
f: ‘$’ + numVal.toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 })
};
}
};
}

finalTableView.setColumns([
{
type: ‘number’,
label: ‘Rank’,
calc: function(dt, row) {
return row + 1;
}
},
createStringColumn(0, tableData.getColumnLabel(0) || ‘Airport Code’),
createStringColumn(1, tableData.getColumnLabel(1) || ‘Airport Name’),
createCurrencyColumn(2, tableData.getColumnLabel(2) || ‘Current Fare’),
createCurrencyColumn(3, tableData.getColumnLabel(3) || ‘Previous Fare’),
createStringColumn(4, tableData.getColumnLabel(4) || ‘Trend YoY’)
]);

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

Most and Least Expensive Flight Routes in the U.S.

The cost of air travel varies significantly depending on the specific route flown. This price disparity is driven by route-level dynamics — specifically, the number of competing airlines, the balance of business versus leisure demand, and the degree to which a single carrier dominates market share between 2 cities.

Once again, we pored over data from the Bureau of Transportation Statistics’ Airline Origin and Destination Survey to compare the latest 4 quarters of airfare data among U.S. airport-pair routes. Average airfares were calculated from domestic, one-way airfares and included taxes and fees applied at the time of purchase. Bulk fares, zero fares, and anomalous data were excluded.

The Most Expensive Long Flight Routes

The Least Expensive Long Flight Routes

The Most Expensive Medium Flight Routes

The Least Expensive Medium Routes

The Most Expensive Short Flight Routes

The Least Expensive Short Flight Routes

Average One-Way Airfare by Flight Route

function drawRouteChartsAndTable() {
if (!globalMasterData) return;

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

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

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

var rawStep = maxValue / 4;
var magnitude = Math.pow(10, Math.floor(Math.log(rawStep) / Math.LN10));
var normalizedStep = rawStep / magnitude;
var niceScales = [1, 2, 2.5, 5, 10];
var selectedScale = 10;

for (var i = 0; i = normalizedStep) {
selectedScale = niceScales[i];
break;
}
}

var step = selectedScale * magnitude;
var divisionCount = (step * 3 >= maxValue) ? 3 : 4;

var ticks = [];
for (var i = 0; i <= divisionCount; i++) {
ticks.push(i * step);
}

return { max: ticks[ticks.length – 1], ticks: ticks };
}

var chartsConfig = [
{ cols: [26, 27], id: 'chart_long_dist_top' },
{ cols: [28, 29], id: 'chart_long_dist_bottom' },
{ cols: [30, 31], id: 'chart_medium_dist_top' },
{ cols: [32, 33], id: 'chart_medium_dist_bottom' },
{ cols: [34, 35], id: 'chart_short_dist_top' },
{ cols: [36, 37], id: 'chart_short_dist_bottom' }
];

chartsConfig.forEach(function(config) {
var view = new google.visualization.DataView(globalMasterData);
view.setColumns(config.cols);
view.setRows(view.getFilteredRows([
{
column: 0,
test: function(val) {
if (val == null) { return false; }
if (val === '') { return false; }
return true;
}
}
]));

var data = view.toDataTable();
var rows = data.getNumberOfRows();

for (var i = 0; i 0) {
var firstCell = tableData.getValue(0, 0);
if (typeof firstCell === ‘string’) {
if (firstCell.toLowerCase() === ‘route’) {
tableData.removeRow(0);
}
}
}

var dashboard = new google.visualization.Dashboard(document.getElementById(‘dashboard_div_2’));

var stringFilter = new google.visualization.ControlWrapper({
‘controlType’: ‘StringFilter’,
‘containerId’: ‘filter_div_3’,
‘options’: {
‘filterColumnIndex’: 1,
‘matchType’: ‘any’,
‘ui’: {
‘label’: ‘Search Flight Route:’,
‘placeholder’: ‘Type a flight route…’
}
}
});

var tableChart = new google.visualization.ChartWrapper({
‘chartType’: ‘Table’,
‘containerId’: ‘table_3’,
‘options’: {
‘showRowNumber’: false,
‘width’: ‘100%’,
‘height’: ‘100%’,
‘page’: ‘enable’,
‘pageSize’: 15,
‘allowHtml’: true
}
});

var finalTableView = new google.visualization.DataView(tableData);

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

function createCurrencyColumn(colIndex, labelText) {
return {
type: ‘number’,
label: labelText,
calc: function(dt, row) {
var val = dt.getValue(row, colIndex);
var numVal = safeParseFloat(val);
if (numVal === null) return {v: null, f: ‘N/A’};
return {
v: numVal,
f: ‘$’ + numVal.toLocaleString(undefined, {minimumFractionDigits: 2, maximumFractionDigits: 2})
};
}
};
}

function createIntegerColumn(colIndex, labelText) {
return {
type: ‘number’,
label: labelText,
calc: function(dt, row) {
var val = dt.getValue(row, colIndex);
var numVal = safeParseFloat(val);
if (numVal === null) return {v: null, f: ‘N/A’};
var intVal = Math.round(numVal);
return {
v: intVal,
f: intVal.toLocaleString(undefined, {minimumFractionDigits: 0, maximumFractionDigits: 0})
};
}
};
}

finalTableView.setColumns([
{
type: ‘number’,
label: ‘Rank’,
calc: function(dt, row) {
return row + 1;
}
},
createStringColumn(0, tableData.getColumnLabel(0) || ‘Route’),
createCurrencyColumn(1, tableData.getColumnLabel(1) || ‘Current Fare’),
createCurrencyColumn(2, tableData.getColumnLabel(2) || ‘Previous Fare’),
createIntegerColumn(3, tableData.getColumnLabel(3) || ‘Distance’),
createStringColumn(4, tableData.getColumnLabel(4) || ‘Trend YoY’)
]);

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

Where Are U.S. Airfares Rising and Falling the Most?

Airfare volatility is often driven by distinct city-level dynamics. Large metropolitan areas served by multi-airport systems often benefit from diverse, consistent demand — often fueled by business travel, large events or conventions, and general tourism. This volume supports high flight frequency and competitive pricing. Conversely, routes connecting smaller markets often face limited schedules, sometimes with only a few flights per week, resulting in a scarcity that can be strained during periods of increased demand.

To identify these outliers, data from the U.S. Department of Transportation’s Domestic Airline Consumer Airfare Report was used to track average fare increases in the top 1,000 directionless, city-pair markets. Airfares shown represent one-way equivalent itineraries — one-way flights and round-trip fares divided by two. A city-pair market aggregates all passenger traffic and average fares between two metropolitan areas into a single route. It consolidates data from all local airports to reflect the true overall demand and pricing between those two cities, regardless of which specific airports or airlines are used.

The Largest Airfare Hikes and Drops in the U.S.

Percentage Change

function drawButterflyChart() {
if (!globalMasterData) return;

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

function calculateNiceTicks(maxValue) {
var stepCandidates = [5, 10, 20, 25, 30, 40, 50, 100];
var bestStep = 5;
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] 15) {
increaseData.removeRows(15, increaseData.getNumberOfRows() – 15);
}
if (decreaseData.getNumberOfRows() > 15) {
decreaseData.removeRows(15, decreaseData.getNumberOfRows() – 15);
}

var globalMaxValue = 0;

for (var i = 0; i 0) {
var incMax = increaseData.getColumnRange(1).max;
if (incMax > globalMaxValue) globalMaxValue = incMax;
}

for (var j = 0; j 0) {
var decMax = decreaseData.getColumnRange(1).max;
if (decMax > globalMaxValue) globalMaxValue = decMax;
}

var labelDiv = document.getElementById(“bf_axis_label_fixed”);
if (labelDiv) {
if (increaseData.getNumberOfColumns() > 1) {
var axisLabel = increaseData.getColumnLabel(1) || ‘Percentage Change in Average One-Way Airfare (YoY)’;
labelDiv.innerText = axisLabel;
}
}

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

var isMobile = window.innerWidth <= 768;
var rowHeight = isMobile ? 55 : 35;
var numRows = 15;
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_fixed');
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_fixed’);
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: ‘#000000’ },
gridlines: { color: ‘transparent’ }
},
tooltip: { isHtml: true }
};
var decreaseChart = new google.visualization.BarChart(decreaseContainer);

// SVG OVERRIDE: Decrease Chart (Anchor text to the Right)
google.visualization.events.addListener(decreaseChart, ‘ready’, function () {
var labels = decreaseContainer.getElementsByTagName(‘text’);
var bounds = decreaseChart.getChartLayoutInterface().getChartAreaBoundingBox();

for (var i = 0; i bounds.top && 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: ‘#000000’ },
gridlines: { color: ‘transparent’ }
},
tooltip: { isHtml: true }
};
var increaseChart = new google.visualization.BarChart(increaseContainer);

// SVG OVERRIDE: Increase Chart (Anchor text to the Left)
google.visualization.events.addListener(increaseChart, ‘ready’, function () {
var labels = increaseContainer.getElementsByTagName(‘text’);
var bounds = increaseChart.getChartLayoutInterface().getChartAreaBoundingBox();

for (var i = 0; i bounds.top && y

The Largest Airfare Hikes in the U.S.

function drawHikesTable() {
if (!globalMasterData) return;

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

var view = new google.visualization.DataView(globalMasterData);
view.setColumns([38, 39, 40, 41, 42]);
view.setRows(view.getFilteredRows([
{
column: 0,
test: function(val) {
if (val == null) { return false; }
if (val === ”) { return false; }
return true;
}
}
]));

var data = view.toDataTable();

if (data.getNumberOfRows() > 0) {
var firstCell = data.getValue(0, 0);
if (typeof firstCell === ‘string’) {
if (firstCell.toLowerCase().includes(‘city’)) {
data.removeRow(0);
}
}
}

var dashboard = new google.visualization.Dashboard(
document.getElementById(‘dashboard_div_3’)
);

var stringFilter = new google.visualization.ControlWrapper({
‘controlType’: ‘StringFilter’,
‘containerId’: ‘filter_div_4’,
‘options’: {
‘filterColumnIndex’: 1,
‘matchType’: ‘any’,
‘ui’: {
‘label’: ‘Search City-Pair Market:’,
‘placeholder’: ‘Type a city-pair market…’
}
}
});

var tableChart = new google.visualization.ChartWrapper({
‘chartType’: ‘Table’,
‘containerId’: ‘table_4’,
‘options’: {
‘width’: ‘100%’,
‘height’: ‘100%’,
‘page’: ‘enable’,
‘pageSize’: 15,
‘sortColumn’: 2,
‘sortAscending’: false,
‘allowHtml’: true,
‘cssClassNames’: {
‘headerRow’: ‘google-visualization-table-th’,
‘tableRow’: ‘google-visualization-table-tr’,
‘oddTableRow’: ‘google-visualization-table-tr’,
‘tableCell’: ‘google-visualization-table-td’
}
}
});

var finalView = new google.visualization.DataView(data);

finalView.setColumns([
{
type: ‘number’,
label: ‘Rank’,
calc: function(dt, row) {
return row + 1;
}
},
0,
{
type: ‘number’,
label: data.getColumnLabel(1) || ‘Percentage Increase’,
calc: function(dt, row) {
var val = safeParseFloat(dt.getValue(row, 1));
return { v: val, f: ‘+’ + val.toFixed(1) + ‘%’ };
}
},
{
type: ‘number’,
label: data.getColumnLabel(2) || ‘Price Change’,
calc: function(dt, row) {
var val = safeParseFloat(dt.getValue(row, 2));
return { v: val, f: (val > 0 ? ‘+$’ : val < 0 ? '-$' : '$') + Math.abs(val).toFixed(2) };
}
},
{
type: 'number',
label: data.getColumnLabel(3) || 'Current Fare',
calc: function(dt, row) {
var val = safeParseFloat(dt.getValue(row, 3));
return { v: val, f: '$' + val.toLocaleString(undefined, {minimumFractionDigits: 2, maximumFractionDigits: 2}) };
}
},
{
type: 'number',
label: data.getColumnLabel(4) || 'Previous Fare',
calc: function(dt, row) {
var val = safeParseFloat(dt.getValue(row, 4));
return { v: val, f: '$' + val.toLocaleString(undefined, {minimumFractionDigits: 2, maximumFractionDigits: 2}) };
}
}
]);

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

The Largest Airfare Drops in the U.S.

function drawDropsTable() {
if (!globalMasterData) return;

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

var view = new google.visualization.DataView(globalMasterData);
view.setColumns([43, 44, 45, 46, 47]);
view.setRows(view.getFilteredRows([
{
column: 0,
test: function(val) {
if (val == null) { return false; }
if (val === ”) { return false; }
return true;
}
}
]));

var data = view.toDataTable();

if (data.getNumberOfRows() > 0) {
var firstCell = data.getValue(0, 0);
if (typeof firstCell === ‘string’) {
if (firstCell.toLowerCase().includes(‘city’)) {
data.removeRow(0);
}
}
}

var dashboard = new google.visualization.Dashboard(
document.getElementById(‘dashboard_div_4’)
);

var stringFilter = new google.visualization.ControlWrapper({
‘controlType’: ‘StringFilter’,
‘containerId’: ‘filter_div_5’,
‘options’: {
‘filterColumnIndex’: 1,
‘matchType’: ‘any’,
‘ui’: {
‘label’: ‘Search City-Pair Market:’,
‘placeholder’: ‘Type a city-pair market…’
}
}
});

var tableChart = new google.visualization.ChartWrapper({
‘chartType’: ‘Table’,
‘containerId’: ‘table_5’,
‘options’: {
‘width’: ‘100%’,
‘height’: ‘100%’,
‘page’: ‘enable’,
‘pageSize’: 15,
‘sortColumn’: 2,
‘sortAscending’: true,
‘allowHtml’: true,
‘cssClassNames’: {
‘headerRow’: ‘google-visualization-table-th’,
‘tableRow’: ‘google-visualization-table-tr’,
‘oddTableRow’: ‘google-visualization-table-tr’,
‘tableCell’: ‘google-visualization-table-td’
}
}
});

var finalView = new google.visualization.DataView(data);

finalView.setColumns([
{
type: ‘number’,
label: ‘Rank’,
calc: function(dt, row) {
return row + 1;
}
},
0,
{
type: ‘number’,
label: data.getColumnLabel(1) || ‘Percentage Decrease’,
calc: function(dt, row) {
var val = safeParseFloat(dt.getValue(row, 1));
return { v: val, f: val.toFixed(1) + ‘%’ };
}
},
{
type: ‘number’,
label: data.getColumnLabel(2) || ‘Price Change’,
calc: function(dt, row) {
var val = safeParseFloat(dt.getValue(row, 2));
return { v: val, f: (val >= 0 ? ‘$’ : ‘-$’) + Math.abs(val).toFixed(2) };
}
},
{
type: ‘number’,
label: data.getColumnLabel(3) || ‘Current Fare’,
calc: function(dt, row) {
var val = safeParseFloat(dt.getValue(row, 3));
return { v: val, f: ‘$’ + val.toLocaleString(undefined, {minimumFractionDigits: 2, maximumFractionDigits: 2}) };
}
},
{
type: ‘number’,
label: data.getColumnLabel(4) || ‘Previous Fare’,
calc: function(dt, row) {
var val = safeParseFloat(dt.getValue(row, 4));
return { v: val, f: ‘$’ + val.toLocaleString(undefined, {minimumFractionDigits: 2, maximumFractionDigits: 2}) };
}
}
]);

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

  • The Most Expensive Flights in the U.S.
  • Airports With the Biggest Increase in Airfare This Year
  • Discover the Cheapest Long-Distance Flights in the U.S.

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

  • U.S. Department of Transportation. (2025). Domestic Airline Consumer Airfare Report [Data Set]. Retrieved quarterly from https://www.transportation.gov/policy/aviation-policy/domestic-airline-consumer-airfare-report.
  • U.S. Bureau of Labor Statistics. (2025). Consumer Price Index [Data Set]. Retrieved quarterly from https://www.bls.gov/cpi/.
  • Bureau of Transportation Statistics. (2025). DB1B (Airline Origin and Destination Survey) [Data set]. U.S. Department of Transportation. Retrieved quarterly from https://www.transtats.bts.gov/DatabaseInfo.asp?QO_VQ=EFI&Yv0x=D.