← Back to Blog
Weekly Export Sales Tracker
Mar 11, 2026 · 6 min read
Every Thursday, USDA publishes the Weekly Export Sales report — the most closely watched demand-side data for grains. By plotting cumulative commitments across marketing years, you can quickly see whether export pace is ahead or behind historical norms.
In this notebook we'll pull corn total commitments, compute seasonal week numbers, and build a year-over-year overlay chart.
[1]
import os
import fastagsdata as fdata
import matplotlib.pyplot as plt
import pandas as pd
fdata.set_key(os.environ['FASTAGSDATA_API_KEY'])
Pull corn export sales
Query the exportsales_api source for corn total commitments (tc). Each row is one weekly report. Values are in metric tons. Source: USDA Export Sales Reporting.
[2]
exports = fdata.get(
'exportsales_api',
param=['tc'],
comdty=['c'],
shape_id='us'
)
# Keep last 5 marketing years
latest_year = exports['year'].max()
export = exports[exports['year'] >= latest_year - 5].copy()
print(f"{len(export)} rows, years {export['year'].min()}–{export['year'].max()}")
Out
302 rows, years 2021–2026
Compute marketing year weeks
Corn's marketing year starts September 1, so MY 2026 runs Sep 2025 through Aug 2026. We compute each row's week number within its marketing year so we can align years on the same axis.
[3]
# Corn marketing year starts Sep 1 (MY 2026 runs Sep 2025 → Aug 2026)
my_start_month, my_start_day = 9, 1
# Each MY starts in the prior calendar year (MY 2026 → Sep 1 2025)
export['my_cal_year'] = export['year'] - 1
export['my_start'] = export['my_cal_year'].apply(lambda y: pd.Timestamp(y, my_start_month, my_start_day))
# Week number within the marketing year
export['days_into_my'] = (export['date_ref'] - export['my_start']).dt.days
export['my_week'] = export['days_into_my'] // 7 + 1
# Pivot: one column per year, one row per week number
pvt = export.pivot_table(index='my_week', columns='year', values='value')
# Convert week numbers to dates for the x-axis
my_start_date = pd.Timestamp(latest_year - 1, my_start_month, my_start_day)
pvt.index = my_start_date + pd.to_timedelta(pvt.index * 7, unit='D')
pvt.reset_index().head()
| year |
my_week |
2021 |
2022 |
2023 |
2024 |
2025 |
2026 |
| 0 |
2025-09-08 |
18846903.0 |
24326115.0 |
11718159.0 |
11159925.0 |
13361230.0 |
22601465.0 |
| 1 |
2025-09-15 |
20456088.0 |
24572698.0 |
12301269.0 |
11726782.0 |
14208580.0 |
23833099.0 |
| 2 |
2025-09-22 |
22595090.0 |
24945705.0 |
12483608.0 |
12568565.0 |
14743636.0 |
25756544.0 |
| 3 |
2025-09-29 |
24622215.0 |
25316093.0 |
12995653.0 |
14384529.0 |
16427705.0 |
27151393.0 |
| 4 |
2025-10-06 |
25847866.0 |
26581176.0 |
13222698.0 |
15294940.0 |
17649771.0 |
29411131.0 |
Seasonal overlay chart
Plot each marketing year as a separate line. The current year stands out with a thicker line.
[4]
fig, ax = plt.subplots(figsize=(10, 5))
pvt.plot(ax=ax, style='--', alpha=0.5, linewidth=1.2, legend=True)
# Highlight the current marketing year
ax.lines[-1].set(linestyle='-', linewidth=2.5, alpha=1.0)
ax.set_xlabel('Date')
ax.set_ylabel('Total Commitments (MT)')
ax.set_title('Corn Export Commitments — Seasonal Overlay')
ax.legend([f"MY {yr}" for yr in pvt.columns])
plt.tight_layout()
plt.show()