Meeting time precision
The additive meetingTimePrecision contract is pending coordinated producer and consumer release. Explicit date or unknown precision must not be displayed as a verified clock. date retains the civil date and means no clock was supplied; time means the source clock was verified; unknown means unverified. Missing or null precision is a legacy value. Do not infer a known clock from midnight, rewrite old evidence, or treat a sorting anchor as a meeting start.
Public research routes
Public coverage, agenda-hits, and source-status routes do not require a key. Coverage describes dated research evidence and gaps. Agenda hits can be filtered by an allowlisted jurisdictionSlug and topicSlug, and each result includes meetingAt and sourceUrl. The public coverage route has a default and maximum page size of 20. Source status reports live health for a featured set of DFW meeting sources. It does not cover every entry in the research catalog.
Authenticated civic data routes
Health keeps lifetime document fetch failures in pipeline.documentDeadLetters. The separate pipeline.actionableDocumentDeadLetters count covers failed documents for meetings today or later in America/Chicago. A current document fetch failure causes degraded status unless it is an oversized-document failure covered by another document from the same source, meeting time, and title. Coverage accepts any readable or OCR document with `document_type` other than `minutes`: agendas, packets, staff reports, attachments, and HTML documents. Unknown document errors remain uncovered and cause degraded status. Historical or covered oversized-document failures remain in the history but do not set status to degraded. Stale sources, current source errors, including unrecognized errors categorized as upstream_failure, and extraction or classification dead letters also set degraded status.
- POST /api/v1/data/snapshots creates a seven-day snapshot and needs civic:export.
- GET /api/v1/data/snapshots/{snapshotId}/{resource} reads one snapshot resource and needs civic:export.
- GET /api/v1/data/changes reads ordered changes and needs civic:read.
- POST /api/v1/data/batch reads up to 100 record IDs across bounded requests and needs civic:read.
- GET /api/v1/data/health reports feed health and needs civic:read.
curl --fail --silent --show-error \
--header "Authorization: Bearer $AICD_API_KEY" \
https://aicdapi.com/api/v1/data/healthAuthenticated pagination and limits
- Authenticated civic data has a default page size of 100 and a service ceiling of 500. Your account can have a lower limit.
- Use the opaque nextCursor value exactly as returned. Do not edit or decode it to make business decisions.
- A cursor can expire after the replay window. A snapshot can also expire.
- If a response exceeds your account byte limit, request a smaller page.
Time, source evidence, and missing records
- Machine timestamps use ISO 8601. UTC timestamps end in Z. A source or meeting timezone is kept when known.
- A source link is evidence, not a guarantee that the publisher will keep the page unchanged.
- No result does not prove that no government action occurred. Check coverage, spelling, time range, and source publication delay. If the publisher is in the featured meeting-source list, also check its source status.
- Keep AICD API metadata and the government source URL when you store or publish a result.
- The public catalog's M, D, P, R, U, and N codes describe one named subtype only. They do not promise full-family coverage.
Feed subscriptions (polling)
A feed subscription is a named pointer to the change feed for one resource, or for every resource, and you read it by polling. GET /api/v1/data/subscriptions lists the active feeds of the account and the key that asks in data.items, with the account-wide activeCount and the account's current maxActiveSubscriptions. POST /api/v1/data/subscriptions creates one, DELETE /api/v1/data/subscriptions/{subscriptionId} revokes one, and GET /api/v1/data/subscriptions/{subscriptionId}/changes polls one. Creation takes a name of up to 80 characters and an optional resource, and the answer carries the feed id, name, resource, key id, start cursor, and creation time.
A feed is bound to the account and to the key that created it, and that binding never changes. Many feeds can share one key. The resource is fixed at creation, and a poll accepts only cursor and limit, so the fixed resource query cannot be overridden.
A new name and resource returns 201. The same name and resource on the same account and key is idempotent and returns the existing feed with 200. The same name with a different resource is refused as a conflict.
Polling draws on the account's normal request, byte, rate, and burst limits and needs civic:read. A poll returns the same data.items and meta envelope as GET /api/v1/data/changes, with the same page-size and response bounds and the same retention window. A poll without a cursor starts at the feed's stored start cursor. A cursor you supply is a position rather than a permission, so after rotating a key you register the replacement key and resume the retained cursor while the change feed still retains it.
A feed is active while the feed is unrevoked and its bound key is unrevoked and unexpired. Revoking or expiring that key stops polls and frees the slot without deleting the feed, and revoking a feed never revokes the key or that key's other feeds.
Feed subscriptions need a current paid period on Pro or Ultra, they are counted per account across every key, and Builder carries none.
A Pro or Ultra account that drops to the other eligible plan keeps its existing feeds and their polls, and new creation is refused while the account is over the new plan's count. An account that drops to a plan without feed subscriptions, or that loses its current paid period, is refused on every feed-subscription operation while its stored feeds stay in place. No feed is deleted or rebound on its own.
- 403 feed_subscription_plan_required: the account's current plan carries no feed subscriptions, such as Builder or another billing channel.
- 403 billing_entitlement_required: the account's paid period is not current. The shared paid admission refuses the request before the feed-subscription gate reads the plan.
- 409 feed_subscription_limit_reached: the account holds its plan's number of active feed subscriptions.
- 409 feed_subscription_conflict: the name already exists for this key with a different resource.
- 404 not_found: the feed belongs to another account or key, or the feed is revoked. A request with an invalid, expired, or revoked key stays 401.
- 410: the cursor is older than the change-feed retention window. 429 with Retry-After applies at a rate limit, and 503 feed_unavailable applies while the feed state cannot be read.
curl --fail --silent --show-error \
--header "Authorization: Bearer $AICD_API_KEY" \
--header "Content-Type: application/json" \
--data '{"name":"commission-meetings","resource":"meetings"}' \
https://aicdapi.com/api/v1/data/subscriptionscurl --fail --silent --show-error \
--header "Authorization: Bearer $AICD_API_KEY" \
https://aicdapi.com/api/v1/data/subscriptions/{subscriptionId}/changesResponse shape
Successful agent-facing HTTP responses use data and may include meta. Errors use error.code and error.message. Authenticated responses are not stored in shared caches.
{
"error": {
"code": "invalid_parameter",
"message": "Invalid civic data request"
}
}