Loading CIV.IQ
Preparing your civic information dashboard
Preparing your civic information dashboard
This platform publishes civic data through open protocols. No API key required.
Every piece of data on CIV.IQ is available through open protocols. No account. No API key. No tracking. We pull from official government sources and publish to decentralized networks so these records exist independently of this website.
Download complete datasets as CSV or JSON. Updated hourly from official government sources. No account required.
All current members of the 119th Congress with party, state, district, and contact info.
Congressional committees and their members with roles and seniority.
The 250 most recently updated bills in the 119th Congress.
The 20 most recent roll-call vote summaries from both chambers.
Individual member voting positions for recent roll-call votes.
Campaign finance totals for all members of Congress from FEC.gov.
All datasets are public domain. CSV files include metadata comment headers. JSON files include a column data dictionary. Data refreshes every hour via ISR (campaign finance refreshes daily).
No API key, no registration. Paste this into a terminal:
curl https://civdotiq.org/api/v1/bills?limit=5
{
"data": "[ ... ]",
"pagination": {
"total": "...",
"limit": 5,
"offset": 0,
"hasMore": "..."
},
"meta": {
"apiVersion": "v1",
"timestamp": "...",
"source": "congress.gov",
"license": "MIT",
"documentation": "https://civdotiq.org/docs/api"
}
}Every response includes data, optional pagination, and meta with source attribution. Try the URL above to see live data.
| Detail | Value |
|---|---|
| Authentication | None required |
| Rate limit | 60 requests/min per IP. Headers: X-RateLimit-Remaining, X-RateLimit-Reset |
| Response format | JSON with data, pagination, meta envelope |
| CORS | Access-Control-Allow-Origin: * on all endpoints |
| Freshness | Legislative data cached 1–24 hours depending on endpoint. Historical data cached longer. Cache headers included in every response. |
JavaScript: fetch('https://civdotiq.org/api/v1/bills?limit=5').then(r => r.json())
10 endpoints covering representatives, bills, votes, districts, and committees.
https://civdotiq.org/api/v1| Method | Path | Description | Parameters |
|---|---|---|---|
| GET | /representatives | List all current members of Congress | chamber, state, party, limit, offset |
| GET | /representatives/{bioguideId} | Detailed info for one member | — |
| GET | /bills | Latest bills from Congress | sort, limit, offset |
| GET | /bills/{billId} | Bill detail from Congress.gov | — |
| GET | /bills/{billId}/summary | Cached plain-language bill summary | — |
| GET | /votes/{voteId} | Roll-call vote details | — |
| GET | /districts/{districtId} | District info and representatives | — |
| GET | /committees | List congressional committees | chamber, limit, offset |
| GET | /committees/{committeeId} | Committee detail with members | — |
| GET | /changelog | API version history | — |
OpenAPI 3.0 specification · Full API reference · Self-describing index
List endpoints use offset-based pagination. The response pagination object tells you where you are.
| Parameter | Type | Description |
|---|---|---|
| limit | integer | Results per page. Default varies by endpoint (50–100). Max 250–535. |
| offset | integer | Number of results to skip. Default 0. |
| hasMore | boolean | Returned in response. true when offset + limit < total. |
# Fetch page by page until hasMore is false offset=0 while true; do resp=$(curl -s "https://civdotiq.org/api/v1/bills?limit=50&offset=$offset") echo "$resp" | jq '.data | length' has_more=$(echo "$resp" | jq '.pagination.hasMore') [ "$has_more" = "false" ] && break offset=$((offset + 50)) done
Errors use the same envelope with an error object instead of data.
{
"error": {
"code": 404,
"message": "Bill not found",
"details": "No bill matching ID \"119-hr-99999\""
},
"meta": {
"apiVersion": "v1",
"timestamp": "...",
"source": "error",
"license": "MIT",
"documentation": "https://civdotiq.org/docs/api"
}
}| Code | Meaning |
|---|---|
| 400 | Invalid request parameters |
| 404 | Resource not found |
| 429 | Rate limited. Check X-RateLimit-Reset header. |
| 500 | Internal error |
| 503 | Upstream data source temporarily unavailable |
import requests
resp = requests.get("https://civdotiq.org/api/v1/representatives", params={
"chamber": "senate",
"state": "MI"
})
data = resp.json()
for member in data["data"]:
print(f'{member["name"]} ({member["party"]})')
print(f'Source: {data["meta"]["source"]}')async function fetchAllBills() {
const bills = [];
let offset = 0;
while (true) {
const url = `https://civdotiq.org/api/v1/bills?limit=250&offset=${offset}`;
const { data, pagination } = await fetch(url).then(r => r.json());
bills.push(...data);
if (!pagination.hasMore) break;
offset += 250;
}
return bills;
}import feedparser
feed = feedparser.parse("https://civdotiq.org/api/feed/member/P000197")
for entry in feed.entries:
print(f'{entry.updated}: {entry.title}')
print(f' {entry.link}')# Fetch two members in parallel curl -s "https://civdotiq.org/api/v1/representatives/P000197" > rep1.json & curl -s "https://civdotiq.org/api/v1/representatives/S000148" > rep2.json & wait # Compare with jq jq -s '.[0].data.name, .[1].data.name' rep1.json rep2.json
Subscribe to civic updates in any RSS/Atom reader. All feeds return application/atom+xml.
| Feed | Description |
|---|---|
| /api/feed/bills/latest/feeds/bills | Latest bills introduced in Congress |
| /api/feed/member/{bioguideId}/feeds/representative/{bioguideId} | Activity feed for a member of Congress |
| /api/feed/bill/{billId} | Status updates for a specific bill |
| /api/feed/committee/{committeeId} | Activity feed for a congressional committee |
| /api/feed/district/{districtId} | Activity feed for a congressional district |
| /feeds/floor | House and Senate floor activity |
| /api/feed/state/{state}/bills | State legislation feed |
| /api/feed/state/{state}/legislator/{id} | State legislator activity feed |
curl -H "Accept: application/atom+xml" https://civdotiq.org/api/feed/bills/latest
Every significant civic event is cryptographically signed and published to Nostr relays as a permanent public record. Events use NIP-23 long-form content (kind 30023) with Markdown content readable in any long-form client. A NIP-65 relay list (kind 10002) is published so clients auto-discover which relays carry CIV.IQ data. State coverage spans 15 states.
civiq@civdotiq.org| Type | Description |
|---|---|
| bill-action | Bill status changes (passed, amended, signed) |
| bill-introduced | New bills introduced in Congress |
| vote-record | Roll-call vote results |
| executive-order | Presidential executive orders |
| comment-period | Federal regulation comment periods |
| hearing | Congressional committee hearings |
| state-bill-introduced | State legislation introduced |
| state-bill-action | State bill status changes |
| state-vote | State legislature votes |
{
"kind": 30023,
"content": "# HR 1 passed in House\n\nThe bill passed...\n\n**Type**: bill-action | **Source**: [congress.gov](...)\n\n---\n\n<details><summary>Structured Data</summary>\n\n```json\n{ ... }\n```\n\n</details>",
"tags": [
[
"d",
"civiq:bill-action:hr1-119-action-2025-01-15"
],
[
"title",
"HR 1 passed in House"
],
[
"summary",
"The bill passed..."
],
[
"published_at",
"1705276800"
],
[
"t",
"bill-action"
],
[
"t",
"legislation"
],
[
"r",
"https://www.congress.gov/bill/119th-congress/house-bill/1"
]
],
"pubkey": "<civiq public key>",
"sig": "<schnorr signature>"
}Content is Markdown, readable in any NIP-23 client (Habla, Yakihonne, Highlighter). Structured data is preserved in a collapsible <details> block. Filter by t tag to subscribe to specific event types. The d tag is the unique event identifier.
import { SimplePool } from 'nostr-tools/pool';
const pool = new SimplePool();
const relays = [
"wss://relay.damus.io",
"wss://relay.snort.social",
"wss://nos.lol"
];
// Subscribe to all CIV.IQ civic events
const sub = pool.subscribeMany(relays, [{
kinds: [30023],
'#L': ['civic-event'],
// Optional: filter by type
// '#t': ['bill-action', 'vote-record'],
}], {
onevent(event) {
const title = event.tags.find(t => t[0] === 'title')?.[1];
const type = event.tags.find(t => t[0] === 't')?.[1];
console.log(`[${type}] ${title}`);
console.log(event.content.slice(0, 200));
}
});Follow CIV.IQ from Mastodon or any ActivityPub client. The same civic events published to Nostr are delivered to every follower's inbox as signed ActivityPub activities.
@civiq@civdotiq.org| Endpoint | Purpose |
|---|---|
| /api/activitypub/actor | JSON-LD actor document (Service type) |
| /api/activitypub/inbox | Receives Follow/Undo activities from remote instances |
| /api/activitypub/outbox | Paginated OrderedCollection of published activities |
| /api/activitypub/followers | Paginated OrderedCollection of follower actors |
| /api/activitypub/following | Empty collection (CIV.IQ is publish-only) |
| /.well-known/nodeinfo | NodeInfo 2.0 for fediverse directory discovery |
Search @civiq@civdotiq.org in your Mastodon instance to follow. New civic events are delivered directly to follower inboxes via HTTP Signature-authenticated POST. Activities support Create and Update types.
Machine-readable endpoints for protocol discovery.
| Standard | URL |
|---|---|
| OpenAPI 3.0 | /openapi.json |
| WebFinger (RFC 7033) | /.well-known/webfinger |
| NIP-05 (Nostr) | /.well-known/nostr.json |
| NodeInfo 2.0 | /.well-known/nodeinfo |
| NIP-65 Relay List | Kind 10002 event (published to relays) |
| Atom 1.0 (RFC 4287) | /feeds/bills |
Cache durations per endpoint. Every response includes Cache-Control headers so your HTTP client can cache automatically. s-maxage is the CDN cache time; stale-while-revalidate allows serving stale data while refreshing in the background.
| Endpoint | Cache (s-maxage) | Stale-while-revalidate |
|---|---|---|
| Bills list | 1 hour | 2 hours |
| Bill detail (active) | 24 hours | 1 hour |
| Bill detail (historical) | 1 year | 24 hours |
| Bill summary | 1 hour | 2 hours |
| Representatives list | 1 hour | 24 hours |
| Representative detail | 1 hour | 2 hours |
| Vote detail | 1 hour | 2 hours |
| District detail | 24 hours | 48 hours |
| Committees list | 24 hours | 48 hours |
| Committee detail | 1 hour | 2 hours |
Making publicly accessible data easier to understand. Licensed under MIT.
| Source | Data |
|---|---|
| Congress.gov API | Members, bills, votes, committees |
| House Clerk | House floor votes |
| Senate.gov | Senate floor schedule, roll-call votes |
| GovInfo | Bill text, legislative documents |
| Federal Register | Regulations, federal documents |
| FEC | Campaign finance, contributions, committees |
| USASpending.gov | Federal spending, contracts, grants |
| Senate LDA | Lobbying disclosures |
| U.S. Census Bureau | Demographics, geocoding, district boundaries |
| Bureau of Labor Statistics | Employment, economic indicators |
| CDC Open Data | Public health statistics |
| Dept. of Education | School district data |
| FCC Open Data | Broadband deployment |
| OpenStates | State legislature data |
| Wikipedia | Reference information |
| Wikidata | Structured reference data |
| NewsAPI | News aggregation |