Business formation platforms, small business banking apps, and POS onboarding flows all face the same problem after a merchant signs up: the business now legally exists, but the owner does not know which licenses and permits they actually need to operate. Leaving that question unanswered is a customer experience failure - and a liability.
This guide is written for developers building on top of a compliance API. It covers the request and response structure, code samples in Python and JavaScript, UI patterns for presenting requirements to users, caching strategy, and how to handle edge cases like conditional requirements. The patterns here apply whether you are building a post-formation checklist, an onboarding compliance step in a banking app, or a license verification layer in a POS merchant setup flow.
Use Cases: Where Compliance Data Fits
Post-Formation Compliance Checklist (Formation Platforms)
The LegalZoom / Stripe Atlas pattern: once an LLC or corporation is formed, the user lands on a "What's next?" screen. This is the perfect moment to show them a structured checklist of licenses and permits they need - while they are already in compliance mode and their attention is focused on setup. Embedding this step increases perceived value significantly and reduces post-formation churn because users feel guided rather than dropped.
Banking App Onboarding (Mercury, Relay Pattern)
Small business banking apps know the business type and state at account opening. Surfacing a "You may need these licenses to operate legally" panel in the onboarding flow is low-cost to build and high-value to customers. It positions the bank as a compliance partner, not just a place to park money. Banks serving high-risk business types (food service, contractors, healthcare) can also use license status as part of their risk assessment.
POS and Payments Merchant Onboarding (Toast, Square Pattern)
POS providers onboard thousands of new merchants per week. Many merchants - especially first-time restaurant or retail owners - are not in full compliance at launch. Surfacing compliance requirements at the time of hardware setup gives the platform an opportunity to add value, and it documents that they flagged the issue if a compliance problem arises later.
The API Request Structure
A well-designed compliance API centers on a POST /requirements endpoint. The request takes three key inputs:
- business_type - standardized using NAICS (North American Industry Classification System) codes. NAICS codes cover every business type with a 6-digit taxonomy. Using NAICS lets the API map business type to the relevant licensing agencies precisely, rather than relying on free-text descriptions that create ambiguity.
- city - the municipality where the business will physically operate
- state - 2-letter state code
Optional parameters include entity_type (sole_proprietor, llc, corporation, partnership) since some requirements differ by entity type, and activities for businesses with multiple activities that may trigger additional requirements (e.g., a restaurant that also sells packaged goods and has a bar).
{
"business_type": "722511",
"city": "Austin",
"state": "TX",
"entity_type": "llc",
"activities": ["food_service", "alcohol_service"]
}
The API Response Structure
The response returns a requirements array. Each requirement object in the array includes the fields necessary to build an actionable checklist without any additional lookup:
{
"business_type": "722511",
"location": {"city": "Austin", "state": "TX"},
"requirements": [
{
"id": "req_tx_sales_permit",
"name": "Texas Sales and Use Tax Permit",
"requirement_type": "registration",
"issuing_authority": "Texas Comptroller of Public Accounts",
"authority_url": "https://comptroller.texas.gov/taxes/sales/",
"estimated_fee_usd": 0,
"renewal_period_months": null,
"priority": "state",
"conditional": false,
"notes": "Required before making first taxable sale. Free to obtain."
},
{
"id": "req_austin_food_manager",
"name": "Austin Food Manager Certification",
"requirement_type": "permit",
"issuing_authority": "Austin Public Health",
"authority_url": "https://www.austintexas.gov/department/environmental-health-services-division",
"estimated_fee_usd": 116,
"renewal_period_months": 60,
"priority": "local",
"conditional": true,
"condition": "Required if you have 5+ employees handling food",
"notes": "At least one certified food manager must be on staff per shift."
}
],
"total_count": 7,
"estimated_total_fees_usd": 842
}
Handling the Response: Python Example
Here is a clean Python implementation using the requests library that fetches requirements and groups them by priority (federal, state, local) for display:
import requests
from collections import defaultdict
API_BASE = "https://api.bizcomplianceapi.com/v1"
API_KEY = "your_api_key_here"
def get_requirements(naics_code, city, state, entity_type="llc"):
response = requests.post(
f"{API_BASE}/requirements",
headers={
"Authorization": f"Bearer {API_KEY}",
"Content-Type": "application/json"
},
json={
"business_type": naics_code,
"city": city,
"state": state,
"entity_type": entity_type
},
timeout=10
)
response.raise_for_status()
return response.json()
def group_by_priority(requirements):
grouped = defaultdict(list)
priority_order = ["federal", "state", "county", "local"]
for req in requirements:
grouped[req["priority"]].append(req)
return {k: grouped[k] for k in priority_order if k in grouped}
def build_checklist(naics_code, city, state):
data = get_requirements(naics_code, city, state)
grouped = group_by_priority(data["requirements"])
print(f"Compliance requirements for {city}, {state}")
print(f"Estimated total fees: ${data['estimated_total_fees_usd']}\n")
for level, reqs in grouped.items():
print(f"--- {level.upper()} REQUIREMENTS ---")
for req in reqs:
fee = f"${req['estimated_fee_usd']}" if req['estimated_fee_usd'] else "Free"
renewal = f"Every {req['renewal_period_months']} months" if req['renewal_period_months'] else "One-time"
print(f" [ ] {req['name']}")
print(f" Authority: {req['issuing_authority']}")
print(f" Fee: {fee} | Renewal: {renewal}")
if req.get('conditional'):
print(f" Condition: {req['condition']}")
print()
# Example usage
build_checklist("722511", "Austin", "TX")
Handling the Response: JavaScript / Node Example
For a Node.js backend or a server-side rendered formation flow, the equivalent implementation looks like this:
const API_BASE = 'https://api.bizcomplianceapi.com/v1';
const API_KEY = process.env.BIZCOMPLIANCE_API_KEY;
async function getRequirements({ naicsCode, city, state, entityType = 'llc' }) {
const res = await fetch(`${API_BASE}/requirements`, {
method: 'POST',
headers: {
'Authorization': `Bearer ${API_KEY}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({
business_type: naicsCode,
city,
state,
entity_type: entityType
})
});
if (!res.ok) {
const err = await res.json().catch(() => ({}));
throw new Error(err.message || `API error (${res.status})`);
}
return res.json();
}
function sortRequirements(requirements) {
const priorityWeight = { federal: 0, state: 1, county: 2, local: 3 };
return [...requirements].sort(
(a, b) => (priorityWeight[a.priority] ?? 99) - (priorityWeight[b.priority] ?? 99)
);
}
// Usage in an Express route
app.post('/api/compliance-checklist', async (req, res) => {
try {
const { naicsCode, city, state, entityType } = req.body;
const data = await getRequirements({ naicsCode, city, state, entityType });
const sorted = sortRequirements(data.requirements);
res.json({
requirements: sorted,
estimatedFees: data.estimated_total_fees_usd,
count: data.total_count
});
} catch (err) {
res.status(500).json({ error: err.message });
}
});
Building the Checklist UI
The key design decisions for the compliance checklist UI:
Group by Requirement Type, Sort by Priority
Show federal requirements first (users must complete these before anything else), then state, then county, then city. Within each group, show licenses before permits before registrations - license implies ongoing authorization, registration is more of a one-time filing. This order reflects the dependency chain: you cannot apply for a state food service permit before you have your state business entity registered.
Distinguish Conditional Requirements
Conditional requirements (requirements that only apply under certain conditions) should be rendered differently from mandatory ones - a different border color or an "if applicable" badge works well. Show the condition text clearly. Do not hide them - a restaurant owner may not need an outdoor seating permit yet, but they should know it exists when they expand to a patio.
Surface Fees and Deadlines
The estimated fee and renewal period are the two most actionable fields in the response. Build a running total of estimated fees at the top of the checklist - "Total estimated first-year compliance cost: $842" - so users can budget. For items with renewal periods, surface a "Set reminder" option that drops a calendar event for the renewal date.
Link Directly to the Authority
Every requirement includes an authority_url. Link to it directly. Do not abstract it away. Users need to go to the actual agency website to apply - your platform is not the one doing the filing, you are orienting them. Direct links eliminate one step of friction.
Caching Strategy
Compliance requirements change, but not frequently. Fee amounts update when legislatures pass budget bills - typically annually. New requirements are added occasionally. Agency URLs change when websites get redesigned. A 7-day cache is appropriate for most use cases - fresh enough to catch major changes, old enough to avoid hammering the API on every page load.
Cache by the tuple of (naics_code, city, state, entity_type). In Redis:
const cacheKey = `compliance:${naicsCode}:${city}:${state}:${entityType}`;
const TTL_SECONDS = 7 * 24 * 60 * 60; // 7 days
const cached = await redis.get(cacheKey);
if (cached) return JSON.parse(cached);
const data = await getRequirements({ naicsCode, city, state, entityType });
await redis.setex(cacheKey, TTL_SECONDS, JSON.stringify(data));
return data;
Do not cache error responses. If the API returns a 5xx, do not store that in cache - let the next request try again.
Building the Renewal Monitoring Integration
The first-pass checklist integration is valuable, but the renewal monitoring integration is where ongoing user value is created. Once a user marks a requirement as "completed" with an application date, your platform can calculate the renewal date (application_date + renewal_period_months) and surface renewal reminders. This is a feature that drives retention - users who set up renewal reminders log in to act on them, creating sustained engagement. See our guide on automating permit renewal tracking for the full implementation pattern.
The Build vs. Buy Calculation
Many platform teams consider building this data in-house before deciding on an API approach. The rough economics:
- Initial data build: Researching requirements for 50 states x 100 major cities x 20 NAICS categories = 100,000 combinations. At 2 hours of research per combination (aggressive assumption), that is 200,000 hours of work. Even at $25/hour, that is $5 million. In practice, you would scope more narrowly, but a realistic full-coverage dataset costs $50K-$200K to build from scratch.
- Ongoing maintenance: Requirements change. Budget at least 0.5 FTE to track changes and update the database - roughly $40K-$70K/year loaded cost.
- Total first-year cost: $90K-$270K for meaningful coverage.
An API subscription at $499/month is $5,988/year. For a platform with under $270K in annual development budget for this feature area, the build-vs-buy calculation is straightforward. For large enterprises with very specific coverage needs, a hybrid approach makes sense: API for the long tail, curated in-house data for the top 10 markets.
For more context on the current landscape, see our state-by-state research guide: how to check business license requirements by state.
Ready to Integrate?
BizComplianceAPI is purpose-built for developers embedding compliance into formation, banking, and POS platforms. Structured JSON, NAICS-based lookups, federal-to-city coverage, and a caching-friendly response design.
Get Early Access