API
Every meeting, agenda item and summary on this site is readable as JSON, CSV, RSS or a calendar subscription. No key, no account, no sign-up. Local government records are public, and so is reading them.
What you need before the first request
- The base address is
https://ncgovwatch.com. Every path below hangs off it. - Answers are JSON in UTF-8 unless the path says otherwise. Times are
RFC 3339 with the Eastern offset, so
2026-08-27T18:30:00-04:00is half past six on a summer evening in North Carolina. - Send a
User-Agentthat says who you are and how to reach you. Nothing rejects a request without one, but it is how a problem gets sorted out instead of blocked. - Rate limits are below. They are generous for a person and firm for a loop.
- A meeting id is a permanent address.
/meetings/412will always be the meeting it is today.
Meetings
GET /api/meetings
The meeting list. It takes the same filters as the pages on this site, so any view you can build by clicking is also an address you can fetch.
| Parameter | What it takes |
|---|---|
entity | A government slug, from the table further down. |
board | What kind of body: governing, planning, school, committee, other. |
type | What kind of meeting, such as work-session. |
action | Meetings holding an item of this kind, such as public-hearing. |
topic | A subject slug, such as rezoning. |
q | Free text, searched across agendas, summaries, minutes and captions. |
when | upcoming (the default), month, past, all. A request with q and no when searches everything, because most of what is worth finding has already happened. |
limit | 1 to 500. Default 100. |
GET /api/meetings?entity=cary&topic=rezoning&when=all&limit=5
{
"meetings": [
{
"id": 412,
"title": "Town Council Regular Meeting",
"government": "Town of Cary",
"board": "Town Council",
"board_kind": "governing",
"starts_at": "2026-08-27T18:30:00-04:00",
"location": "Town Hall Council Chamber",
"status": "scheduled",
"meeting_type": "regular",
"topics": ["rezoning", "water-sewer"],
"item_count": 34,
"summary": "Two paragraphs on what this body will decide.",
"agenda_url": "https://carync.iqm2.com/Citizens/FileOpen.aspx?Type=14&ID=2843",
"minutes_url": "",
"url": "/meetings/412"
}
],
"count": 1
}
agenda_url, packet_url,
minutes_url, stream_url and detail_url
point at the government's own documents, not at copies held here. They are the
record. summary is absent until somebody has read the agenda.
GET /api/meetings/{id}
One meeting with its agenda items. Each item carries its number and title,
what kind of action it asks for, the subjects it was sorted under, a summary,
and the recorded outcome where the source published one. It may also carry
matter_url, which is the item's own document: a staff report on
most agendas, and the bill itself on a General Assembly calendar.
An item that names a property also carries places, with whatever
the county parcel file answered.
"places": [
{
"kind": "address",
"raw": "4312 Jacobs Creek Lane",
"status": "matched",
"parcel": {
"county": "Wake",
"parcel_no": "0723194404",
"owner": "STERLING WEST LLC",
"acres": 8.92,
"assessed": 1188857,
"lat": 35.8217, "lon": -78.8695,
"url": "/parcels/17"
}
}
]
status is matched or
not_found, and is absent when the reference has not been looked up
yet. not_found is ordinary rather than an error. Land is merged,
split and renumbered between the agenda and the parcel file, and some counties
publish thinner records than others.
Property
GET /api/parcels.geojson
Every matched property as GeoJSON, which is what the map on this site reads.
Takes entity, county, topic,
from and to as YYYY-MM-DD, and
limit from 1 to 2000, default 500. When more match than the limit
allows, truncated comes back true. The answer to that is a narrower
filter rather than a second page: a thousand outlines on one map is a smear.
GET /api/parcels/{id}
One parcel, as a single-feature collection.
Vocabularies and coverage
GET /api/vocabulary
The three closed lists this site sorts everything into: subjects, meeting types and action types. They are also printed at the bottom of this page.
GET /api/concepts
The wordings search treats as the same thing, so a query for "granny flat" reaches an item that says "accessory dwelling unit". Each group has a canonical phrase and the phrases that reach it.
GET /api/status
The Sources page as JSON, in three parts.
sources is one row per government with its state and how many
meetings are held for it. holdings is what came of that across the
whole site, including unread, which is how many meetings have a
document nobody has read yet. queue is the reading backlog's own
bookkeeping.
found_last_run and
meetings_on_file are different numbers and only the second means
what a reader expects. A government whose whole published calendar has already
been collected finds nothing new on the next run, so found_last_run
drops to zero while nothing is wrong. Use meetings_on_file for how
much this site holds.
GET /api/finances
Audited figures from each government's annual financial report. With no
parameters it answers the coverage question first: which governments are short
of years. With entity it gives that government's rows and
the note written about them. With year, every government for that
fiscal year.
Every figure carries whether it was reported. Without that a zero is unreadable: a town that levies no sales tax and a town whose sales tax was never filed both store zero and mean different things.
GET /api/channels
Where each government streams its meetings, one row per government.
GET /api/build
Which version of this site is running, and when it started.
Feeds and exports
Not JSON, but the same surface and the same filters. A narrowed view becomes a narrow subscription.
| Path | Format | Example |
|---|---|---|
/calendar.ics | iCalendar, with a real time zone and an hourly refresh hint | rezoning in Apex |
/feed.xml | RSS 2.0 | every planning board |
/export.csv | One row per meeting | everything collected |
/items.csv | One row per agenda item, with its subjects and outcome | a year of rezoning items |
/analysis.csv | Subject counts per government, with the per-resident rates behind the analysis page | the last twelve months |
/items.csv takes entity,
board, topic, action, q,
months, and from and to as
YYYY-MM-DD. It returns at most 500 rows, so page it by government
and month to walk the whole corpus.
Rate limits
Requests are counted per address, in three separate budgets. Spending one leaves the other two untouched, so a script pulling exports cannot lock a reader out of the meeting pages, and a reader searching hard is not competing with it.
| Kind of request | Paths | Requests a minute |
|---|---|---|
| Pages | everything else | 120 |
| Search and the wider views | /search, /items, /watch, /map, /trends, /analysis, /compare | 30 |
| JSON, feeds and exports | /api/…, /calendar.ics, /feed.xml, /export.csv, /items.csv, /analysis.csv | 60 |
The whole minute's worth can be spent at once and then
refills steadily, so a burst is fine and a sustained loop is what runs out.
Stylesheets, scripts, robots.txt and the health check are not
counted at all. Every answer carries RateLimit-Limit and
RateLimit-Remaining. Going over gets a 429 with
Retry-After in seconds; wait that long and the next request goes
through. If you want more than this, the exports hand over the whole corpus in
one request, which is cheaper for both of us than paging.
What to know before you publish anything from this
- The agenda documents are the record. Every meeting links to the government's own, and where this site and that document disagree, the document is right.
- Summaries and the written narratives are produced by a language model reading the published documents. Treat them as a reading aid and open the agenda before quoting anything.
- Captions are automatic transcripts. They misrender names and figures, and a search hit marked as one should be checked against the recording.
- An empty result is not proof of nothing. Read the Sources page for what was reachable and when.
Governments
The entity slug for each government
currently collected.
| Slug | Government |
|---|---|
nc-general-assembly | NC General Assembly |
wake-county | Wake County |
durham-county | Durham County |
chatham-county | Chatham County |
durham-city | City of Durham |
raleigh | City of Raleigh |
cary | Town of Cary |
chapel-hill | Town of Chapel Hill |
apex | Town of Apex |
wake-forest | Town of Wake Forest |
holly-springs | Town of Holly Springs |
hillsborough | Town of Hillsborough |
fuquay-varina | Town of Fuquay-Varina |
knightdale | Town of Knightdale |
garner | Town of Garner |
morrisville | Town of Morrisville |
orange-county | Orange County |
pittsboro | Town of Pittsboro |
sanford | City of Sanford |
wcpss | Wake County Public School System |
chatham-county-schools | Chatham County Schools |
Subjects
The topic slugs. One fixed list for every
government, which is what makes a rezoning in Apex countable against a rezoning
in Cary.
| Slug | Subject |
|---|---|
appointments | Appointments and personnel |
civil-rights | Civil rights and nondiscrimination |
dei-programs | DEI programs |
elections-districts | Elections and districts |
ethics-transparency | Ethics and transparency |
immigration | Immigration and enforcement |
intergovernmental | Intergovernmental agreements |
lgbtq-policy | LGBTQ+ policy |
litigation | Litigation and settlements |
legislative-agenda | State legislative agenda |
surveillance | Surveillance and cameras |
broadband | Broadband and technology |
facilities | Public facilities |
transportation | Roads and transportation |
solid-waste | Solid waste and recycling |
stormwater | Stormwater and flooding |
transit | Transit |
water-sewer | Water and sewer |
annexation | Annexation |
comprehensive-plan | Comprehensive plan |
data-centers | Data centers and large loads |
farmland-conservation | Farmland and open space |
historic | Historic preservation |
housing | Housing and affordability |
land-conversion | Land conversion and large development |
rezoning | Rezoning |
subdivision | Subdivision and site plans |
text-amendment | Zoning text amendment |
bonds-debt | Bonds and debt |
budget | Budget and appropriations |
procurement | Contracts and procurement |
incentives | Economic development incentives |
fees-rates | Fees and utility rates |
grants | Grants and outside funding |
tax-rate | Tax rate and revaluation |
arts-culture | Arts and public art |
drugs-addiction | Drugs and addiction |
environment | Environment and sustainability |
faith-culture | Faith and cultural communities |
fire-ems | Fire and EMS |
health-human-services | Health and human services |
homelessness-shelter | Homelessness and shelter |
parks-rec | Parks and recreation |
public-safety | Police and public safety |
schools | Schools and education |
Meeting and action types
| Slug | Kind of meeting |
|---|---|
regular | Regular meeting |
work-session | Work session |
public-hearing | Public hearing |
special | Special or emergency meeting |
committee | Committee meeting |
advisory | Advisory board meeting |
quasi-judicial | Quasi-judicial hearing |
budget | Budget session |
retreat | Retreat or planning session |
annual | Annual or organizational meeting |
closed-session | Closed session |
legislative-session | Legislative session |
other | Other |
| Slug | What the item asks for |
|---|---|
consent | Consent agenda |
public-hearing | Public hearing |
ordinance | Ordinance |
resolution | Resolution |
contract | Contract or award |
appointment | Appointment |
personnel | Personnel |
budget-action | Budget action |
presentation | Presentation |
report | Staff or committee report |
discussion | Discussion item |
proclamation | Proclamation or recognition |
minutes | Minutes approval |
closed-session | Closed session |
procedural | Procedural |
other | Other |