(Written 2026-02-01)
It sucks having to rely on Plaid or MasterCard Data Services to aggregate financial data, since they certainly sell this data in some way. I've been trying to get access to BECU programmatically, as they do expose an OFX API, and after significant effort, I've recently succeeded in getting access! This little post tells you how to do it. The hardest part so far has been just getting BECU to respond in a non-generic error manner, and get it to initiate a connection as described in the link.
You can initiate an app connection request using the python script below. Be aware that some details lie the version, app version, etc details may have changed since writing. And you'll need to populate env vars referenced at the start of the script - I generated a UUIDv4 for my BECU_CLIENTUID.
After running this and receiving a Please contact your Financial Institution. The CLIENTUID sent by the client was incorrect. response, I was then able to find and authorize the OFX connection request in the BECU online banking connected services page, where it showed up as Quicken for Windows (wrong, but whatever). Now time to build out the full transaction and account state capture service!
# main.py
import urllib.request
import urllib.error
from datetime import datetime, timezone
import os
BECU_USERNAME = os.environ["BECU_USERNAME"]
BECU_PASSWORD = os.environ["BECU_PASSWORD"]
BECU_CLIENTUID = os.environ["BECU_CLIENTUID"]
# Build raw OFX request
now = datetime.now(timezone.utc).strftime("%Y%m%d%H%M%S")
ofx_request = f"""OFXHEADER:100
DATA:OFXSGML
VERSION:103
SECURITY:NONE
ENCODING:USASCII
CHARSET:1252
COMPRESSION:NONE
OLDFILEUID:NONE
NEWFILEUID:NONE
<OFX>
<SIGNONMSGSRQV1>
<SONRQ>
<DTCLIENT>{now}
<USERID>{BECU_USERNAME}
<USERPASS>{BECU_PASSWORD}
<LANGUAGE>ENG
<FI>
<ORG>BECU
<FID>3670
</FI>
<APPID>QWIN
<APPVER>2700
<CLIENTUID>{BECU_CLIENTUID}
</SONRQ>
</SIGNONMSGSRQV1>
<SIGNUPMSGSRQV1>
<ACCTINFOTRNRQ>
<TRNUID>{now}
<ACCTINFORQ>
<DTACCTUP>19700101
</ACCTINFORQ>
</ACCTINFOTRNRQ>
</SIGNUPMSGSRQV1>
</OFX>"""
print("=== REQUEST ===")
print(ofx_request)
req = urllib.request.Request(
"https://onlinebanking.becu.org/ofx/ofxprocessor.asp",
data=ofx_request.encode("ascii"),
headers={"Content-Type": "application/x-ofx"},
)
try:
resp = urllib.request.urlopen(req, timeout=30)
print("\n=== RESPONSE ===")
print(resp.read().decode())
except urllib.error.HTTPError as e:
print(f"\n=== ERROR {e.code} ===")
print(e.read().decode())