Skip to main content

GraphQL API Reference

GovQL exposes all data through a PostGraphile-generated GraphQL API at https://api.govql.us/graphql.

PostGraphile reflects the PostgreSQL schema directly into GraphQL — tables become types, columns become fields, and foreign keys become nested resolvers. The schema below is the complete public API surface.

tip

Try queries live in the built-in GraphiQL interface at api.govql.us/graphql.

Data model

Legislators ──< LegislatorTerms
Legislators ──< VotePositions >── Votes ──< VotePositions
Legislators ──< BillCosponsors >── Bills >── Votes
Bills ──< BillCommittees >── Committees ──< CommitteeMemberships >── Legislators

Available types

TypeallXxx queryDescription
LegislatorallLegislatorsEvery person who has served in Congress
LegislatorTermallLegislatorTermsIndividual terms of service per chamber/state
VoteallVotesRoll call vote events (House and Senate)
VotePositionallVotePositionsIndividual member Yea/Nay positions per vote
BillallBillsLegislation referenced by roll call votes
CommitteeallCommitteesHouse, Senate, and Joint committees
BillCosponsorallBillCosponsorsCosponsor records for bills
BillCommitteeallBillCommitteesCommittee referrals for bills
CommitteeMembershipallCommitteeMembershipsCurrent committee membership rosters

Common patterns

Filtering

Every allXxx query accepts a filter argument with per-field conditions:

{
allVotes(
filter: {
chamber: { equalTo: "s" }
category: { equalTo: "nomination" }
congress: { greaterThanOrEqualTo: 117 }
}
) {
nodes { voteId votedAt question result }
}
}

String fields support equalTo, notEqualTo, includes, startsWith, and more. Numeric fields support equalTo, lessThan, greaterThan, between, etc.

Pagination

All list queries use cursor-based pagination via first / after (forward) or last / before (backward):

{
allVotes(first: 20, after: "cursor-from-previous-page") {
pageInfo { hasNextPage endCursor }
nodes { voteId votedAt }
}
}

Ordering

Pass an orderBy enum value — field names are SCREAMING_SNAKE_CASE suffixed with _ASC or _DESC:

{
allLegislators(orderBy: LAST_NAME_ASC, first: 50) {
nodes { officialFull }
}
}

Nested relationships

Foreign keys are automatically resolved as nested fields — no manual joins required:

{
allVotePositions(
filter: { voteId: { equalTo: "s83-119.2025" } }
) {
nodes {
position
party
legislatorByBioguideId {
officialFull
}
voteByVoteId {
question
result
votedAt
}
}
}
}

Reverse relationships (one-to-many) return a connection with nodes:

{
allLegislators(
filter: { lastName: { equalTo: "Warren" } }
) {
nodes {
officialFull
legislatorTermsByBioguideId {
nodes { startDate endDate state termType party }
}
}
}
}

Aggregation

Some common counts are exposed as aggregates — the database does the grouping, so you don't have to fetch every position row and tally it client-side. Each is a filterable connection like any other type. Filter them to the slice you care about (a vote, a member, a congress) rather than scanning everything.

Party breakdown of a vote — how each party split, in one round-trip:

{
allVotePartyBreakdowns(filter: { voteId: { equalTo: "s83-119.2025" } }) {
nodes { party position positions }
}
}

Returns one row per (party, position), e.g. { party: "D", position: "Yea", positions: 45 }.

Position totals for a vote — the overall Yea/Nay/Present/Not Voting tally:

{
allVoteTotals(filter: { voteId: { equalTo: "s83-119.2025" } }) {
nodes { position positions }
}
}

A member's voting record, summarised by congress and vote category:

{
allMemberVotingSummaries(
filter: {
bioguideId: { equalTo: "W000817" }
congress: { equalTo: 119 }
category: { equalTo: "cloture" }
}
) {
nodes { position positions }
}
}

congress and category are optional — omit them for a member's full record across all congresses and categories.

Voting similarity — pairwise agreement between members within a congress is precomputed in allVoteSimilarities (all congresses). Each row gives sharedVotes (votes where both members cast a Yea/Nay) and agreed (votes where they matched); compute the agreement ratio as agreed / sharedVotes. Filter by congress (and usually chamber). Find a member's closest allies in a given congress:

{
allVoteSimilarities(
filter: {
congress: { equalTo: 119 }
chamber: { equalTo: "s" }
memberA: { equalTo: "W000817" }
}
orderBy: AGREED_DESC
first: 5
) {
nodes { memberB sharedVotes agreed }
}
}

Pairs are stored once with memberA < memberB, so to find all of one member's pairings you may need to match on memberA or memberB.

Member-vs-party agreement — how often each member voted with a party is precomputed in allMemberPartyAgreements (all congresses). On each vote, a party's "position" is its majority of Yea/Nay, and a member agrees when their Yea/Nay matches it. Each row gives sharedVotes (votes where the member cast Yea/Nay and the party had a majority), agreed, and agreementRate (agreed / sharedVotes, ready to sort on). otherParty includes the member's own party — that row is a party-loyalty measure. Which Democrats most often voted with Republicans this Congress:

{
allMemberPartyAgreements(
filter: {
congress: { equalTo: 119 }
memberParty: { equalTo: "D" }
otherParty: { equalTo: "R" }
sharedVotes: { greaterThanOrEqualTo: 20 }
}
orderBy: AGREEMENT_RATE_DESC
first: 5
) {
nodes {
agreementRate
sharedVotes
legislatorByBioguideId { officialFull }
}
}
}

The sharedVotes floor filters out members with too few comparable votes to be meaningful. Drop memberParty/otherParty to compare a member against every party at once.


note

The per-type pages in this section are generated from the db/migrations/ Flyway migration files by scripts/generate-schema-docs.mjs. Re-run npm run generate-schema-docs after schema changes.