Making requests
Response format
Successful responses are JSON with data, meta, and links. Fields we add later appear without notice, so ignore fields you don't recognize.
- Dates are
YYYY-MM-DD; timestamps are ISO 8601 in UTC. Date filters takefromandto(inclusive). - Members are identified by ward number (1-50). Members who left during the term use 100 + ward (for example
135). - Legislation can be looked up by the Clerk's matter id or its record number (
O2026-0028502). - Votes are spelled out:
yea,nay,absent,recused,excused,not_voting,not_in_office. - Links: every record has
links.page(to cite),links.markdown(to read), andlinks.api(to follow). - AI-written fields (
aiSummary,impactReport,aiProfile, meetingnotes,answer) are labeled and point to their sources. All counts are computed from City data. - Freshness:
meta.dataUpdatedsays when the underlying data was last refreshed.
Pagination
Lists return up to limit items (default 50, maximum 200). When there are more, meta.nextCursor and links.next are set; request links.next until it is missing. meta.total is the total number of matches.
async function getAll(url) {
const items = [];
while (url) {
const res = await fetch(url, { headers: { Authorization: 'Bearer ' + process.env.CHICAGO_POLITICS_KEY } });
if (!res.ok) throw new Error((await res.json()).detail);
const body = await res.json();
items.push(...body.data);
url = body.links.next; // missing on the last page
}
return items;
}
const nays = await getAll('https://www.chicagopolitics.org/api/v1/members/12/votes?vote=nay&limit=200');
import os
import requests
session = requests.Session()
session.headers["Authorization"] = f"Bearer {os.environ['CHICAGO_POLITICS_KEY']}"
def get_all(url):
items = []
while url:
r = session.get(url)
r.raise_for_status()
body = r.json()
items += body["data"]
url = body["links"].get("next") # missing on the last page
return items
nays = get_all("https://www.chicagopolitics.org/api/v1/members/12/votes?vote=nay&limit=200")
Caching and CORS
Responses send Cache-Control: private, max-age=60, and most data changes at most daily (see data sources), so cache responses on your side. CORS is open (Access-Control-Allow-Origin: *) and the rate-limit headers are exposed to browsers, but keep keys on a server.