Every API here is available over the APIs.io API and to AI agents over MCP.
MCP server
One button, every client — Claude, Cursor, VS Code and the rest.
https://apis.io/mcp
Tools for apis
7 MCP tools reach this
find_apisBrowse and filter every API in the catalog.
get_api_artifactsOne API's artifacts, grouped by type.
get_openapiThe primary OpenAPI for this API.
find_similar_apisAPIs that look like this one.
apis_io_searchSTART HERE — APIs, providers and tags for one query, each with its total.
resolveTurn a domain, URL or GitHub org into the provider it belongs to.
find_cohortsEvery scored population of providers in the catalog.
All 92 tools →
Call it yourself
curl for this page
This API
curl "https://apis.io/api/v1/apis/aicdapi-com:aicdapi-com-research-coverage-api"
All apis
curl "https://apis.io/api/v1/apis?limit=25"
Discovery needs no key. Ratings and market analysis are Pro.
Get an API key
Free tier, no form to fill in. Signing in shares your email address with us — we
store it to create your key and to recognise you if you sign in with another
provider. See our Privacy Policy and
Terms.
A second provider on the same verified email joins the account you already have.
openapi: 3.2.0
info:
title: AICD Research coverage API
version: 1.0.0
contact:
name: AICD API
url: https://aicdapi.com
email: hello@aicdapi.com
x-publisher:
'@id': https://cairn-labs.com/#organization
description: 'Structured Dallas-Fort Worth civic records. Public endpoints retain their documented cache and IP limits. Customer endpoints under /api/v1/data require a server bearer credential and always return Cache-Control: no-store. Customer grants, request/burst/export limits and response size caps are enforced. v1 allows additive response fields; breaking changes use a new version with a minimum 90-day migration notice during the first-customer pilot. Source check time and processing state describe observed coverage; no uptime or completeness guarantee is implied.'
servers:
- url: /
description: Same origin as this document
security: []
tags:
- name: Research coverage
description: Dated evidence about possible DFW civic-data sources and research gaps.
paths:
/api/v1/public/coverage:
get:
operationId: listDfwResearchCoverage
summary: List DFW civic-data research coverage
description: Returns dated source-discovery evidence for 238 active municipalities and 16 counties in the NCTCOG region. Inactive Mustang is omitted unless `includeInactive=true`. M/D/P/R/U/N statuses apply only to the named data subtype. They do not prove full-family coverage, current completeness, reuse permission or production ingestion. Cursors are bound to the catalog version and all filters.
tags:
- Research coverage
parameters:
- name: jurisdictionSlug
in: query
description: Exact jurisdiction slug, such as `dallas` or `dallas-county`.
schema:
type: string
minLength: 1
maxLength: 80
- name: county
in: query
description: One of the 16 lowercase DFW county slugs, such as `tarrant`.
schema:
$ref: '#/components/schemas/DfwCountySlug'
- name: family
in: query
description: Fully qualified family ID, such as `city.permits` or `county.property_tax`.
schema:
$ref: '#/components/schemas/ResearchCoverageFamilyId'
- name: includeInactive
in: query
description: Set to true to include inactive Mustang. The default is false.
schema:
type: boolean
default: false
- name: limit
in: query
description: Maximum jurisdictions per page. The small cap bounds source-evidence text.
schema:
type: integer
minimum: 1
maximum: 20
default: 20
- name: cursor
in: query
description: Opaque cursor from the prior page. Use it with the same filters and limit.
schema:
type: string
minLength: 1
maxLength: 4096
responses:
'200':
description: Dated research evidence and gaps. This is not an ingestion-health response.
headers:
Cache-Control:
$ref: '#/components/headers/PublicCacheControl'
content:
application/json:
schema:
$ref: '#/components/schemas/ResearchCoverageEnvelope'
examples:
cityPermitResearch:
value:
data:
- kind: city
researchId: city:4801240
jurisdictionSlug: addison
name: Addison
active: true
municipalityType: town
censusPlaceGeoid: '4801240'
counties:
- Dallas
regionCounties:
- Dallas
primaryRegionCounty: Dallas
censusStatus:
functionalStatus: A
placeClass: C1
vintage: '2026-01-01'
sourceUrls:
official: https://addisontexas.net/
openData: https://the-town-of-addison-gis-directory-addisontx.hub.arcgis.com/
websiteDiscovery: null
roster: https://geospatial.nctcog.org/map/rest/services/Boundaries/Boundaries/MapServer/6
censusStatus: https://tigerweb.geo.census.gov/tigerwebmain/Files/acs26/tigerweb_acs26_incplace_tx.html
countyMembership: https://geospatial.nctcog.org/map/rest/services/RDC/POP_ESTIMATES_REAL_RATES_BY_CITY/MapServer/1
rosterLimits:
boundaryYear: '2024'
boundarySource: City
directoryListed: true
websiteVerification: NCTCOG index link; live site ownership not confirmed.
verificationNotes: []
families:
- familyId: city.permits
status: P
evidenceScope: source_discovery_only
checkedDate: '2026-09-09'
sourceUrl: https://developmentservices.addisontx.gov/Data-Reports
method: Official source or report index identified
evidence: Official reports index names monthly commercial building permits.
constraints: Bulk access, refresh cadence and reuse terms still need a connector proof.
alternatives: []
meta:
catalogVersion: 2026-09-09.f006aca03096
researchDate: '2026-09-09'
ingestionVerified: false
statusDefinitions:
M: A public machine-readable query, sample, or count succeeded.
D: A public record or document was opened and read.
P: An official source or portal was found, but record extraction was not proved.
R: Access, terms, account, payment, or reuse restrictions need resolution.
U: No source was proved in this research pass; this does not mean the data is unavailable.
N: The family or named subtype is explicitly not applicable for this jurisdiction.
scope:
name: NCTCOG 16-county region
counties:
- Collin
- Dallas
- Denton
- Ellis
- Erath
- Hood
- Hunt
- Johnson
- Kaufman
- Navarro
- Palo Pinto
- Parker
- Rockwall
- Somervell
- Tarrant
- Wise
rosterCheckedDate: '2026-09-08'
censusStatusVintage: '2026-01-01'
method: Dated NCTCOG and Census roster cross-check.
caveat: This is a dated research baseline, not a live ingestion claim.
pagination:
limit: 20
returned: 1
nextCursor: null
'400':
$ref: '#/components/responses/InvalidParameter'
'429':
$ref: '#/components/responses/RateLimited'
'500':
$ref: '#/components/responses/InternalError'
security: []
x-auth-class: public
x-required-scope: []
x-release-status: available
x-markdown-url: /api-reference/listDfwResearchCoverage.md
x-curl-example: curl -X GET 'https://aicdapi.com/api/v1/public/coverage'
x-rate-limit: '120 requests per 60 seconds per client IP, counted separately for each public endpoint and keyed on the first X-Forwarded-For entry. The allowance is not shared between public endpoints: exhausting one leaves the others available. If the limiter''s own store fails, the request is allowed through rather than refused.'
x-caching: public, s-maxage=60, stale-while-revalidate=300 on success. Error responses carry no cache directive.
x-notes:
- Unknown query keys and repeated query keys are rejected rather than ignored.
x-pagination: Offset cursor pagination, 1 to 20 items per page with a default of 20. The cursor is invalidated by any filter change and by a new catalog version.
x-retry-after: Sent on 429 only, as whole seconds.
x-errors:
- status: 400
code: invalid_parameter
- status: 429
code: rate_limited
- status: 500
code: internal_error
components:
schemas:
ResearchCoverageFreshness:
type: object
additionalProperties: false
properties:
note:
type: string
latestOpenedSample:
type: string
format: date-time
latestFilingSample:
type: string
format: date-time
latestSampleRecord:
type: string
sourceHistoryClaim:
type: string
futureStatusAnomaly:
type: string
format: date-time
fullHistoryVerified:
type: boolean
ResearchCoverageEnvelope:
type: object
additionalProperties: false
required:
- data
- meta
properties:
data:
type: array
maxItems: 20
items:
oneOf:
- $ref: '#/components/schemas/CityResearchCoverage'
- $ref: '#/components/schemas/CountyResearchCoverage'
meta:
type: object
additionalProperties: false
required:
- catalogVersion
- researchDate
- ingestionVerified
- statusDefinitions
- scope
- pagination
properties:
catalogVersion:
type: string
pattern: ^\d{4}-\d{2}-\d{2}\.[a-f0-9]{12}$
researchDate:
type: string
format: date
ingestionVerified:
type: boolean
const: false
statusDefinitions:
type: object
additionalProperties: false
required:
- M
- D
- P
- R
- U
- N
properties:
M:
type: string
D:
type: string
P:
type: string
R:
type: string
U:
type: string
N:
type: string
scope:
type: object
additionalProperties: false
required:
- name
- counties
- rosterCheckedDate
- censusStatusVintage
- method
- caveat
properties:
name:
type: string
const: NCTCOG 16-county region
counties:
type: array
minItems: 16
maxItems: 16
items:
type: string
rosterCheckedDate:
type: string
format: date
censusStatusVintage:
type: string
format: date
method:
type: string
caveat:
type: string
pagination:
type: object
additionalProperties: false
required:
- limit
- returned
- nextCursor
properties:
limit:
type: integer
minimum: 1
maximum: 20
returned:
type: integer
minimum: 0
maximum: 20
nextCursor:
type:
- string
- 'null'
maxLength: 4096
CityResearchCoverage:
type: object
additionalProperties: false
required:
- kind
- researchId
- jurisdictionSlug
- name
- active
- municipalityType
- censusPlaceGeoid
- counties
- regionCounties
- primaryRegionCounty
- censusStatus
- sourceUrls
- rosterLimits
- families
properties:
kind:
type: string
const: city
researchId:
type: string
pattern: ^city:48[0-9]{5}$
jurisdictionSlug:
type: string
name:
type: string
active:
type: boolean
municipalityType:
type: string
enum:
- city
- town
- village
censusPlaceGeoid:
type: string
pattern: ^48[0-9]{5}$
counties:
type: array
minItems: 1
items:
type: string
regionCounties:
type: array
minItems: 1
items:
type: string
primaryRegionCounty:
type: string
censusStatus:
type: object
additionalProperties: false
required:
- functionalStatus
- placeClass
- vintage
properties:
functionalStatus:
type: string
placeClass:
type: string
vintage:
type: string
format: date
sourceUrls:
$ref: '#/components/schemas/ResearchSourceUrls'
rosterLimits:
type: object
additionalProperties: false
required:
- boundaryYear
- boundarySource
- directoryListed
- websiteVerification
- verificationNotes
properties:
boundaryYear:
type: string
boundarySource:
type: string
directoryListed:
type: boolean
websiteVerification:
type: string
verificationNotes:
type: array
items:
type: string
families:
type: array
minItems: 1
maxItems: 10
items:
$ref: '#/components/schemas/ResearchCoverageFamily'
ProblemEnvelope:
type: object
additionalProperties: false
required:
- error
properties:
error:
type: object
additionalProperties: false
required:
- code
- message
properties:
code:
$ref: '#/components/schemas/ErrorCode'
message:
type: string
details: {}
CountyResearchCoverage:
type: object
additionalProperties: false
required:
- kind
- researchId
- jurisdictionSlug
- countySlug
- name
- active
- sourceUrls
- families
properties:
kind:
type: string
const: county
researchId:
type: string
pattern: ^county:[a-z]+(?:-[a-z]+)*$
jurisdictionSlug:
type: string
countySlug:
$ref: '#/components/schemas/DfwCountySlug'
name:
type: string
active:
type: boolean
const: true
sourceUrls:
type: object
additionalProperties: false
required:
- roster
properties:
roster:
type: string
format: uri
families:
type: array
minItems: 1
maxItems: 11
items:
$ref: '#/components/schemas/ResearchCoverageFamily'
DfwCountySlug:
type: string
enum:
- collin
- dallas
- denton
- ellis
- erath
- hood
- hunt
- johnson
- kaufman
- navarro
- palo-pinto
- parker
- rockwall
- somervell
- tarrant
- wise
ResearchEvidenceScope:
type: string
enum:
- named_subtype_only
- source_discovery_only
- access_or_reuse_restriction
- unresolved
- not_applicable
ResearchCoverageAlternative:
type: object
additionalProperties: false
required:
- sourceUrl
properties:
title:
type: string
status:
$ref: '#/components/schemas/ResearchCoverageStatus'
sourceUrl:
type: string
format: uri
checkedDate:
type: string
format: date
method:
type: string
evidence:
type: string
constraints:
type: string
ResearchCoverageStatus:
type: string
enum:
- M
- D
- P
- R
- U
- N
description: Dated evidence level, not production ingestion status.
ResearchSourceUrls:
type: object
additionalProperties: false
required:
- official
- openData
- websiteDiscovery
- roster
- censusStatus
- countyMembership
properties:
official:
type:
- string
- 'null'
format: uri
openData:
type:
- string
- 'null'
format: uri
websiteDiscovery:
type:
- string
- 'null'
format: uri
roster:
type: string
format: uri
censusStatus:
type: string
format: uri
countyMembership:
type: string
format: uri
ErrorCode:
type: string
enum:
- invalid_parameter
- not_found
- unauthorized
- forbidden
- rate_limited
- internal_error
ResearchCoverageFamilyId:
type: string
enum:
- city.permits
- city.planning_gis
- city.laws_governance
- city.finance
- city.procurement
- city.service_code
- city.public_safety
- city.health_licenses
- city.infrastructure
- city.elections_notices
- county.property_tax
- county.land_records
- county.courts
- county.procurement
- county.finance
- county.elections
- county.development
- county.gis_infrastructure
- county.public_safety
- county.health_environment
- county.government_records
ResearchCoverageFamily:
type: object
additionalProperties: false
required:
- familyId
- status
- evidenceScope
- checkedDate
- sourceUrl
- method
- evidence
- constraints
- alternatives
properties:
familyId:
$ref: '#/components/schemas/ResearchCoverageFamilyId'
status:
$ref: '#/components/schemas/ResearchCoverageStatus'
evidenceScope:
$ref: '#/components/schemas/ResearchEvidenceScope'
checkedDate:
type: string
format: date
sourceUrl:
type:
- string
- 'null'
format: uri
method:
type: string
evidence:
type: string
constraints:
type: string
alternatives:
type: array
items:
$ref: '#/components/schemas/ResearchCoverageAlternative'
subtype:
type: string
subtypes:
type: array
items:
$ref: '#/components/schemas/ResearchCoverageSubtype'
coverageStart:
type: string
format: date
coverageEnd:
type: string
format: date
recordCount:
type: integer
minimum: 0
coverageQueryUrl:
type: string
format: uri
countQueryUrl:
type: string
format: uri
liveSourceFollowup:
type: string
freshness:
$ref: '#/components/schemas/ResearchCoverageFreshness'
custodian:
type: string
history:
type: string
cost:
type: string
updateFrequency:
type: string
familyConstraints:
type: string
ResearchCoverageSubtype:
type: object
additionalProperties: false
required:
- type
- status
- reason
properties:
type:
type: string
status:
$ref: '#/components/schemas/ResearchCoverageStatus'
reason:
type: string
url:
type: string
format: uri
responses:
RateLimited:
description: More than 120 requests were made by this client IP in 60 seconds.
headers:
Retry-After:
$ref: '#/components/headers/RetryAfter'
content:
application/json:
schema:
$ref: '#/components/schemas/ProblemEnvelope'
examples:
rateLimited:
value:
error:
code: rate_limited
message: Rate limit exceeded
InvalidParameter:
description: A path or query parameter is invalid or unsupported.
content:
application/json:
schema:
$ref: '#/components/schemas/ProblemEnvelope'
examples:
invalidParameter:
value:
error:
code: invalid_parameter
message: Invalid parameter
InternalError:
description: The request could not be completed because of an internal error.
content:
application/json:
schema:
$ref: '#/components/schemas/ProblemEnvelope'
examples:
internalError:
value:
error:
code: internal_error
message: Internal server error
headers:
PublicCacheControl:
description: Public CDN cache policy for successful public GET responses.
schema:
type: string
const: public, s-maxage=60, stale-while-revalidate=300
RetryAfter:
description: Seconds until this client IP may retry.
required: true
schema:
type: integer
minimum: 1
securitySchemes:
ApiCustomerBearer:
type: http
scheme: bearer
bearerFormat: Opaque service credential
description: Server-only customer key. civic:read covers changes, batch and health. civic:export covers snapshot creation and pages. Keep test and production keys separate.
x-aicd-reference:
human: /api-reference
markdown: /api-reference.md
operationMarkdownTemplate: /api-reference/{operationId}.md
openApi: /api/openapi.json
llmsIndex: /llms.txt
llmsFull: /llms-full.txt
x-aicd-mcp:
transport:
paths:
- /api/v1/mcp
methods:
- POST
protocol: 'Streamable HTTP carrying JSON-RPC 2.0 over HTTPS. The same path serves two protocol eras. A current-era request identifies itself in the body with a params._meta envelope claim (io.modelcontextprotocol/protocolVersion) together with protocolVersion and clientCapabilities, and is answered with one JSON response. A POST without that envelope is treated as a 2025-era request: it is answered per request with no session, and the reply is an SSE stream whose data frames carry the JSON-RPC result. JSON-RPC batch arrays are accepted only on the 2025-era path; a batch containing a current-era element is refused. The server never issues an Mcp-Session-Id, so nothing has to be kept between calls. Only tools are exposed: there are no resources and no prompts. Clients use initialize, tools/list and tools/call; server/discover reports the supported revision.'
headers:
- 'Authorization: Bearer <aicd_... API key> - required on every request. The key must grant mcp:read. A browser session is not accepted here.'
- 'Content-Type: application/json - required. Any other media type is refused with HTTP 415.'
- 'Accept: application/json and text/event-stream - required on a 2025-era request, and both media types must be listed; otherwise HTTP 406. Not checked on a 2026-07-28 request.'
- MCP-Protocol-Version - optional. On a 2025-era request it must be one of 2025-11-25, 2025-06-18, 2025-03-26, 2024-11-05 or 2024-10-07. On a request carrying the 2026-07-28 envelope claim it must be 2026-07-28 and must equal the body claim.
- Mcp-Method - required on a 2026-07-28 request and must agree with the JSON-RPC method in the body; a disagreement is refused.
- Mcp-Name - required on a 2026-07-28 tools/call and must equal params.name.
- Origin - optional, but when sent its hostname must match the hostname of the request URL, otherwise HTTP 403. A cross-site browser write is refused before the route runs.
- Content-Length - optional, but when sent it must parse as a non-negative integer and must not exceed 65,536; otherwise HTTP 400 or HTTP 413.
- Mcp-Session-Id - never issued and never required. The transport is stateless, so send none.
sizeLimit: 65,536 bytes for the whole request body, enforced both on the declared Content-Length and on the streamed body (HTTP 413 request_too_large). Civic tool results are separately capped at 16,384 bytes; a result that cannot fit is trimmed, and a result that still cannot fit fails with the tool error response_too_large rather than being returned partially. The REST data-report routes have their own 8 KiB body limit, which does not apply to a submission sent inside an MCP request.
auth:
class: bearer_api_key
scopes:
- mcp:read
note: 'Send an opaque AICD API key as `Authorization: Bearer aicd_...`. The same valid key serves the records API and MCP, and every stored key carries the MCP read scope, including tools/list. The two report tools ask for no report permission and never use the paid civic executor or take an extra payment, and the account still needs current paid access. The tool list itself is static: all five tools are always listed, and the difference between accounts is per call, not per list - a civic tool call is billed or refused according to the account''s billing channel (pay-as-you-go settlement, a live subscription period, or an operator channel that rejects payment metadata). Authentication happens before the MCP handler runs and can fail with HTTP 401 unauthorized, HTTP 403 forbidden or a billing code, or HTTP 503 authentication_unavailable; a handler failure returns HTTP 503 mcp_unavailable. Every route response carries Cache-Control: no-store. The separate route /api/mcp is a closed legacy stub that always returns HTTP 403 mcp_prelaunch with no credential check and is not an MCP surface; it is not an alias of /api/v1/mcp.'
rateLimit: 'On the metered path, a message from an account bound to a subscription that is not a `tools/call` consumes two per-account transport buckets: a minute bucket (60-second window, default 120 requests) and a burst bucket (1-second window, default 30 requests), at the account''s own allowances, which the account console and the public usage limits help show. On exhaustion the request fails with HTTP 429 rate_limited and a Retry-After header carrying whole seconds until the current fixed window ends, minimum 1. A civic `tools/call` is not counted here: the paid quota path charges it. A message from an account on another billing channel is not counted here either. An allowlisted metadata message (`initialize`, `tools/list`, `ping`, `server/discover`, `notifications/initialized`, `notifications/cancelled`) and one of the two report tools take neither bucket: they share one admission instead, a valid key with mcp:read and then a fail-closed per-client-IP report edge limit that refuses with 429 rate_limited and a Retry-After, or with 503 when it cannot be checked. For a report that admission is abuse control only, because the report still needs the account''s current paid access. Report counters surface as tool errors with retryAfterSeconds inside the payload instead of HTTP 429: 5 submissions per account-minute, 30 status reads per account-minute, 20 new reports per account-day, and 20 unresolved reports.'
releaseStatus: limited
tools:
# --- truncated at 32 KB (80 KB total) ---
# Full source: https://artifacts.apis.io/aicdapi-com/openapi/aicdapi-com-research-coverage-api-openapi.yml