API Reference

The PermitBase API takes a business address and business type and returns source-backed license, permit, registration, inspection, agency, coverage, and application context as structured JSON for supported launch-city workflows.

Base URL: https://www.permitbase.dev

OpenAPI: GET /v1/openapi.json

Launch-city coverage: Denver, New York City, Austin, Miami, and San Francisco.

Authentication

Protected endpoints require a valid API key in the Authorization header using the Bearer scheme. Generate your key from the dashboard.

Authorization: Bearer pb_live_your_api_key_here

Discovery endpoints (/v1/jurisdictions and /v1/industries) are public and do not require authentication.

Base URL

https://www.permitbase.dev

Public docs use /v1. The underlying /api/v1 paths remain supported.

POST

/v1/requirements

Core endpoint. Returns source-backed permit, license, registration, inspection, agency, and coverage details for a supported address and business type or NAICS code. Requires authentication.

Request Body

{
  "address":                  string   // required - full U.S. address with ZIP code
  "business_type":            string   // optional when naics_code is present
  "naics_code":               string   // optional when business_type is present
  "entity_type":              string   // optional
  "allow_geocoding_fallback": boolean  // optional, defaults to false
}

The address must contain a valid 5-digit ZIP code. Provide at least one of business_type or naics_code.

Response

{
  "jurisdiction": {
    "id":        string,
    "name":      string,
    "state":     string,
    "fips_code": string | null
  },
  "requirements": [
    {
      "id":                         string,
      "type":                       string,
      "name":                       string,
      "authority":                  string,
      "fee_min":                    number,
      "fee_max":                    number,
      "renewal_period":             string,
      "requires_inspection":        boolean,
      "application_url":            string,
      "estimated_processing_days":  number,
      "source_url":                 string,
      "notes":                      string,
      "confidence_score":           number,
      "last_verified":              string | null,
      "needs_review":               boolean,
      "applicability":              "core" | "baseline" | "conditional",
      "applies_when":               string
    }
  ],
  "coverage": {
    "status":       "complete" | "partial" | "sparse" | "no_requirements_found" | "review",
    "reason":       string,
    "result_count": number,
    "reviewed":     boolean
  },
  "resolved_naics":       string | null,
  "total_estimated_cost": {
    "min": number,
    "max": number
  },
  "confidence_score":     number,
  "geocoding_method":     "zcta_lookup" | "point_in_polygon" | "zcta_lookup_fallback",
  "schema_version":       "permitbase-v1.0",
  "last_verified":        string | null,
  "disclaimer": "This data is informational only and does not constitute legal advice. Verify requirements with the relevant authority."
}

Response Fields

FieldMeaning
coverage.statusPrimary developer-facing coverage signal for the returned result set.
coverage.reasonDeveloper-facing explanation of why a result is complete, partial, sparse, empty, or review-oriented.
confidence_scoreSecondary numeric quality signal from 0 to 1. Use with coverage and source metadata, not as legal certainty.
resolved_naicsNAICS code resolved from the submitted business_type or naics_code.
geocoding_methodHow the submitted address was resolved to a supported jurisdiction.
last_verifiedOldest verification timestamp across returned requirements, or null when unavailable.

Requirement Fields

FieldMeaning
requirements[].authorityIssuing agency or public authority associated with the returned requirement.
requirements[].applicabilityWhether the requirement is core, baseline, or conditional for the request.
requirements[].source_urlOfficial source reference when available in the public response.
requirements[].confidence_scorePer-requirement secondary numeric quality signal from 0 to 1.
requirements[].application_urlPublic application or agency page when available.
requirements[].last_verifiedVerification timestamp for the requirement row, or null when unavailable.
GET

/v1/jurisdictions

Public endpoint. Returns supported jurisdictions with coverage status and requirement counts. Use this to display coverage or validate addresses before calling /v1/requirements.

Response

{
  "jurisdictions": [
    {
      "id":               string,
      "name":             string,
      "state":            string,
      "requirement_count": number
    }
  ],
  "total":          number,
  "schema_version": "permitbase-v1.0"
}
GET

/v1/industries

Public endpoint. Returns supported business type keywords and their NAICS codes. Use this to build autocomplete or validate business_type before calling /v1/requirements.

The business_type field in /v1/requirements is matched case-insensitively against the keyword field in this list.

Response

{
  "industries": [
    {
      "keyword":    string,
      "naics_code": string
    }
  ],
  "total":          number,
  "schema_version": "permitbase-v1.0"
}

Error Codes

HTTPerror_codeMeaningWhat to do
400invalid_requestThe request body is malformed or missing required fields.Send a valid JSON body with address and either business_type or naics_code.
401invalid_api_keyThe Authorization bearer token is missing, malformed, expired, or inactive.Pass Authorization: Bearer pb_live_your_api_key_here with an active API key.
422unsupported_business_typeThe plain-English business type could not be resolved to a supported NAICS code.Use a supported business keyword from GET /v1/industries or provide a supported naics_code.
422unsupported_jurisdictionThe address is outside the declared launch-city coverage.Check GET /v1/jurisdictions before calling the requirements endpoint.
422jurisdiction_resolution_failedThe address could not be resolved to a covered jurisdiction.Confirm the address and ZIP code, or retry with allow_geocoding_fallback when appropriate.
429usage_limit_exceededThe monthly request limit for the API key has been reached.Wait for the monthly reset or upgrade the plan in the dashboard.
503geocoder_unavailablePrecise geocoding is required but temporarily unavailable.Retry later, or allow fallback if ZIP-level resolution is acceptable for the workflow.
404stripe_customer_not_foundNo billing customer exists for the authenticated dashboard user.Create a checkout session before opening the billing portal.
500internal_errorAn unexpected server error occurred.Retry the request. If it repeats, contact support with the request details.
503internal_errorUsage enforcement is temporarily unavailable.Retry later; requests are not completed while usage enforcement is unavailable.
// Error response shape (all error codes)
{
  "error": {
    "code": string,
    "message": string,
    "details": object | null
  },
  "schema_version": "permitbase-v1.0"
}

Rate Limits

Limits are enforced per API key on a monthly rolling basis. Usage resets on the 1st of each month at midnight UTC. When the limit is reached, the API returns 429 Too Many Requests - there is no overage billing.

PlanMonthly RequestsPrice
Free100$0 / mo
Builder2,000$49 / mo
Scale15,000$199 / mo

Geocoding Transparency

Every successful requirements response includes a geocoding_method field that describes how the jurisdiction was resolved from the provided address.

zcta_lookup

ZIP code mapped directly to a jurisdiction via a pre-computed ZCTA table.

point_in_polygon

ZIP code crosses jurisdiction boundaries. The full address was geocoded and a point-in-polygon query determined the correct jurisdiction.

zcta_lookup_fallback

Precise geocoding was required but unavailable, and the request explicitly allowed fallback. Boundary results may be less precise.

Reliability Signals

Use coverage and source fields to decide how to present a result in your product. These fields are designed for beta integrations where coverage may vary by city and business category.

coverage.status

A compact status such as complete, partial, sparse, no_requirements_found, or review.

coverage.reason

A short explanation your UI can show when coverage is limited or conditional.

requirements[].authority

The issuing agency or public authority associated with a returned requirement.

requirements[].applicability

Optional context for whether a row is core, baseline, or conditional.

requirements[].source_url

Official source reference when available in the public response.

confidence_score

Secondary numeric quality signal from 0 to 1 that should be interpreted with coverage and source metadata.

PermitBase includes confidence_score as a secondary numeric quality signal from 0 to 1. Higher values indicate stronger source and verification confidence. Use coverage.status, coverage.reason, requirement authority, applicability, and source metadata as the primary reliability signals.

PermitBase provides data tools, not legal advice. Users should verify final obligations with the issuing agency or a qualified professional.

Code Examples

cURL

curl -X POST https://www.permitbase.dev/v1/requirements \
  -H "Authorization: Bearer pb_live_your_api_key_here" \
  -H "Content-Type: application/json" \
  -d "{"address": "1455 Market St, San Francisco, CA 94103", "business_type": "restaurant"}"

Python

import requests

response = requests.post(
    "https://www.permitbase.dev/v1/requirements",
    headers={
        "Authorization": "Bearer pb_live_your_api_key_here",
        "Content-Type": "application/json",
    },
    json={
        "address": "900 E 11th St, Austin, TX 78702",
        "business_type": "restaurant",
    },
)

data = response.json()
print(f"Found {len(data['requirements'])} requirements")
cost = data['total_estimated_cost']
print(f"Estimated cost: {cost['min']}-{cost['max']}")

Node.js

const response = await fetch("https://www.permitbase.dev/v1/requirements", {
  method: "POST",
  headers: {
    "Authorization": "Bearer pb_live_your_api_key_here",
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    address: "900 E 11th St, Austin, TX 78702",
    business_type: "restaurant",
  }),
});

const data = await response.json();
console.log("Found " + data.requirements.length + " requirements");

Discovery Endpoints (no auth)

# List all supported cities
curl https://www.permitbase.dev/v1/jurisdictions

# List all supported business types
curl https://www.permitbase.dev/v1/industries
Need help? Contact support or visit the dashboard.