API guide
Credentials, rate limits and error handling Β· reference lives at /apiWhich credential do I need?
Three ways to call CivicGate, in increasing order of what they let you do.
| Credential | Gets you | Cannot |
|---|---|---|
| None | Every public read β bills, people, votes, money, courts. No sign-up. | Anything account-scoped. Limited per IP. |
| API key create one | Public reads plus that account's own data (its feed, its subscriptions). Safe to put in a public page. | Write anything. Keys are read-only by design, so a key pasted into a web page cannot change the account it belongs to. |
| Application (OAuth) register one | Acts as a signed-in user who consented β including writes they could make themselves. | Act without that consent, or exceed that user's own rate budget. |
Rate limits
Every request is charged to an identity, not an address: an API key is charged to the key, an Application to the user who authorised it, and anonymous traffic to its IP. That means one key spread across many servers still shares one budget, and a busy office behind one address does not share a budget with strangers.
| Tier | Limit | |
|---|---|---|
| Free | 300 / minute | 300 requests per minute. Enough for a personal site, a dashboard, or a widget on a low-traffic page. |
| Subscriber | 1,000 / minute | 1,000 requests per minute, for a production site or an app with real traffic. |
| Enterprise | No enforced limit | No enforced request limit. Arranged directly. |
Every account starts on Free. Higher tiers are arranged directly for now β get in touch.
When you hit it
The response carries a GraphQL error with a machine-readable code:
{
"errors": [{
"message": "Rate limit exceeded: 300/minute on the free tier. β¦",
"extensions": { "code": "RATE_LIMITED", "retryable": true, "trace_id": "β¦" }
}]
} retryable: true means exactly that β the window is one minute, so back off and
retry rather than treating it as a failure. Every CivicGate error carries
code, retryable and a trace_id worth quoting if you
report a problem.
Using an API key
Send it as X-API-Key. Keys are shown once at creation and stored only as a hash β if you lose one, revoke it and make another.
curl https://api.civicgate.org/graphql \
-H 'content-type: application/json' \
-H 'X-API-Key: cg_live_β¦' \
-d '{"query":"{ feed(limit: 5) { events { title url occurredAt } } }"}' Optionally restrict a key to specific origins when you create it, so a copied key cannot be used from someone else's page. Full details: API keys.
The SDK
@cg/sdk is zero-dependency and wraps the same API. It also provides
createFeedStore(), which paints synchronously from cache and refreshes in the
background, so a widget never shows a blank box while it loads.
import { createClient } from "@cg/sdk";
const cg = createClient({ apiKey: "cg_live_β¦" });
const page = await cg.feed({ limit: 20 }); Embeddable widgets, with every option documented: widgets.
Reference
- Public API β endpoint, schema, and what the data covers.
- API keys β creating, scoping and revoking.
- Widgets β every embeddable widget and its parameters.
The API is public-domain civic data and stays free to read. If you build something with it, we would like to hear about it.