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.
On this page
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.
/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
| Field | Meaning |
|---|---|
| coverage.status | Primary developer-facing coverage signal for the returned result set. |
| coverage.reason | Developer-facing explanation of why a result is complete, partial, sparse, empty, or review-oriented. |
| confidence_score | Secondary numeric quality signal from 0 to 1. Use with coverage and source metadata, not as legal certainty. |
| resolved_naics | NAICS code resolved from the submitted business_type or naics_code. |
| geocoding_method | How the submitted address was resolved to a supported jurisdiction. |
| last_verified | Oldest verification timestamp across returned requirements, or null when unavailable. |
Requirement Fields
| Field | Meaning |
|---|---|
| requirements[].authority | Issuing agency or public authority associated with the returned requirement. |
| requirements[].applicability | Whether the requirement is core, baseline, or conditional for the request. |
| requirements[].source_url | Official source reference when available in the public response. |
| requirements[].confidence_score | Per-requirement secondary numeric quality signal from 0 to 1. |
| requirements[].application_url | Public application or agency page when available. |
| requirements[].last_verified | Verification timestamp for the requirement row, or null when unavailable. |
/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"
}/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
| HTTP | error_code | Meaning | What to do |
|---|---|---|---|
| 400 | invalid_request | The request body is malformed or missing required fields. | Send a valid JSON body with address and either business_type or naics_code. |
| 401 | invalid_api_key | The Authorization bearer token is missing, malformed, expired, or inactive. | Pass Authorization: Bearer pb_live_your_api_key_here with an active API key. |
| 422 | unsupported_business_type | The 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. |
| 422 | unsupported_jurisdiction | The address is outside the declared launch-city coverage. | Check GET /v1/jurisdictions before calling the requirements endpoint. |
| 422 | jurisdiction_resolution_failed | The address could not be resolved to a covered jurisdiction. | Confirm the address and ZIP code, or retry with allow_geocoding_fallback when appropriate. |
| 429 | usage_limit_exceeded | The monthly request limit for the API key has been reached. | Wait for the monthly reset or upgrade the plan in the dashboard. |
| 503 | geocoder_unavailable | Precise geocoding is required but temporarily unavailable. | Retry later, or allow fallback if ZIP-level resolution is acceptable for the workflow. |
| 404 | stripe_customer_not_found | No billing customer exists for the authenticated dashboard user. | Create a checkout session before opening the billing portal. |
| 500 | internal_error | An unexpected server error occurred. | Retry the request. If it repeats, contact support with the request details. |
| 503 | internal_error | Usage 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.
| Plan | Monthly Requests | Price |
|---|---|---|
| Free | 100 | $0 / mo |
| Builder | 2,000 | $49 / mo |
| Scale | 15,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_lookupZIP code mapped directly to a jurisdiction via a pre-computed ZCTA table.
point_in_polygonZIP code crosses jurisdiction boundaries. The full address was geocoded and a point-in-polygon query determined the correct jurisdiction.
zcta_lookup_fallbackPrecise 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.statusA compact status such as complete, partial, sparse, no_requirements_found, or review.
coverage.reasonA short explanation your UI can show when coverage is limited or conditional.
requirements[].authorityThe issuing agency or public authority associated with a returned requirement.
requirements[].applicabilityOptional context for whether a row is core, baseline, or conditional.
requirements[].source_urlOfficial source reference when available in the public response.
confidence_scoreSecondary 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