Spaces:
Running on CPU Upgrade
title: BigQuery Nonprofit Enrichment
BigQuery Nonprofit Enrichment
Overview
Enrich nonprofit data with mission statements and website URLs from Google BigQuery's public IRS 990 dataset.
Workflow
Option 1: Web UI (No Authentication Required) ✅ RECOMMENDED
Step 1: Export SQL Query
python scripts/enrich_nonprofits_bigquery.py \
--input data/gold/nonprofits_tuscaloosa_form990.parquet \
--export-sql scripts/bigquery_tuscaloosa_missions.sql
Step 2: Run Query in BigQuery
- Go to https://console.cloud.google.com/bigquery
- Click "COMPOSE NEW QUERY"
- Paste SQL from
scripts/bigquery_tuscaloosa_missions.sql - Click "RUN"
- Wait for results (~200-400 rows expected)
Step 3: Export Results
- Click "SAVE RESULTS" → "CSV (local file)"
- Save as:
data/cache/bigquery_results.csv
Step 4: Merge into Gold Data
python scripts/enrich_nonprofits_bigquery.py \
--input data/gold/nonprofits_tuscaloosa_form990.parquet \
--from-csv data/cache/bigquery_results.csv \
--update-in-place
Option 2: Direct Query (Requires gcloud Auth)
Setup (one-time):
# Install gcloud CLI
curl https://sdk.cloud.google.com | bash
exec -l $SHELL
# Authenticate
gcloud auth application-default login
Run:
python scripts/enrich_nonprofits_bigquery.py \
--input data/gold/nonprofits_tuscaloosa_form990.parquet \
--output data/gold/nonprofits_tuscaloosa_bigquery.parquet \
--project YOUR_PROJECT_ID
Data Schema
New Fields Added
| Field | Type | Description | Coverage |
|---|---|---|---|
bigquery_mission |
string | Activity or mission description from Form 990 | ~30-40% |
bigquery_website |
string | Website URL from Form 990 | ~30-40% |
bigquery_tax_year |
string | Tax year of the filing | ~30-40% |
bigquery_form_type |
string | Form type: "990" or "990-EZ" | ~30-40% |
bigquery_updated_date |
string | Date when BigQuery data was added (YYYY-MM-DD) | 100% |
Data Sources Queried
The script queries across multiple IRS 990 tables:
bigquery-public-data.irs_990.irs_990_2023(Full Form 990)bigquery-public-data.irs_990.irs_990_2022(Full Form 990)bigquery-public-data.irs_990.irs_990_2021(Full Form 990)bigquery-public-data.irs_990.irs_990_ez_2023(990-EZ for smaller orgs)bigquery-public-data.irs_990.irs_990_ez_2022(990-EZ for smaller orgs)bigquery-public-data.irs_990.irs_990_ez_2021(990-EZ for smaller orgs)
Deduplication: Prefers most recent year, then Full 990 over 990-EZ.
Combined Data Coverage
After enrichment with both GivingTuesday and BigQuery:
For Tuscaloosa (921 nonprofits)
Missions:
- EO-BMF: 0 (0%)
- GivingTuesday: ~299 (32.5%)
- BigQuery: ~200-400 (30-40%)
- Combined: ~400-500 (40-50%) ✅
Websites:
- EO-BMF: 0 (0%)
- GivingTuesday: 0 (0%)
- BigQuery: ~200-400 (30-40%)
- Combined: ~200-400 (30-40%) ✅
Financials:
- GivingTuesday: 307 orgs with revenue/expenses/assets (33.3%)
- BigQuery: Same data, different source
Best Practices
When to Use BigQuery vs GivingTuesday
| Data Need | Best Source |
|---|---|
| Mission statements | Both (GivingTuesday + BigQuery for coverage) |
| Website URLs | BigQuery (GivingTuesday doesn't extract this) |
| Detailed financials | GivingTuesday Data Lake (XML parsing) |
| Grants paid | GivingTuesday Data Lake |
| Executive compensation | BigQuery (irs_990_schedule_j_YYYY) |
| Related organizations | BigQuery (irs_990_schedule_r_YYYY) |
Update Frequency
Re-run BigQuery enrichment:
- Annually after IRS releases new Form 990 data (typically June/July)
- When expanding to new jurisdictions
- After major nonprofit landscape changes
Data Cleaning
Mission statements from BigQuery may contain XML artifacts:
import re
# Remove XML tags
mission = re.sub(r'<[^>]+>', ' ', mission)
# Clean whitespace
mission = re.sub(r'\s+', ' ', mission).strip()
Cost
FREE when using:
- Public BigQuery datasets via web UI
- Within Google Cloud's 1TB free tier per month
Typical query cost: $0 (Tuscaloosa query ~10 MB)
Troubleshooting
"No results returned"
- EINs may not have filed 990 in queried years
- Check if organizations are too small (< $50K revenue exempts from 990)
- Try expanding
--yearsto include more historical data
"CSV column names don't match"
BigQuery exports use lowercase column names. The script handles this automatically.
"Existing BigQuery columns found"
The script automatically drops and replaces existing BigQuery columns when using --update-in-place.
Examples
Full Alabama health nonprofits:
# 1. Export SQL
python scripts/enrich_nonprofits_bigquery.py \
--input data/gold/nonprofits_organizations.parquet \
--export-sql scripts/bigquery_alabama_health.sql \
--states AL --ntee E
# 2. Run in BigQuery web UI, export CSV
# 3. Merge
python scripts/enrich_nonprofits_bigquery.py \
--input data/gold/nonprofits_organizations.parquet \
--from-csv data/cache/bigquery_alabama_health.csv \
--update-in-place
Sample 100 orgs for testing:
python scripts/enrich_nonprofits_bigquery.py \
--input data/gold/nonprofits_tuscaloosa_form990.parquet \
--export-sql scripts/bigquery_sample.sql \
--sample 100