Use OpenFEC Commercially Without Donor Data with Public Data
Learn openfec aggregate data with public OpenFEC metadata, repeatable Apify runs, clean JSON output, validation checks, and a practical privacy-safe analysis.

TL;DR: The FEC Campaign Finance Scraper turns public OpenFEC records into a repeatable openfec aggregate data dataset for compliance teams. It accepts focused discovery inputs, saves structured evidence, deduplicates records, and exposes explicit result limits. Use it when a browser export or a one-off script would make comparisons difficult to reproduce. Keep dated snapshots, validate row counts, and join additional security or ownership evidence separately instead of stretching public-source data beyond what it proves.
Why scrape OpenFEC for privacy-safe analysis?
OpenFEC aggregate data replaces manual catalog checks with comparable, time-stamped evidence. A source website answers one question at a time, while compliance teams usually need to compare a cohort, repeat the same query next week, and explain why an option was shortlisted. The useful unit is therefore not a screenshot; it is a stable table with provenance.
The OpenFEC record contributes candidate identity, office, state, party, election years, principal committee identity, totals availability, cycle coverage, aggregate receipts, aggregate contributions, spending, cash, and debt. Those fields support discovery, triage, trend analysis, and watchlists without claiming that popularity proves security or product quality. The official OpenFEC API documentation describes the source and its public access model. Apify's Tasks documentation explains how tested inputs become repeatable scheduled jobs.
This workflow is especially useful when the decision has a defined population and cadence: defined research cohorts, regulated records, funded projects, or identities that require review. Start with a narrow question, retain the raw evidence, and calculate rankings downstream. That separation keeps collection reproducible and analysis revisable.
How does this compare to the alternatives?
A purpose-built Actor gives privacy-safe analysis a smaller operational surface than maintaining a custom collector. The source itself remains authoritative; the Actor standardizes extraction, limits, deduplication, and delivery.
| Method | Commercial model | Reliability | Setup time | Ongoing maintenance |
|---|---|---|---|---|
| DIY Python script | Engineering time plus hosting | Depends on local retry and schema handling | Hours to days | Owned by your team |
| Generic scraping API | Usage or subscription | Page-oriented and source-dependent | Hours | Selectors and pagination remain yours |
| Thirdwatch Actor | Pay per saved result | Source-specific validation and retries | Minutes | Managed listing and schema updates |
The DIY route can be appropriate for a deeply customized internal system. A generic API helps when rendered pages are the only source. For public public-source data, the actor page offers a direct path with inputs that match the actual collection modes and output shaped for datasets.
How to run openfec aggregate data in 5 steps
What question should the dataset answer?
The first step is to write one decision question with a defined population and review cadence. Examples include identifying maintained options, monitoring a production watchlist, or comparing activity within one category. Avoid a query like "all software," because a large result set makes neither the inclusion rule nor the refresh strategy clear.
Write down the audience, expected result range, fields required for the decision, and what happens when a value changes. That short contract prevents silent scope expansion. For market research, discovery queries are appropriate. For governance, exact names are usually stronger because the cohort should not drift between runs.
Which Actor input should I use?
Use discovery inputs for broad research and exact-name inputs for fixed watchlists. The Actor schema exposes only supported fields, and the default limits keep the first run small. This example combines the modes supported by this Actor:
{"queries":["Smith"],"candidateIds":[],"cycle":2026,"office":"S","includeTotals":true,"maxResultsPerQuery":25,"maxResults":25}Keep separate Tasks when two queries represent different business questions. Combined runs deduplicate by candidateId, which is useful for a canonical table but can hide query membership. If membership matters, store a bridge table containing query, key, run ID, and collection time.
How do I run it through the Apify API?
The Apify client can start the Actor and return its dataset in one reproducible script. Put the token in an environment variable rather than source control.
import os
from apify_client import ApifyClient
client = ApifyClient(os.environ["APIFY_TOKEN"])
run = client.actor("thirdwatch/fec-campaign-finance-scraper").call(
run_input={
"queries": ["Smith"],
"candidateIds": [],
"cycle": 2026,
"office": "S",
"includeTotals": true,
"maxResultsPerQuery": 25,
"maxResults": 25,
}
)
items = list(client.dataset(run["defaultDatasetId"]).iterate_items())
print(f"saved {len(items)} records from run {run['id']}")For production automation, set an explicit timeout, log the run ID, and fail the downstream load if the Actor run is not successful. The Apify API documentation covers run and dataset endpoints for non-Python clients.
How should I validate the returned records?
Validation should check identity, evidence URLs, counts, and the fields required by the decision. Do not reject a row merely because an optional description or popularity metric is null. Public datasets contain records of different ages and publishing practices, so optional-field completeness is not uniform.
required = ["candidateId", "sourceUrl", "source"]
bad = [row for row in items if any(not row.get(field) for field in required)]
if bad:
raise ValueError(f"{len(bad)} rows failed identity validation")
if len(items) == 0:
raise ValueError("The run returned no evidence; keep the previous snapshot")Add range checks for counts, parse timestamps into UTC, and compare current volume with the previous successful run. A sudden collapse often indicates a changed query, upstream outage, or rate limit,not a real market event.
How do I schedule and store the snapshots?
A saved Task turns the tested input into a stable collection contract. Choose a cadence that matches the signal: daily for release watchlists, weekly for operational portfolios, and monthly for market maps. Write each successful run to an immutable dated partition before updating a current-state table.
Use candidateId as the natural key, collected_at as snapshot time, and the source URL as evidence. Calculate changes after ingestion: new records, removed records, version transitions, activity changes, and rank movement. Alert only on changes tied to a decision; indiscriminate alerts teach teams to ignore the feed.
What does the OpenFEC output look like?
The output preserves a canonical identity and the public metadata needed for privacy-safe analysis. A representative record looks like this:
{"candidateId":"S8FL00216","name":"BARTLETT, HAMILTON ALLEN SMITH","office":"Senate","party":"REP","totalsStatus":"available","receipts":3852434.85,"disbursements":831532.74,"totalsCycle":2026,"source":"Federal Election Commission public data"}The candidateId field is the durable join key. The sourceUrl is the human-review path, while source identifies the upstream system. Dates, classifications, amounts, status fields, and source identifiers are snapshot evidence. Keep the raw row even if a downstream model uses only a subset; reprocessing is cheaper and more defensible than recollecting historical state.
Common pitfalls in openfec aggregate data
The most common failure is treating public-source data as a complete risk or quality verdict. Federal law restricts sale or commercial use of individual contributor information. This Actor intentionally returns no donor rows, names, employers, occupations, locations, or contact details.
Other pitfalls include mixing discovery and watchlist rows without recording mode, overwriting the previous good snapshot after an empty run, comparing rank across different query caps, and alerting on mutable tag names without retaining immutable version or digest evidence. Rate limits also make aggressive concurrency counterproductive.
Use explicit limits, stable queries, UTC collection times, and immutable snapshots. Join review decisions and external evidence in separate, attributed tables. The Thirdwatch Actor handles bounded requests, retries, deduplication, and public-source normalization; your downstream model remains responsible for the business decision.
Related use cases
These adjacent workflows extend the same evidence-first collection pattern. Continue with:
- Compare Fec Candidate Finance Totals
- Monitor Fec Candidate Receipts And Spending
- Build Fec Candidate Committee Dataset
- Build an npm package intelligence dataset
- Explore all Thirdwatch data workflows
- Read the business-data scraping guide
Source boundary and responsible use
The Actor never calls individual-contributor endpoints and removes treasurer, agent, phone, and address fields. With the shared DEMO_KEY, candidate discovery uses the official FEC cycle candidate-master file; supplied keys use live OpenFEC search. Totals depend on filing coverage and amendments. Each row reports totalsStatus, and a private OpenFEC key supports steadier enrichment at scale.
Federal law restricts sale or commercial use of individual contributor information. This Actor intentionally returns no donor rows, names, employers, occupations, locations, or contact details. The Actor saves a source URL and input query with every record so reviewers can trace a finding. Keep the unmodified dataset alongside any derived score, and label internal judgments as analysis rather than source facts.
Validate the pipeline with invariants and samples
Treat OpenFEC aggregate data as a data contract. Identity must be non-empty and unique, source URLs must use the expected host, counts cannot be negative, enumerated values must stay within known sets, and result volume must remain within the requested limits. Optional fields may be null; required evidence may not.
Add a canary input with a small, well-known query. Run it after code or schema changes and compare the returned structure with a stored contract fixture. This does not assert that mutable values never change. It asserts that identity, types, provenance, and bounded behavior remain intact.
For every release, inspect records from two different queries on OpenFEC. Record the run IDs and field-level checks. Automated tests catch regressions in normalization; source samples catch assumptions that were wrong even when the code executed perfectly.
Operate a governed inventory
An inventory becomes useful when every row has an owner, purpose, status, and evidence trail. Start with exact identifiers from deployment manifests, approved-tool lists, architecture repositories, or procurement records. Discovery search can reveal gaps, but it should not automatically add items to a governed production population. New records enter a review queue before becoming approved inventory.
Use the upstream canonical identity as the external key and an internal immutable ID as the warehouse key. Store environment, business owner, technical owner, approval state, first seen, last seen, and last reviewed outside the collector row. Keep public metadata as a dated snapshot. This design survives renames and lets policy history remain stable when upstream descriptions or counts change.
Define lifecycle states such as proposed, under review, approved, exception, deprecated, and removed. Each transition needs a reason and actor. A public archive flag or missing record can propose deprecation, but should not automatically remove an internally deployed dependency. Conversely, a popular and active upstream project is not automatically approved. External state informs internal control; it does not replace it.
Reconcile the inventory against real usage on a schedule. Report deployed-but-unregistered identities, registered-but-unseen identities, duplicate aliases, and rows whose evidence is stale. Route discrepancies to the relevant owner rather than a central undifferentiated queue. Track median review age and unresolved exceptions as operating metrics.
Retain snapshots long enough to answer when a material fact changed and which internal systems were affected at that time. Join vulnerability, license, source-control, procurement, and deployment evidence through the canonical identity or a maintained mapping table. With those boundaries in place, a public catalog Actor becomes one reliable sensor in a broader governance system instead of an accidental system of record.
Inventory control metrics
Track coverage of deployed identities, percentage with assigned owners, evidence freshness, exception age, duplicate aliases, and time since last review. Counts of catalog records alone say nothing about governance. Coverage should be calculated against an independent source of real usage so the inventory cannot declare itself complete.
Test restore and history queries periodically. Select an identity, reconstruct its state at an earlier date, and identify the systems and owner attached at that time. If the warehouse cannot answer that question, snapshots are being stored without usable lineage. Keep deletion conservative: mark upstream disappearance and internal removal as separate dates.
Frequently asked questions
Can this workflow run on a schedule?
Yes. Save the tested input as an Apify Task, schedule it at an interval that matches the decision, and retain dated datasets so changes remain reviewable.
What should identify a stable record?
Use the canonical candidateId as the primary key, preserve the source URL, and treat mutable popularity and version fields as snapshot attributes rather than identity.
Related
100 free credits, no credit card.
About 30 real searches. Add the MCP to Claude or Cursor in two minutes.