Developer documentation
One key, one base URL, structured JSON for live government meetings, elections, the congressional schedule, politician profiles, and civic news.
Quickstart
Three steps to your first response. The free tier needs no card and the key takes effect the moment it's created.
-
1
Create an account & pick a plan
Sign up on the pricing page. Free works for personal projects; paid tiers unlock higher quotas and more keys.
-
2
Generate a key
Go to Settings → API and click Create key. The full secret is shown once — copy it immediately, we only store a hash.
-
3
Make your first call
curl https://civicstream.tv/wp-json/cstv/public/v1/whoami \ -H "X-CivicStream-Key: csk_live_..."You should get back your plan name, current monthly usage, and the key prefix.
Rate limits
Each account has two budgets: a monthly quota and a
per-minute burst cap. All keys on an account share those
budgets. Hit either and you'll get a 429 Too Many Requests
with a Retry-After header (seconds).
| Plan | Monthly requests | Per-minute burst | Keys |
|---|---|---|---|
| Free | 10,000 | 30 / min | 1 |
| Starter | 250,000 | 120 / min | 1 |
| Pro | 2,000,000 | 600 / min | 5 |
| Business | 10,000,000 | 2,000 / min | 25 |
| Enterprise | Custom | Custom | Unlimited |
Every response carries headers so you can self-pace:
X-RateLimit-Limit— monthly cap on this accountX-RateLimit-Remaining— requests left this monthX-RateLimit-Reset— UNIX timestamp when the month rolls overX-RateLimit-Minute-Remaining— left in the current minute window
Every tier hard-stops at its monthly cap. Contact us before a launch if you need a custom quota rather than relying on an unexpected overage.
Caching
Caching is required, not optional. Every response includes an
ETag and a Cache-Control: public, max-age=N.
Send back If-None-Match on subsequent requests and you'll
get a 304 Not Modified — which does not count
against your quota.
# First call — full response + ETag
curl -i https://civicstream.tv/wp-json/cstv/public/v1/streams/live \
-H "X-CivicStream-Key: csk_live_..."
# HTTP/1.1 200 OK
# ETag: "ab12cd34"
# Cache-Control: public, max-age=30
# Subsequent call — send ETag back
curl -i https://civicstream.tv/wp-json/cstv/public/v1/streams/live \
-H "X-CivicStream-Key: csk_live_..." \
-H "If-None-Match: \"ab12cd34\""
# HTTP/1.1 304 Not Modified ← free request
Typical max-ages: /streams/live 60s,
/news/recent 120s, /schedule/today 300s,
/elections/upcoming 1,800s. Cache-friendly clients commonly
cut their billable requests by 80–95%.
Errors
Errors are JSON, with a consistent shape. Status code tells you the
category; code is a machine code, message is
a sentence you can show to a developer (not an end-user).
{
"code": "rate_limited",
"message": "Per-minute rate limit exceeded. Slow down and retry in 60 seconds.",
"data": { "status": 429 }
}
| Status | Error code | What happened |
|---|---|---|
| 400 | invalid_request | Bad parameter (e.g. limit=abc). |
| 401 | missing_api_key | No X-CivicStream-Key header. |
| 401 | invalid_api_key | Key doesn't match any active record. |
| 403 | plan_required | Endpoint requires a higher plan. |
| 401 | revoked_api_key | Key was revoked — create a new one. |
| 404 | not_found | Resource ID doesn't exist. |
| 429 | rate_limited | Monthly or per-minute cap hit. |
| 5xx | server_error | Our problem. Retry with exponential backoff. |
Endpoints
Data endpoints return JSON and live under https://civicstream.tv/wp-json/cstv/public/v1.
Core civic records stay on Free. Badges below identify paid enrichments.
/streams/live
Currently live government streams — federal floor, committees, statehouses, city councils. Updated every ~30s. Returns metadata and live status only; stream URLs are not exposed via the API (watch on civicstream.tv).
Example response
{
"data": [
{
"id": 4821,
"title": "U.S. Senate — Floor",
"scope": "federal",
"state": null,
"started_at": "2026-06-08T14:02:11Z",
"is_live": true,
"thumbnail": "https://civicstream.tv/thumbs/4821.jpg"
}
],
"meta": { "count": 18 }
}
/news/recent
Recent civic-news headlines. Use /news for category, search, and pagination.
/schedule/today
Today's federal schedule — House, Senate, committees, White House — with start times in UTC.
/elections/upcoming
Compact upcoming races. Use /elections for state, office, year, date, and search filters.
/elections/calendar
Date-bucketed election calendar. days accepts 1–30.
/whoami
Confirms your key. Returns plan, key prefix, monthly usage, and reset timestamp. Free of charge — does not count against quota.
| Resource | Endpoint | Free access | Paid enrichment |
|---|---|---|---|
| Location | /location/resolve, /representatives | Districts and public officials | Pro: district geometry; Starter: /local/overview |
| Politicians | /politicians, /politicians/{scope}/{id} | Directory, search, and public profiles | Pro: campaign finance and race history |
| Bills | /bills, /bills/{jurisdiction}/{id} | Metadata, status, and official links | Pro: available bill text |
| Elections | /elections, /elections/{id} | Current races and candidates | Starter: result detail; Pro: historical races |
| Schedules | /schedule, /schedule/calendar | Today and nearby dates | Starter: extended dates; Pro: daily recaps |
| Streams | /streams, /streams/live | Directory and live status | Starter: incremental sync and larger pages |
| Committees | /committees, /committees/{chamber}/{slug} | Committee directory and metadata | Starter: membership |
| News | /news, /news/{id} | Searchable civic news | Starter: larger pages and unified /search |
| Integrations | /webhooks, /changes, /bulk/{dataset} | — | Starter webhooks; Pro changes; Business bulk |
Free responses are capped at 25 records. Starter and Pro allow 100; Business allows 500. The machine-readable contract is at /openapi.json.
Code examples
Same call, three languages. Swap in your real key.
curl https://civicstream.tv/wp-json/cstv/public/v1/streams/live \
-H "X-CivicStream-Key: $CIVICSTREAM_KEY" \
-H "Accept: application/json"
const res = await fetch(
'https://civicstream.tv/wp-json/cstv/public/v1/streams/live',
{ headers: { 'X-CivicStream-Key': process.env.CIVICSTREAM_KEY } }
);
if (!res.ok) throw new Error(`API ${res.status}`);
const { data } = await res.json();
console.log(`${data.length} streams live`);
import os, requests
r = requests.get(
"https://civicstream.tv/wp-json/cstv/public/v1/streams/live",
headers={"X-CivicStream-Key": os.environ["CIVICSTREAM_KEY"]},
timeout=10,
)
r.raise_for_status()
print(len(r.json()["data"]), "streams live")
Rules of the road
Plain-English terms. By using the API you agree to these — the formal version is in the Terms of Service.
- One API key per environment. Send it as
X-CivicStream-Keyon every request. - Quotas are shared by all keys on an account, reset each calendar month, and include a per-minute burst cap.
429includesRetry-After. - Caching is mandatory. Honour
ETag/If-None-Match— 304 responses don't count against quota. - Attribution required on display: “Data via CivicStream” with a link, on any screen that renders the data. Waivable at Enterprise.
- Free, Starter, and Pro may not mirror more than 25% of a dataset. Business bulk exports remain subject to the commercial data licence and may not be republished as a competing API.
- Stream URLs are not exposed via the API —
/streams/livereturns metadata and live status only. Viewing happens on civicstream.tv. Scraping or republishing the underlying HLS/MP4 is a bannable offence and a copyright issue. - No PII or engagement endpoints. Comments, chat, donor data, and user info are not exposed.
- Free tier is non-commercial — civic tech, journalists, students, hobbyists. Anything ad-supported, paywalled, or sold to clients needs Starter+.
- If you exhaust your quota, don't scrape the public site to make up the difference. The WAF will block you.
- Deprecations get 90 days' notice. Breaking changes ship under a new version path.
- Keys can be revoked without refund for ToS violations. Voter-suppression or harassment use cases are explicitly disallowed.
- Uptime: best-effort on Free/Starter, 99.5% on Pro, 99.9% on Business (with credits).
Versioning & changelog
The current version is v1, mounted at /wp-json/cstv/public/v1.
Backwards-incompatible changes ship under /v2; v1
stays online for at least 12 months after a successor is released.
- 2026-07-14 — Added representatives, politicians, bills, committees, stream discovery, full election and schedule queries, news search, webhooks, change feeds, bulk exports, and OpenAPI discovery.
- 2026-06-08 —
v1launched: streams, news, schedule, elections, whoami.