Developer documentation

Build with The Grant Map API

Evaluate a public sample, then integrate licensed, city-organized home-repair assistance records with bilingual fields, cited URLs, and explicit freshness and quarantine signals.

Start without a key

Five-minute quickstart

The public sample returns one city in the same program-record shape used by the licensed API. Start here to test parsing, field mapping, bilingual presentation, and freshness warnings without creating an account or sending personal information.

curl

Public, no credential
curl --fail-with-body \
  https://www.thegrantmap.com/api/sample \
  | python3 -m json.tool

Before evaluation or production use, also inspect the live dataset-health response, the paid-readiness gate, and the OpenAPI document. Those machine-readable resources are part of the buyer review path, not marketing claims.

Zero-key demo

Explore the live public sample

Load the public sample from this page, then filter it locally by project type or program name. The explorer never requests a key and never sends the filter text to the server. It shows at most six preview records; use the raw JSON to inspect the complete returned evaluation collection. The sample applies the same home-repair scope and known-problem quarantine as the licensed program routes, so it is not every stored row for the city.

Open raw JSON

The sample has not been loaded.

Access control

Authentication and evaluation scope

Licensed endpoints require a key in the X-Api-Key request header. API authentication retains a one-way hash. A separately encrypted delivery copy is kept only while paid delivery is pending or ambiguous, deleted after accepted delivery, and deleted after at most 30 days if ambiguity remains; support can revoke and rotate a key but cannot retrieve expired plaintext. Keep the raw value in a server-side secret manager or local environment variable; do not embed it in browser JavaScript.

Authenticated request

Header only
echo "Paste API key, then press Return (input hidden):"
read -s TGM_API_KEY

printf 'header = "X-Api-Key: %s"\n' "$TGM_API_KEY" | \
curl --fail-with-body --config - \
  --header "Accept: application/json" \
  "https://www.thegrantmap.com/api/programs/des-moines?type=homeowner&project=exterior"
unset TGM_API_KEY

The shell example feeds the secret header to curl over standard input, keeping the raw key out of the curl process arguments. Process-environment inspection can still expose local secrets; use your platform's secret manager in production.

Evaluation keys are deliberately bounded. A trial expires within 30 days and covers one to five approved live-city slugs. Its city index is filtered to that allowlist; requests outside it are denied. Trial change-feed calls must name an approved city, and the city-neutral national endpoint is reserved for paid tiers. These limits are evaluation controls, not a representation that the full commercial corpus has been delivered.

TierRequest limitsScope behavior
Trial60/minute; 2,000/dayOne to five approved live cities; no more than 30 days.
Nonprofit120/minute; 20,000/dayLicensed live-city scope only after manual eligibility and scope review under the applicable agreement.
Commercial600/minute; 200,000/dayLicensed live-city scope under the applicable agreement. Stored keys use the internal enterprise tier identifier.

The table describes runtime limits, not a self-service purchase menu. Only the commercial annual offer can use self-service checkout, and only while the public commercial-release gate is green. Nonprofit or academic access requires manual eligibility and scope review. Assisted-pilot availability is held pending reviewed scope and retention terms; neither path is offered through self-service checkout.

Limits resolve from the key's current tier at request time. Access may also stop when a key expires, is revoked, or cannot be validated. The service fails closed during an authentication outage rather than returning licensed data without validation.

HTTP JSON

Endpoint map

Public evaluation and transparency

GET/api/sample

One evaluation-city collection in the licensed program-record shape, after the current scope and quarantine rules. No key required.

GET/api/dataset/health

Current corpus coverage, licensed and quarantined counts, link-health categories, audit status, and plain-language methodology notes.

GET/api/data/readiness

The fail-closed paid-sale readiness decision and blocker codes. A red gate should stop procurement or delivery.

GET/api/dataset/schema

JSON Schema for one program record, including the nested freshness object.

GET/api/dataset/export-schema

JSON Schema for one candidate flat-export row. A schema is not an offer, delivery receipt, or source-rights approval for a file.

GET/openapi.json

OpenAPI 3.1 description of the public and licensed HTTP contract.

Licensed endpoints

GET/api/cities

Licensed live-city metadata. Trial keys see only their approved city allowlist.

GET/api/cities/counts

Licensed cities with safe actionable program counts and an aggregate total.

GET/api/programs/{city}

Home-repair records for one live city after adjacent-scope and known-problem exclusions, with optional filters and serve-time freshness metadata.

GET/api/programs/{city}/{programId}

One non-quarantined record. A missing or quarantined record returns 404.

GET/api/programs/national

Four maintained city-neutral federal records. Paid tiers only; it is not the broader replicated national-looking pool.

GET/api/changes?since=YYYY-MM-DD&city={city}&limit=200

Recent detected changes, newest first, with durable event IDs and an opaque nextCursor. Trial calls must include an approved city slug.

Server-side examples

curl, Python, and JavaScript

These examples use environment variables so credentials do not appear in source control, process arguments, or URLs. The repository also includes small dependency-free runnable clients under docs/examples/grantmap-api/.

Python 3

Standard library only
import json
import os
from urllib.request import Request, urlopen

key = os.environ["TGM_API_KEY"]
request = Request(
    "https://www.thegrantmap.com/api/programs/des-moines?project=exterior",
    headers={"Accept": "application/json", "X-Api-Key": key},
)

with urlopen(request, timeout=20) as response:
    payload = json.load(response)

print(payload["count"])
for program in payload["programs"][:3]:
    print(program["id"], program["freshness"]["confidence"])

JavaScript

Node 18+ server process
const key = process.env.TGM_API_KEY;
if (!key) throw new Error("TGM_API_KEY is required");

const response = await fetch(
  "https://www.thegrantmap.com/api/programs/des-moines?project=exterior",
  { headers: { Accept: "application/json", "X-Api-Key": key } },
);
const payload = await response.json();
if (!response.ok) {
  throw new Error(`${response.status}: ${payload.code ?? payload.error}`);
}

console.log(payload.count);
for (const program of payload.programs.slice(0, 3)) {
  console.log(program.id, program.freshness.confidence);
}

Collection behavior

Filters and pagination contract

Program-list filters combine with logical AND. Unknown values normally produce a valid empty collection rather than a validation error, so discover filter values from returned records and keep an empty result distinct from a failed request.

ParameterMatchesTypical value
typeeligibilityTypehomeowner
incomeIncome hierarchy; any is a stored sentinel, not proof that no income test applies.low
projectprojectTypes, including supported aliasesexterior
specialspecialPopulations; records with no stored population restriction can remain in the result.veterans
categoryExact stored categoryhome-repair
statusExact stored funding-status labelavailable

City program collections return the complete filtered array. The count is the post-filter home-repair result count. excludedBySafetyPolicy reports matching adjacent-scope or known-problem records withheld from that same filtered request, with exclusionPolicyVersion identifying the policy contract.

The change feed uses stable keyset pagination. limit accepts 1–1,000. Read hasMore; while it is true, send the returned opaque nextCursor with the same since and city values. Do not stop merely because changes is empty: a page can advance across raw events withheld from the licensed response and still return hasMore: true. Stop only when hasMore is false. The first page freezes snapshotMaxId, so later inserts, including backdated rows, cannot cross the traversal boundary. Start a new first-page request to discover events committed after that snapshot. Legacy change rows do not carry a home-repair scope field, so reconcile each event's program ID against the current scoped program endpoint before downstream use.

Program responses are serialized through the published JSON Schema allowlist. additionalProperties is false: internal review fields, UI helpers, and future corpus keys are not licensed response fields unless the published contract is deliberately revised.

Stable integrations

Versions, revisions, and deprecation

The response header X-API-Version identifies the semantic HTTP contract. The currently served contract is 1.4.0, and the unprefixed endpoints are the 1.x contract. New optional fields and endpoints can be added in a compatible minor release; a breaking response or behavior change requires a new major route such as /api/v2 rather than a silent mutation of the 1.x contract. Read the header or OpenAPI info.version at runtime instead of assuming this page will always carry the latest value.

X-Dataset-Revision identifies one effective served generation. Its deterministic digest binds the API contract/revision scheme, checked-in live-city corpus, current freshness evidence, durable founder corrections, enrichment implementation, and active licensed scope and exclusion policy. Successful collection, sample, health, and change-feed bodies also expose datasetRevision; a successful detail response carries it in the header only. Error responses omit it rather than labeling an unavailable dataset. Store it with an import or evaluation result so a defect report can name the exact generation it used.

Every response includes an X-Request-ID. A caller-supplied value is echoed only when it is 8–80 characters, starts with an ASCII letter or digit, and otherwise contains only ASCII letters, digits, ., _, :, or -. An absent or unsafe value is replaced with an opaque service-generated ID. Keep secrets and personal information out of it, and include that value—not an API key—in a support report.

No operation is currently deprecated. If a replacement is introduced, The Grant Map will publish it before retirement, provide at least 90 days' notice, and mark affected responses with the standard Deprecation, Sunset, and replacement Link headers. The OpenAPI document is the machine-readable source of truth for the current contract and deprecation state.

On the canonical public host, /api/sample, /api/dataset/schema, /api/dataset/export-schema, and /openapi.json provide an ETag and accept If-None-Match; a match returns 304 with no body. Dataset health and paid readiness are public but are not part of that ETag allowlist. Licensed responses deliberately remain private, no-store, vary on X-Api-Key, and omit ETags; do not weaken that boundary in a shared cache.

Evidence, not guarantees

Freshness and quarantine semantics

Each served program carries a nested freshness object. The signals describe what The Grant Map has checked; they do not replace confirmation with the program administrator.

linkStatus
live, dead, bot-blocked, or unchecked. A live HTTP response confirms only that a URL resolved—not that every stored fact is current.
lastChecked
The recorded date of the last applicable link-check attempt. It can be present when linkStatus is unchecked because a connection failure or other inconclusive attempt still has a date.
confidence
A bounded evidence label: source-verified, federal-canonical, model-identity-reviewed, link-verified, link-resolves-unverified, or unverified. Read the JSON Schema descriptions before mapping these labels to product UI.
sourceReviewed
Whether a dated source-review workflow ran. A model-assisted identity review is not the same as stored verbatim proof or amount verification.
sourceReviewStale
True when the last valid source review is at least 90 days old; stale reviews cannot retain a current top confidence tier.

Quarantine is fail-closed. Known-problem rows are withheld from licensed list and detail responses. A quarantined detail request returns 404 so callers cannot use the endpoint as an oracle for suppressed records. Monitor excludedBySafetyPolicy, exclusionPolicyVersion, and the public health response instead of hard-coding today's exclusion count.

For any application, budgeting, or consumer-facing decision, surface the record's cited URL and a verification warning. Funding status, deadlines, dollar amounts, geography, and administrator rules can change between sweeps.

Fail clearly

Errors, rate limits, and retries

StatusMeaningClient action
400Invalid parameter, or a trial change-feed call omitted its required city.Fix the request; do not retry unchanged.
401Missing or invalid key. Stable codes include API_KEY_REQUIRED and INVALID_API_KEY.Load the correct server-side secret.
403Revoked, expired, malformed-trial, or out-of-scope key.Stop and resolve the entitlement or trial scope.
404City/program not found in licensed live scope, including a safety-quarantined detail record.Do not infer why it is absent; reconcile against health and policy signals.
429The key's current minute or daily limit was exceeded.Honor Retry-After when present and apply bounded exponential backoff with jitter.
503Authentication, licensed scope, or change-feed storage is unavailable.Treat it as retryable; never reinterpret it as an empty dataset.

Typical JSON error

Exact message may evolve; branch on status and code
{
  "error": "API key required. Request access at https://www.thegrantmap.com/data",
  "code": "API_KEY_REQUIRED"
}

Rate-limited routes publish X-RateLimit-Limit, X-RateLimit-Remaining, and X-RateLimit-Reset for the active window. Retry-After accompanies retryable 429 or 503 responses when the server can state a delay. Licensed city/program responses are marked private and non-cacheable and vary on X-Api-Key. Clients should not place them in a shared public cache. Public transparency endpoints may use separate cache policies.

Safe product use

What the API is—and is not

  • It is a discovery and research dataset. It organizes published program records and evidence signals for downstream review.
  • It is not an eligibility engine. A filter match does not establish eligibility, available funding, approval, award size, or coordination with another program.
  • It does not contain applicants or leads. Licensed program endpoints are not a source of homeowner, contractor-contact, application, banking, identity-document, or other user PII.
  • It is not the program administrator's system of record. Consumers and staff must confirm current terms with the cited administrator before acting.
  • It is not an unrestricted raw-data resale license. Rights, permitted uses, attribution, redistribution, and delivery scope come from the applicable written agreement.

Bulk file delivery is not a public download endpoint. When separately offered, its contents, rights review, update cadence, and delivery method must be stated in the applicable evaluation or commercial agreement.

Data stewardship

Corrections and support workflow

Report a suspected record problem through Contact and Support or email support@thegrantmap.com. Include the city slug, program ID, field in question, authoritative source URL, what the source says, and the date you reviewed it.

Do not send Social Security numbers, bank records, tax returns, identity documents, medical information, or applicant files with a correction report. The team can request a safer minimum if more context is needed.

A submitted correction is evidence to review, not an automatic edit. Source changes may appear later in the change feed after verification and durable publication.

Buyer engineering review

Evaluation checklist

  1. Parse the public sample and validate representative records against the published JSON Schema.
  2. Read the live health and readiness payloads; record the licensed, quarantined, link-health, and strict-audit state you evaluated.
  3. Choose one to five representative live cities for a bounded trial, including the markets and program types that matter to your use case.
  4. Test every filter you plan to expose, including empty results, unknown stored values, and bilingual fallbacks.
  5. Exercise 401, 403, 404, 429, and 503 behavior without logging the raw key or treating failure as an empty success.
  6. Reconcile list counts with excludedBySafetyPolicy and monitor exclusionPolicyVersion for policy changes.
  7. Show cited URLs and verification context in the downstream user experience; do not relabel discovery matches as eligibility decisions.
  8. Review security, retention, caching, redistribution, attribution, update cadence, support, and deletion obligations before production use.

Ready for a scoped evaluation?

Send the use case, intended users, target cities, required fields, integration timeline, and whether you need API access or a separately reviewed file delivery. We will not treat an inquiry as an approved license or promise that a specific scope is ready.