open-navigator / web_docs /docs /integrations /fec-campaign-finance.md
jcbowyer's picture
Clean HuggingFace deployment without binary files
e59d91d
|
Raw
History Blame Contribute Delete
14 kB
---
sidebar_position: 7
---
# FEC Campaign Finance Integration
Track political contributions and campaign finance data using the **Federal Election Commission (FEC)** via **OpenFEC API** and **Bulk Downloads**.
## 🎯 Overview
The FEC integration enables tracking of:
- Individual political contributions ($200+)
- Federal candidates (House, Senate, President)
- Political committees and PACs
- Nonprofit leadership political giving
- Donor networks in advocacy organizations
- Campaign expenditures and disbursements
**Two access methods:**
1. **OpenFEC API** - Real-time queries, filtered searches (API key required)
2. **Bulk Downloads** - Complete datasets, no rate limits (no API key needed)
This data integrates with nonprofit and contacts data to reveal political influence patterns.
## πŸ”‘ Get API Access
### OpenFEC API (Real-time Queries)
**1. Sign Up for Free API Key**
Visit: **https://api.data.gov/signup/**
- Free tier: **1,000 requests/hour**
- No credit card required
- Instant activation
**2. Set Environment Variable**
```bash
# Add to your .env file
echo 'FEC_API_KEY="your_api_key_here"' >> .env
# Or export for current session
export FEC_API_KEY="your_api_key_here"
```
Without an API key, you'll use `DEMO_KEY` (limited to 30 requests/hour).
### Bulk Downloads (Complete Datasets)
**No API key required!** Download complete datasets directly:
**Bulk Data Portal:** https://www.fec.gov/data/browse-data/?tab=bulk-data
**Available datasets:**
- **Contributions** (Schedule A) - All individual contributions $200+
- **Expenditures** (Schedule B) - All operating expenditures
- **Candidate Master** - All federal candidates
- **Committee Master** - All PACs and committees
- **Campaign Finance Totals** - Summary by cycle
**Format:** CSV, FEC format
**Update Frequency:** Nightly
**Historical Coverage:** 1980s to present
**Rate Limits:** None (direct download)
**When to use bulk downloads:**
- Analyzing complete datasets (millions of records)
- Historical analysis across election cycles
- Offline analysis without API rate limits
- Data warehousing and archival
**When to use API:**
- Real-time contribution lookups
- Filtered searches (by name, employer, state)
- Incremental updates
- Web application queries
## πŸ“Š Gold Tables Created
The FEC integration creates 4 gold tables per state:
| Table | Description | Key Fields |
|-------|-------------|------------|
| **campaigns_candidates** | Federal candidates in state | candidate_id, name, party, office, district |
| **campaigns_committees** | PACs and campaign committees | committee_id, name, type, party |
| **campaigns_contributions** | Individual contributions $200+ | contributor_name, employer, amount, date, recipient |
| **campaigns_nonprofit_donors** | Nonprofit leadership donations | ein, organization_name, contributor_name, amount |
## πŸš€ Quick Start
### Run Demo Script
```bash
cd /home/developer/projects/open-navigator
source .venv/bin/activate
# Basic demo (using DEMO_KEY)
python examples/demo_fec_integration.py --state MA
# With your API key
export FEC_API_KEY="your_key_here"
python examples/demo_fec_integration.py --state MA --cycle 2024
# Search for specific nonprofit donors
python examples/demo_fec_integration.py --state MA --employer "Community Health"
```
### Create Gold Tables
```bash
# Create campaign finance tables for Massachusetts
python pipeline/create_campaigns_gold_tables.py \
--state MA \
--cycle 2024 \
--max-contributions 10000
# With custom API key
python pipeline/create_campaigns_gold_tables.py \
--state MA \
--api-key "your_key_here"
```
## πŸ“– Use Cases
### 1. Track Nonprofit Leadership Donations
Identify politically active nonprofit officers and directors:
```python
from pipeline.create_campaigns_gold_tables import CampaignsGoldTableCreator
creator = CampaignsGoldTableCreator(state_code="MA")
creator.create_all_campaigns_tables(cycle=2024)
# Analyzes:
# - Contributions where employer matches nonprofit name
# - Contributions from known nonprofit officers/directors
# - Political giving patterns in advocacy sector
```
**Output:** `data/gold/states/MA/campaigns_nonprofit_donors.parquet`
### 2. Map Donor Networks
Find connections between organizations and political campaigns:
```python
from discovery.fec_integration import OpenFECAPI
api = OpenFECAPI(api_key="your_key")
# Search for contributions from health sector
result = api.search_individual_contributions(
contributor_state="MA",
contributor_employer="Health",
min_amount=1000
)
for contrib in result['results']:
print(f"{contrib['contributor_name']} ({contrib['contributor_employer']})")
print(f" β†’ ${contrib['contribution_receipt_amount']} to {contrib['committee_name']}")
```
### 3. Analyze Political Influence on Policy
Cross-reference campaign contributions with grant awards:
```python
import pandas as pd
# Load campaign contributions
contributions = pd.read_parquet('data/gold/states/MA/campaigns_contributions.parquet')
# Load grant data
grants = pd.read_parquet('data/gold/states/MA/grants_revenue_sources.parquet')
# Find organizations that both give politically and receive grants
donor_orgs = contributions['contributor_employer'].unique()
grant_recipients = grants['organization_name'].unique()
overlap = set(donor_orgs) & set(grant_recipients)
print(f"Organizations that both donate politically and receive grants: {len(overlap)}")
```
## πŸ’Ύ Using Bulk Downloads
For large-scale analysis, use FEC bulk downloads instead of the API:
### Download Complete Datasets
```bash
# Download candidate master file (all federal candidates)
wget https://www.fec.gov/files/bulk-downloads/2024/cn24.zip
unzip cn24.zip
# Download committee master file (all PACs and committees)
wget https://www.fec.gov/files/bulk-downloads/2024/cm24.zip
unzip cm24.zip
# Download individual contributions (Schedule A)
# Warning: Very large file (100s of MB to GBs)
wget https://www.fec.gov/files/bulk-downloads/2024/indiv24.zip
unzip indiv24.zip
```
### Process Bulk Data with Pandas
```python
import pandas as pd
from pathlib import Path
# Load contributions for specific cycle
contributions_file = Path("data/fec/bulk/indiv24.txt")
df = pd.read_csv(
contributions_file,
sep="|",
header=None,
names=[
"CMTE_ID", "AMNDT_IND", "RPT_TP", "TRANSACTION_PGI",
"IMAGE_NUM", "TRANSACTION_TP", "ENTITY_TP", "NAME",
"CITY", "STATE", "ZIP_CODE", "EMPLOYER", "OCCUPATION",
"TRANSACTION_DT", "TRANSACTION_AMT", "OTHER_ID",
"TRAN_ID", "FILE_NUM", "MEMO_CD", "MEMO_TEXT", "SUB_ID"
],
encoding="latin1",
low_memory=False
)
# Filter to Massachusetts contributors
ma_contributions = df[df['STATE'] == 'MA']
# Filter to health sector
health_donors = ma_contributions[
ma_contributions['EMPLOYER'].str.contains('Health|Hospital|Dental', case=False, na=False)
]
print(f"Found {len(health_donors):,} health sector contributions from MA")
print(f"Total amount: ${health_donors['TRANSACTION_AMT'].sum():,.2f}")
# Save to parquet for faster future access
health_donors.to_parquet('data/gold/states/MA/campaigns_contributions.parquet')
```
### Bulk Download File Formats
**Candidate Master File (cn.txt):**
- Delimiter: Pipe (`|`)
- Columns: `CAND_ID`, `CAND_NAME`, `CAND_PTY_AFFILIATION`, `CAND_ELECTION_YR`, `CAND_OFFICE_ST`, `CAND_OFFICE`, `CAND_OFFICE_DISTRICT`, etc.
**Committee Master File (cm.txt):**
- Delimiter: Pipe (`|`)
- Columns: `CMTE_ID`, `CMTE_NM`, `TRES_NM`, `CMTE_CITY`, `CMTE_ST`, `CMTE_TP`, `CMTE_PTY_AFFILIATION`, etc.
**Individual Contributions (indiv.txt):**
- Delimiter: Pipe (`|`)
- Columns: `CMTE_ID`, `NAME`, `CITY`, `STATE`, `ZIP_CODE`, `EMPLOYER`, `OCCUPATION`, `TRANSACTION_DT`, `TRANSACTION_AMT`, etc.
- Contains all contributions $200+ as required by law
### Advantages of Bulk Downloads
βœ… **Complete data** - No pagination, no missing records
βœ… **No rate limits** - Download once, analyze forever
βœ… **Historical data** - Access to all election cycles
βœ… **Offline analysis** - No API dependency
βœ… **Faster processing** - Local files vs HTTP requests
### When to Use Each Method
| Scenario | Method | Reason |
|----------|--------|--------|
| Find specific donor | API | Filtered search, fast lookup |
| All MA contributions | Bulk Download | Complete dataset, no limits |
| Real-time updates | API | Latest filings |
| Historical analysis | Bulk Download | Multi-cycle coverage |
| Web application | API | Dynamic queries |
| Research/analysis | Bulk Download | Full data access |
## πŸ› οΈ API Reference
### Search Individual Contributions
```python
from discovery.fec_integration import OpenFECAPI
api = OpenFECAPI(api_key="your_key")
# Search by contributor
result = api.search_individual_contributions(
contributor_name="Smith",
contributor_state="MA",
min_amount=200,
min_date="2023-01-01",
max_date="2024-12-31"
)
# Search by employer
result = api.search_individual_contributions(
contributor_employer="Community Foundation",
contributor_state="MA",
min_amount=1000
)
```
### Search Candidates
```python
# House candidates in Massachusetts
result = api.search_candidates(
state="MA",
office="H", # H=House, S=Senate, P=President
cycle=2024
)
# Senate candidates
result = api.search_candidates(
state="MA",
office="S",
party="DEM", # or "REP"
cycle=2024
)
```
### Search Committees
```python
# Find PACs in Massachusetts
result = api.search_committees(
state="MA",
committee_type="N" # N=PAC, Q=Super PAC
)
```
## πŸ“‹ Data Model Integration
### Links to Nonprofit Data
The `campaigns_nonprofit_donors` table links FEC data to nonprofits:
```sql
-- Conceptual SQL (use pandas for actual queries)
SELECT
nd.organization_name,
nd.contributor_name,
nd.contributor_title,
SUM(nd.contribution_amount) as total_contributions,
COUNT(*) as num_contributions
FROM campaigns_nonprofit_donors nd
GROUP BY nd.organization_name, nd.contributor_name
ORDER BY total_contributions DESC;
```
### Links to Contacts
Match contributions to nonprofit officers:
```python
import pandas as pd
# Load data
officers = pd.read_parquet('data/gold/states/MA/contacts_nonprofit_officers.parquet')
contributions = pd.read_parquet('data/gold/states/MA/campaigns_contributions.parquet')
# Match by name
merged = officers.merge(
contributions,
left_on='officer_name',
right_on='contributor_name',
how='inner'
)
print(f"Found {len(merged)} contribution records from known nonprofit officers")
```
## βš™οΈ Configuration
### Environment Variables
```bash
# Required (or use DEMO_KEY)
FEC_API_KEY="your_api_key_here"
# Optional
FEC_CACHE_DIR="data/cache/fec" # Where to cache bulk downloads
```
### Rate Limits
| API Key | Rate Limit | Notes |
|---------|------------|-------|
| DEMO_KEY | 30 requests/hour | Shared across all users |
| Free API Key | 1,000 requests/hour | Recommended |
| Bulk Download | No limit | 1-5 GB files, use for comprehensive data |
## πŸŽ“ Advanced Usage
### Bulk Data Download
For comprehensive historical analysis, download bulk files:
```python
from discovery.fec_integration import FECBulkDataLoader
loader = FECBulkDataLoader(cache_dir="data/cache/fec")
# Download 2024 individual contributions (WARNING: 1-5 GB)
zip_path = loader.download_individual_contributions(cycle="2024")
# Parse and filter
df = loader.parse_individual_contributions(
zip_path=zip_path,
state_filter="MA",
employer_filter="Foundation",
min_amount=200
)
print(f"Found {len(df):,} contributions from MA foundations")
```
### Custom Analysis Pipeline
Build custom analysis workflows:
```python
from discovery.fec_integration import OpenFECAPI
import pandas as pd
api = OpenFECAPI(api_key="your_key")
# 1. Get all House candidates in MA
candidates_result = api.search_candidates(state="MA", office="H", cycle=2024)
candidates = candidates_result['results']
# 2. For each candidate, get top donors
for candidate in candidates:
candidate_id = candidate['candidate_id']
# Get candidate committees
committees_result = api.search_committees(candidate_id=candidate_id)
# Analyze fundraising
print(f"\n{candidate['name']} ({candidate['party']})")
print(f" Committees: {len(committees_result['results'])}")
```
## πŸ” Data Quality Notes
### Contribution Reporting Threshold
- FEC requires reporting for contributions **$200 or more**
- Smaller contributions are not itemized
- Use `min_amount=200` for complete data
### Employer Field
- Self-reported by contributor
- May have inconsistent formatting
- "Community Health Center" vs "Community Health Ctr"
- Use fuzzy matching for nonprofit employer analysis
### Update Frequency
- FEC data updated **daily**
- Electronic filings appear within 24 hours
- Paper filings may take 30+ days
## πŸ“š Additional Resources
- **OpenFEC API Docs:** https://api.open.fec.gov/developers/
- **FEC Bulk Data:** https://www.fec.gov/data/browse-data/?tab=bulk-data
- **Campaign Finance Guide:** https://www.fec.gov/help-candidates-and-committees/
- **Data Dictionary:** https://www.fec.gov/campaign-finance-data/contributions-individuals-file-description/
## 🀝 Related Integrations
- [Nonprofit Discovery](nonprofit-discovery.md) - Match employers to EINs
- [IRS BMF Data](irs-bmf-data.md) - Nonprofit organization data
- [Gold Table Pipeline](../guides/gold-table-pipeline.md) - Full data model
---
## πŸ’‘ Pro Tips
1. **Start with API, use bulk for scale:** API is easier for exploration, bulk files for comprehensive analysis
2. **Cache aggressively:** Set up `data/cache/fec/` to avoid re-downloading
3. **Match on lowercase:** Employer names vary in capitalization
4. **Use year ranges:** Contributions span multiple years, use date filters
5. **Join with EINs:** Match FEC employer field to `nonprofits_organizations.ein` for verified linkage