An opinionated command-line interface to the OCPF (Massachusetts campaign finance) API
Project description
OCPF Command Line Interface
ocpf is an opinionated command-line interface to the Massachusetts
Office of Campaign and Political Finance (OCPF) API
(https://api.ocpf.us/). It turns a recurring, multi-step lookup — "who is
running in this district and how much have they raised and spent?" — into a
single command.
Install
Run it with no install at all using uvx:
uvx ocpf race 37th
Or install the ocpf command onto your PATH:
pipx install ocpf # isolated, recommended
pip install ocpf # into the current environment
Then:
ocpf --help
Usage
ocpf race <district> [--year <year>] [--json]
ocpf race produces a year-to-date (YTD) financial summary of the legislative
(House or Senate) candidates in a district.
<district>may be a name matched case-insensitively against OCPF's district descriptions (&andandare treated alike) or a raw numeric district code. Ambiguous names are never guessed — the tool prints the matching districts with their codes and exits so you can pick one.--yeardefaults to the current calendar year.--jsonemits the merged, filtered candidate records (including the underlying*Numericvalues) as JSON to stdout. Human status/progress goes to stderr, so JSON output stays pipeable.
Example
$ ocpf race "Suffolk and Middlesex" --year 2026
District: Senate, Suffolk and Middlesex (code 166)
Election: primary 9/1/2026, general 11/3/2026
As of: 6/30/2026 (year-to-date, cumulative)
Candidate Party Inc Raised YTD Spent YTD Cash on Hand
------------------------ ----- --- ----------- ----------- ------------
Brownsberger, William N. - * $265,435.76 $135,490.74 $326,673.49
Lander, Daniel - $117,740.53 $32,674.59 $136,386.28
Wood, Brandon - $80.00 $3.00 $77.00
* incumbent (holds this seat)
Election dates and the as-of date are timeline context. The money is the single cumulative YTD figure the API provides for each candidate; it is never split into per-primary and per-general amounts.
ocpf filer — one candidate's filing summary
ocpf filer <filer> [--year <year>] [--json]
ocpf filer drills into a single filer: their committee profile, cumulative
YTD finances, and most recent reports.
<filer>may be a raw numeric cpfId (works for any filer type) or a candidate name matched case-insensitively against the legislative field for the year. cpfIds are shown byocpf race. As with district names, ambiguous names are never guessed — the tool prints the matching filers with their cpfIds and exits. Name lookup covers legislative filers; for other filer types, pass a cpfId directly.--yeardefaults to the current calendar year (used for name resolution and YTD context).--jsonemits thefiler,ytdReport, andlogReportsrecords (including numeric values) to stdout.
$ ocpf filer "Brownsberger" --year 2026
Filer: Brownsberger, William N. (cpfId 14454)
Committee: Brownsberger Committee
Party: Democratic Type: Legislative Candidates
Office: Senate, Suffolk and Middlesex
Status: active
Organized: 12/13/2005
Treasurer: David Merfeld
Year-to-date (as of 6/30/2026):
Raised YTD: $265,435.76
Spent YTD: $135,490.74
Cash on Hand: $326,673.49
Recent reports:
Type Period Filed Receipts Expenditures
-------------- ------- -------------- --------- ------------
Deposit Report 7/20/26 Mon, 7/20/2026 $500.00 $0.00
...
Scope
v1 covers legislative races (House and Senate). Other office types
(statewide, county, mayoral, ballot question), drill-down into individual
reports/donors/expenditures, and free-text candidate-name search are out of
scope. See openspec/ for the design and specifications.
Development
The project is managed with uv. From a clone of
this repository:
uv sync --extra dev # runtime deps + pytest/respx
uv run pytest # run the test suite
uv run ocpf race 37th # run the CLI from source
Releasing
Releases are published to PyPI automatically by GitHub Actions when a version tag
is pushed. Versioning is tag-driven (via hatch-vcs), so the tag is the single
source of truth for the package version:
git tag v0.1.0
git push --tags
The release workflow runs the test suite, builds the sdist and wheel, and publishes to PyPI using Trusted Publishing (OIDC) — no API token is stored in the repository.
First release only: before pushing the first tag, register a pending Trusted Publisher on PyPI (Your projects → Publishing) so the initial upload can create the project:
- PyPI project name:
ocpf - Owner:
bwbensonjr· Repository:ocpf-cli - Workflow:
release.yml· Environment:pypi
Design Guidelines
- Use OpenSpec to draft designs and create change proposals.
- Use clig.dev for command line interface guidelines.
- Use the
github.com/bwbensonjr/ocpf-analysisrepository (available locally) for information about the OCPF APIs, especially the/apidirectory which containsENDPOINT-STATUS.mdand other script and OCPF API test code. - Use the
github.com/bwbensonjr/ma-election-dbrepository (available locally) for information on candidates and districts we might want to look up.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file ocpf-0.1.0.tar.gz.
File metadata
- Download URL: ocpf-0.1.0.tar.gz
- Upload date:
- Size: 72.1 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
505311408792ceaa7e5276312e9938607e295f6f7533db177d1eef8713ed76d3
|
|
| MD5 |
ba94744f34e07dfde4b084c81eaf4665
|
|
| BLAKE2b-256 |
6bf60cfee3d0ee7d97eafc671dc74b503b0b3ba461c463f86fd9d37a6102b601
|
Provenance
The following attestation bundles were made for ocpf-0.1.0.tar.gz:
Publisher:
release.yml on bwbensonjr/ocpf-cli
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
ocpf-0.1.0.tar.gz -
Subject digest:
505311408792ceaa7e5276312e9938607e295f6f7533db177d1eef8713ed76d3 - Sigstore transparency entry: 2225295718
- Sigstore integration time:
-
Permalink:
bwbensonjr/ocpf-cli@7dc5f6acc8b3cb62a47dae2c482a05460dc6fd55 -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/bwbensonjr
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@7dc5f6acc8b3cb62a47dae2c482a05460dc6fd55 -
Trigger Event:
push
-
Statement type:
File details
Details for the file ocpf-0.1.0-py3-none-any.whl.
File metadata
- Download URL: ocpf-0.1.0-py3-none-any.whl
- Upload date:
- Size: 17.2 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
417a92119afe3eb2ed977c2ed135f38ca8b24dfd8f9a43942dcce7d13099b6e7
|
|
| MD5 |
1c771038f3227e93f704a5da358ce415
|
|
| BLAKE2b-256 |
3f1335b5986372def55a89c8d460ac27dcc599dca9988a740d468f66508ce43c
|
Provenance
The following attestation bundles were made for ocpf-0.1.0-py3-none-any.whl:
Publisher:
release.yml on bwbensonjr/ocpf-cli
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
ocpf-0.1.0-py3-none-any.whl -
Subject digest:
417a92119afe3eb2ed977c2ed135f38ca8b24dfd8f9a43942dcce7d13099b6e7 - Sigstore transparency entry: 2225295985
- Sigstore integration time:
-
Permalink:
bwbensonjr/ocpf-cli@7dc5f6acc8b3cb62a47dae2c482a05460dc6fd55 -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/bwbensonjr
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@7dc5f6acc8b3cb62a47dae2c482a05460dc6fd55 -
Trigger Event:
push
-
Statement type: