Reference

RuleGrid API

A read-only JSON API over the short-term-rental regulation dataset. Every response carries its trust block — a last-verified date, source links, and a disclaimer. There is zero LLM on the serving path; responses are deterministic reads from the dataset.

Base URL

All endpoints are versioned under /v1, served from the apex domain.

https://rulegrid.dev/v1

Responses are application/json; charset=utf-8. All read endpoints allow cross-origin GET (Access-Control-Allow-Origin: *), so you can call them from a browser.

Authentication

Every endpoint except /v1/health requires an API key. Pass it as a Bearer token or the x-api-key header — either works.

Bearer

curl https://rulegrid.dev/v1/jurisdictions?state=FL \
  -H "Authorization: Bearer rgk_live_0123456789abcdef0123456789abcdef"

x-api-key

curl https://rulegrid.dev/v1/jurisdictions?state=FL \
  -H "x-api-key: rgk_live_0123456789abcdef0123456789abcdef"

Keys look like rgk_live_<32 hex> (or rgk_test_… in test mode). Your key is shown exactly once when it is issued — store it securely; we only keep a hash and can't recover it. No key returns 401 missing_key; an unknown, revoked, or inactive key returns 403 invalid_key.

Rate limits & quotas

Quotas are monthly, enforced in real time against usage. Metered endpoints count 1 call each; /v1/health is unmetered. Exceeding a monthly quota returns 429 quota_exceeded with a Retry-After header pointing to the month rollover.

PlanIncluded calls / monthOverageChange feed
Free50Hard stop (429)
Host250Hard stop (429)
Pro2,500Hard stop (429)Read
API15,000$0.02 / call, meteredRead + webhook

Only the API plan bills overage (via Stripe metering); every other plan hard-stops at its quota. Overage metering is asynchronous and never blocks or delays a response.


Endpoints

GET /v1/lookup

Resolve the applicable jurisdiction stack for a location and return each layer's record plus a composed roll-up. Provide exactly one of:

ParameterDescription
addressFree-form US address or city, e.g. 123 Ocean Dr, Miami Beach, FL. Geocoded via the US Census geocoder.
lat & lngDecimal coordinates, e.g. lat=25.79&lng=-80.13.
jurisdictionA known jurisdiction id, e.g. us-fl-miami-beach — skips geocoding.

Request

curl "https://rulegrid.dev/v1/lookup?address=123+Ocean+Dr,+Miami+Beach,+FL" \
  -H "x-api-key: rgk_live_..."

Response · 200

{
  "query": { "address": "123 Ocean Dr, Miami Beach, FL" },
  "resolved": {
    "state": "FL",
    "county": "Miami-Dade",
    "place": "Miami Beach",
    "fips": { "state": "12", "county": "086", "place": "45025" },
    "unincorporated": false
  },
  "stack": [
    { "jurisdiction_id": "us-fl", "level": "state", "name": "Florida",
      "str_status": "allowed_with_registration", "verification": { "last_verified": "2026-06-28", "status": "verified", "confidence": "high" } },
    { "jurisdiction_id": "us-fl-miami-dade", "level": "county", "name": "Miami-Dade County",
      "str_status": "allowed_with_registration", "verification": { "last_verified": "2026-06-25", "status": "verified", "confidence": "high" } },
    { "jurisdiction_id": "us-fl-miami-beach", "level": "city", "name": "Miami Beach",
      "str_status": "restricted", "verification": { "last_verified": "2026-06-12", "status": "verified", "confidence": "high" } }
  ],
  "composed": {
    "str_status": "restricted",
    "requirements": [
      { "layer": "state", "kind": "license", "name": "DBPR Vacation Rental License", "required": true },
      { "layer": "county", "kind": "tax_registration", "name": "Tourist Development Tax account", "required": true },
      { "layer": "city", "kind": "registration", "name": "Miami Beach STR registration", "required": true }
    ],
    "taxes": [
      { "layer": "state", "name": "State Sales & Use Tax", "rate_pct": 6.0 },
      { "layer": "county", "name": "Tourist Development Tax", "rate_pct": 6.0, "platforms_remit": { "airbnb": false, "vrbo": false } }
    ]
  },
  "disclaimer": "Informational summary of public sources; not legal advice. Verify against the linked official sources."
}

Each record in stack is the full jurisdiction record (trimmed above for space). resolved.unincorporated is true when the address falls outside any incorporated place — in which case the stack is state → county only.

GET /v1/jurisdictions

List jurisdiction summaries. Filter with query params.

ParameterDescription
stateTwo-letter state, e.g. FL.
levelOne of state, county, city.

Request

curl "https://rulegrid.dev/v1/jurisdictions?state=FL&level=city" \
  -H "x-api-key: rgk_live_..."

Response · 200

{
  "count": 18,
  "jurisdictions": [
    { "id": "us-fl-miami-beach", "name": "Miami Beach", "level": "city",
      "str_status": "restricted", "last_verified": "2026-06-12" },
    { "id": "us-fl-kissimmee", "name": "Kissimmee", "level": "city",
      "str_status": "allowed_with_registration", "last_verified": "2026-06-20" }
  ]
}

GET /v1/jurisdictions/:id

Fetch one full jurisdiction record by id — the complete normalized record, including requirements, restrictions, taxes, enforcement, sources, and the verification block.

Request

curl "https://rulegrid.dev/v1/jurisdictions/us-fl-miami-beach" \
  -H "x-api-key: rgk_live_..."

Response · 200

{
  "schema_version": "1.0",
  "jurisdiction_id": "us-fl-miami-beach",
  "level": "city",
  "parent_id": "us-fl-miami-dade",
  "name": "Miami Beach",
  "state": "FL",
  "str_status": "restricted",
  "str_status_summary": "Short-term rentals are prohibited in most residential zones; permitted only in specified districts with city registration, resort tax registration, and a state license.",
  "requirements": [
    { "kind": "registration", "layer": "city", "name": "Miami Beach STR Registration",
      "required": true, "fee": { "amount": 571, "period": "annual", "notes": null },
      "renewal": "annual", "url": "https://www.miamibeachfl.gov/...",
      "evidence": "All short-term rental units must register annually with the City." }
  ],
  "restrictions": {
    "primary_residence_required": false, "min_stay_nights": null,
    "max_nights_per_year": null, "zoning_restricted": true,
    "zoning_notes": "Permitted only in listed districts; single-family zones excluded.",
    "cap_on_licenses": false, "notes": [], "evidence": "..."
  },
  "taxes": [
    { "name": "Tourist Development Tax", "layer": "county", "rate_pct": 6.0,
      "administered_by": "county", "platforms_remit": { "airbnb": false, "vrbo": false },
      "registration_url": "https://...", "notes": "Self-administered county." }
  ],
  "enforcement": {
    "fines_summary": "First violation $20,000; escalating to $100,000 for repeats.",
    "active_enforcement": "high", "notes": "Ban ruled illegal; appeal pending."
  },
  "sources": [
    { "source_id": "mb-str-page", "kind": "program_page",
      "title": "City of Miami Beach — Short-Term Rentals",
      "url": "https://www.miamibeachfl.gov/...", "monitored": true,
      "last_checked": "2026-07-01", "last_content_change": "2026-03-14" }
  ],
  "verification": {
    "last_verified": "2026-06-12", "last_changed": "2026-03-14",
    "status": "verified", "confidence": "high"
  },
  "disclaimer": "Informational summary of public sources; not legal advice. Verify against the linked official sources."
}

A missing id returns 404 not_found using the standard error envelope.

GET /v1/changes

The append-only change log — every field that moved between pipeline runs, newest first.

ParameterDescription
sinceISO-8601 timestamp or YYYY-MM-DD; returns changes detected at or after this time.
stateTwo-letter state filter, e.g. FL.
jurisdictionRestrict to one jurisdiction id.
limitMax rows to return (newest first).

Request

curl "https://rulegrid.dev/v1/changes?since=2026-06-01&state=FL&limit=50" \
  -H "x-api-key: rgk_live_..."

Response · 200

{
  "count": 1,
  "changes": [
    {
      "change_id": 4182,
      "jurisdiction_id": "us-fl-miami-dade",
      "detected_at": "2026-06-14T07:12:00Z",
      "field_path": "taxes[county:Tourist Development Tax].rate_pct",
      "old_value": "5.0",
      "new_value": "6.0",
      "source_url": "https://www.miamidade.gov/...",
      "summary": "Miami-Dade Tourist Development Tax increased from 5% to 6%.",
      "schema_version": "1.0"
    }
  ]
}

GET /v1/health public · unmetered · no key

Dataset health. Requires no key and does not count against any quota — safe for uptime checks.

Request

curl "https://rulegrid.dev/v1/health"

Response · 200

{
  "status": "ok",
  "jurisdictions": 31,
  "sources_monitored": 85,
  "oldest_last_verified": "2026-05-30",
  "quarantined": 0,
  "schema_version": "1.0"
}

Errors

Errors use a consistent envelope and the matching HTTP status. Never parse the message string — branch on error.code.

{
  "error": {
    "code": "invalid_key",
    "message": "The provided API key is missing, unknown, or revoked.",
    "docs": "https://rulegrid.dev/docs#errors"
  }
}
HTTPcodeMeaning
400bad_requestMissing or malformed parameters (e.g. no address/lat+lng/jurisdiction on lookup).
401missing_keyNo API key supplied. Provide one via Authorization: Bearer <key> or the x-api-key header.
403invalid_keyAPI key unknown, revoked, or not active.
404not_foundNo jurisdiction matches the requested id (or method not allowed).
422geocode_failedThe address could not be resolved to a US jurisdiction.
429quota_exceededMonthly quota reached (non-API plans). See the Retry-After header for the reset time.
500internal_errorUnexpected server error. Safe to retry with backoff.

Accuracy & terms

RuleGrid is an informational summary of publicly available sources — not legal advice, and no professional-advice relationship is created by using it. Every record links the official sources it was compiled from and carries a last-verified date; always verify against those sources before acting. Data is provided without warranty; where a record cannot be validated it is served with a stale or needs_review status rather than silently changed. Do not treat the API as a system of record for legal compliance. Full API Terms of Service govern use and cap liability at fees paid.