Inspiration
Campaign finance data is technically public, but it’s fragmented and difficult to understand. We wanted to show how money actually moves... who funds whom, what causes connect donors, and how individuals and organizations fit into larger networks of influence.
What it does
NeoPolitan shows connections between individuals, organizations, and campaigns, with edges weighted by funding size. It also:
- Groups donors by political causes and affiliations.
- Uses a RAG system to highlight graph nodes and generate profile summaries relevant to a user’s search query.
- Builds hybrid profiles for candidates and donors based on the top-k “interests” or “associated topics” of a politician, combined with embeddings of retrieved web profiles.
- Links funding sources across causes + shows what other issues or candidates a donor supports.
- Learns groupings of funding sources using few-shot LLM modeling that takes in features like zip code, occupation, employer, and web search info to infer cause alignment..
How we built it
- Frontend: React, D3, Tailwind
- Backend: Flask, Postgres (Supabase), pgvector
- Data: OpenFEC bulk election data, Exa web search
- Infra: Redis caching for interactive graph performance (≈3K active nodes)
We integrated OpenFEC and CivicEngine datasets, handled missing and mismatched entities, and enriched donors through targeted web search. Each node (donor, campaign, or organization) is embedded for retrieval and clustering, enabling RAG-based profile generation.
Challenges we ran into
- Making the graph frontend performant at scale (lots of caching involved...)
- Cleaning and scraping OpenFEC data
- Managing bulk uploads and maintaining foreign key integrity in Postgres
- Handling broken relationships and data loss during large data wrangling steps Generally, these challenges exposed just how much hidden complexity already exists behind campaign finance data. This was very interesting and illuminating to us, since the very nature of the data (heavily obfuscated) made clear to us how BIG the “transparency” problem we were trying to fix.
Accomplishments that we're proud of
- Linking donors based on size and consistency of contributions
- Grouping by cause in a way that reflects real funding patterns
- Developed an interactive and scalable UI that makes exploring complex graphs manageable
- Getting an initial embedding-based RAG system working for contextual retrieval
What we learned
- Data is ugly, inconsistent, and full of obfuscation
- Simple, interpretable systems are easier to debug and trust than black-box embeddings
- Keeping things lightweight and explainable helps when features or topic areas drift
- Transparency isn’t just a design goal — it’s an engineering challenge
What's next for NeoPolitan
- Scaling beyond the 3K-node Redis cache
- Better filtering, sorting, and time-based graph views
- Integrating structured metadata into the RAG system to build out a hybrid serach (for less computationally expensive, and generally more accurate profiles).
- Thinking of ways we can articulate how influence propagation can be traced across campaigns with our tool
Built With
- d3.js
- exa
- flask
- nextjs
- openai
- postgresql
- python
- react
- supabase

Log in or sign up for Devpost to join the conversation.