ffiec-data-connect (Python)
Incremental updates
Use collect_filers_since_date and collect_filers_submission_date_time to detect new and re-filed submissions without a full re-pull.
Call Report data for a reporting period keeps arriving for weeks — sometimes months — after the quarter-end, as institutions file late or amend previous submissions. A pipeline that does a one-shot pull will be stale within days. This page covers the endpoints designed for keeping an existing dataset current.
Why data changes after submission
See Call Reports § Why submitted data changes. The short version: accounting corrections, regulator inquiries, examination findings, and clarification of regulatory guidance all trigger refilings. Budget for them.
Two detection endpoints
collect_filers_since_date
Returns the RSSDs that filed (or refiled) for a reporting period since a given date. Use it for a “give me everything new since my last run” query.
from ffiec_data_connect import collect_filers_since_date
new_or_changed = collect_filers_since_date(
creds,
since_date="4/1/2026", # your last successful run
reporting_period="3/31/2026", # the quarter you care about
)
print(new_or_changed)
# [688556, 175458, 92144, ...]
The return is a list of RSSD integers — just IDs, no timestamps.
collect_filers_submission_date_time
Same filter, plus the submission datetime for each RSSD. Useful when you want to detect late-vs-amended (several submissions per institution per period).
from ffiec_data_connect import collect_filers_submission_date_time
events = collect_filers_submission_date_time(
creds,
since_date="4/1/2026",
reporting_period="3/31/2026",
)
# [{'rssd': 688556, 'datetime': '7/1/2022 12:15:06 AM'}, ...]
FFIEC reports submission timestamps in Washington, DC local time. If you
pass date_output_format="python_format", the library returns tz-aware
datetime objects already labeled as America/New_York — DST is honored
automatically (EST in winter, EDT in summer), so you can compare across
quarters or convert to UTC without reattaching anything:
submissions = collect_filers_submission_date_time(
creds, since_date="1/1/2024", reporting_period="3/31/2026",
date_output_format="python_format",
)
# submissions[0]["datetime"].tzinfo → zoneinfo.ZoneInfo('America/New_York')
# submissions[0]["datetime"].astimezone(timezone.utc) # works out of the box
(date_output_format="python_format" was implemented in 3.0.0; prior
releases silently ignored the parameter. Output is America/New_York-aware
— if you need a different zone, call .astimezone(...) on the result.)
A minimal incremental loop
import json
from pathlib import Path
from datetime import datetime
from ffiec_data_connect import OAuth2Credentials, collect_filers_since_date, collect_data
STATE_FILE = Path("state.json")
PERIOD = "3/31/2026"
def run():
state = json.loads(STATE_FILE.read_text()) if STATE_FILE.exists() else {}
since = state.get("last_run", "1/1/2026")
creds = OAuth2Credentials(...)
new_rssds = collect_filers_since_date(
creds, since_date=since, reporting_period=PERIOD
)
print(f"{len(new_rssds)} institutions with new/amended data since {since}")
for rssd in new_rssds:
rows = collect_data(creds, rssd_id=str(rssd), reporting_period=PERIOD,
series="call", output_type="list")
# ... upsert rows into your store ...
state["last_run"] = datetime.now().strftime("%m/%d/%Y")
STATE_FILE.write_text(json.dumps(state))
run()
Run this nightly or on whatever cadence your downstream consumers tolerate. After the initial backfill, a typical daily incremental is tens to low-hundreds of institutions — well under the rate limit.
Multi-period monitoring
If you track multiple recent quarters (because any of them can still be refiled), loop the period too:
for period in ["3/31/2026", "12/31/2025", "9/30/2025"]:
new_rssds = collect_filers_since_date(creds, since_date=since, reporting_period=period)
for rssd in new_rssds:
# ... refresh this institution for this period ...
Practically, refilings beyond four quarters back are rare. A three- or four-quarter sliding window is usually sufficient.
UBPR incrementals
UBPR has no equivalent “filers since date” endpoint. Instead, UBPR is regenerated when the underlying Call Report is refiled — so if you’re tracking UBPR, trigger a UBPR re-pull whenever you detect a Call Report refile for the same RSSD+period.
See also
- Bulk download — initial backfill.
- Async and rate limits — pacing your loop.
- Call Reports § Why submitted data changes.