Provider intelligence, as a REST API.
RESTful JSON API for programmatic access to dental provider data, territory triggers, market intelligence, license monitoring, and practice scoring. 273,623 providers across all 50 states, 41 enriched.
Authentication
Include your API key in the Authorization header of every request.
API keys are generated in Settings → API Keys in your dashboard. Keys start with ps_live_. The full key is shown only once at creation, so store it securely.
Dashboard users can also access the API via session cookies (automatic when logged in). API key authentication is required for programmatic access.
No API key? Two alternatives
- Free evaluation tier: 100 NPI lookups per source IP per day, no auth required. Use to evaluate data quality.
- Per-call agent payments (x402): Pay USDC per request on Base. Built for AI agents that don't hold API keys or sign contracts.
Rate Limits
Rate limits are applied per API key using a sliding window algorithm. Exceeding the limit returns HTTP 429 with rate limit headers.
API Starter
60 req/min
$99/mo
API Pro
500 req/min
$299/mo
Team (Dashboard)
200 req/min
$499/mo
Enterprise
1,000 req/min
$1,999+/mo
Response Headers
API Scopes
API keys are issued with specific scopes that control which endpoints they can access. Attempting to access an endpoint without the required scope returns HTTP 403.
Search providers, retrieve profiles, alerts, market intelligence data. Included in all API keys.
Endpoints: All GET endpoints except exports
Bulk data export (CSV and JSON). Required for /api/providers/export and /api/scoring/export.
Endpoints: Export endpoints
Create/update saved searches, profile, preferences. Required for POST/PATCH/DELETE on user data.
Endpoints: Saved searches, profile, preferences, onboarding
Plan tier gating (separate from scopes)
A small number of endpoints require a specific plan tier beyond the scope check. These are flagged with a Team+ badge in the endpoint list below. Rep plan keys receive a 403 when hitting these endpoints with an explanatory error message. The Team+ tier covers Team, Enterprise, and any API plan that bundles Team-level data access.
403 Error Example
Pagination
Two pagination strategies are available:
Offset Pagination
Best for: browsing, UI tables, known page numbers.
Supports custom sort/order. Max per_page: 100 (dashboard), 1000 (API keys).
Cursor Pagination
Best for: bulk export, iterating all results, data pipelines.
Stable iteration: no skipped or duplicate rows. Forces order by NPI. Returns next_cursor in meta.
Cursor Iteration Pattern
Response Convention
Every read endpoint returns a JSON envelope with a stable shape. Success responses always include both data and error keys. Error responses use the same envelope with data: null. Never check !response.data alone — an empty list is a valid success.
Success
Error
Two endpoints add an extra wrapper field beyond the standard envelope: /api/demographics echoes the requested metric param, and /api/whitespace echoes the resolved state. Both still include data and error for consistent client handling.
The /api/preferences endpoints (account settings) and write endpoints that return {success: true} use simpler shapes appropriate to user-settings + mutation domains. Both are documented per-endpoint below.
Endpoints
Providers
/api/providersreadSearch and filter dental providers. Supports full-text search, specialty filters, geography, license status, DSO affiliation, Medicare billing history, practice size, and pagination (offset or cursor-based).
Returns { data, error, meta: { total, page, per_page, total_pages, next_cursor? } }
/api/providers/:npireadRetrieve a complete provider profile by NPI number. Includes co-located providers at the same address and any resolved practice-level entities.
Returns { data: { ...provider, co_located: [...], practice_entities: [...] }, error }
/api/providers/mapreadMap-rendering endpoint: returns geocoded provider points within a bounding box, with enough fields to render clusters and popups. Capped at 30,000 results per request.
Returns { data: [{ npi, last_name, first_name, specialty, latitude, longitude, license_status, license_expiration_date, has_disciplinary_action, is_federally_excluded, co_located_count, inferred_practice_name, graduation_year, date_of_birth, is_dso, dso_detection, dso_signals, is_sole_proprietor, organization_name, entity_type_code, ... }], error }
Export
/api/providers/exportexportBulk export provider data as CSV or JSON. Every filter parameter on /api/providers is also accepted here, including excluded, has_email, sedation_permit, retirement_risk, and include_suppressed. JSON format supports cursor-based pagination for iterating through large datasets.
Trial behavior: Trial users are capped at 25 rows per export. Paid tiers get up to 5,000 rows (10,000 for API keys).
CSV: file download. JSON: { data, error, meta: { total, count, per_page, next_cursor } }
/api/scoring/exportexportExport practice scores as CSV or JSON. Includes 6-factor scoring algorithm results. Every filter parameter on /api/providers is also accepted here.
Trial behavior: Trial users are capped at 25 rows per export. Paid tiers capped at 5,000 rows (10,000 for API keys). No cursor pagination on this endpoint — use /api/providers/export with sort=score if you need to iterate beyond the cap.
CSV: file download. JSON: { data: [{ ...provider, score, score_factors }], error, meta: { total, count } }. JSON has no next_cursor — pagination is offset-only here.
Scoring
/api/scoringreadPractice scoring. Returns individual providers ranked by a six-factor algorithm: solo/independent status (25%), retirement risk (20%), practice vintage (15%), practice size (15%), license status (15%), and discipline history (10%). Each response row also includes a confidence score and (when applicable) Medicare Part B aggregates. Reps use the score to prioritize call lists; DSO biz-dev teams use the same score for acquisition target ranking.
Returns { data: [{ ...provider, score, score_factors, confidence, cms_medicare? }], error, meta }
Market Intelligence
/api/whitespacereadMarket whitespace analysis. Returns geographic areas scored by population-to-provider ratio, HPSA designation, and demographic factors. Supports state-wide or geo-scoped (ZIP+radius, city) queries.
Returns { data: [{ zip, provider_count, population, whitespace_score, ... }], error }
/api/reimbursementreadMedicaid fee schedule rates by CDT code and geography.
/api/demographicsreadZIP-level demographics from Census ACS data. Supports state-wide or geo-scoped queries. Returns 400 if `metric` is not one of the listed enum values.
Returns { data: [{ zip, ...demographic_fields }], error, metric }. The extra `metric` field echoes the requested sort metric so callers running parallel queries with different metrics can disambiguate responses without tracking request URLs.
/api/npdbreadAggregate NPDB malpractice payment and adverse action trends by state and year. Anonymized, no individual provider data.
/api/reports/marketreadMarket summary report: total providers, specialty breakdown, license status distribution, top organizations, DSO share, practice-size distribution, retirement-age count, HPSA designations. Supports state-wide and geo-scoped (city, ZIP+radius) queries.
Trial behavior: Trial users are capped at 1 downloaded PDF brief per trial. Previews (page loads without for_brief=1) are unlimited.
/api/hpsa/mapreadHRSA Health Professional Shortage Area designations for dental care, deduplicated by county and enriched with county centroid coordinates for map overlays.
Returns { data: [{ hpsa_id, designation_type, hpsa_score, county_name, county_fips, latitude, longitude }], error }
/api/cms/mapreadTeam+Dental providers with CMS Medicare Part B billing history, geocoded for map overlay rendering. Returns lifetime billing aggregates per NPI (~3,000 providers nationally).
Plan requirement: Team tier or higher. Rep plan receives 403.
Returns { data: [{ npi, first_name, last_name, specialty, latitude, longitude, total_paid_lifetime, total_paid_latest_year, latest_year, years_active }], error }
Triggers
/api/triggers/summaryreadTrigger feed summary for the dashboard home card. Returns a count-by-trigger-type breakdown for the requested state and lookback window. Excludes enumeration_backfill rows (heuristic snapshots). Use this for territory-level rollups; use /api/alerts/events for the underlying license-event detail.
Returns { data: { total, days, state, byType: { dso_acquisition, new_associate, new_specialist, new_practice, practice_opened_new_location, provider_moved, provider_departed, practice_moved, practice_closed, license_renewal_window }, types: [string] } }. Field name is camelCase byType, not snake_case.
Alerts
/api/alerts/eventsreadLicense change events: expirations, status changes, new registrations, NPI deactivations, and disciplinary actions. Paginated.
Returns { data, error, meta: { total, page, per_page, total_pages } }
/api/alerts/grantsreadDental-relevant federal grant opportunities from Grants.gov and SAM.gov. Paginated.
/api/alerts/ratesreadTeam+Medicaid/Medicare reimbursement rate change events detected by the quarterly diff pipeline. Surfaces CPT/CDT codes whose rates moved significantly.
Plan requirement: Team tier or higher. Rep plan receives 403.
/api/alerts/npdbreadTeam+NPDB malpractice trend anomaly events by state. Surfaces states whose latest-year adverse action count exceeds the 3-year rolling average by >20%.
Plan requirement: Team tier or higher. Rep plan receives 403.
Account
/api/profilereadRetrieve the authenticated user's profile (role, territory, company).
/api/profilewriteUpdate profile fields: role, display_name, company_name, territory_states, territory_zips, territory_metros.
/api/saved-searchesreadList the authenticated user's saved searches.
/api/saved-searcheswriteCreate a saved search. Body: { name, filters, notify_email }. Plan limits apply (Rep: 10, Team: unlimited).
/api/saved-searches/:idwriteDelete a saved search by ID. Ownership is verified.
/api/saved-providersreadList the authenticated user's bookmarked providers (Save + Set alert from the triage slideout). Each row carries enrichment fields (specialty, license status, practice context) joined client-side from the providers table.
Returns { data: [{ id, npi, notify_on_change, created_at, ...enrichment }], error }
/api/saved-providerswriteBookmark a provider. Idempotent on (user_id, npi); calling twice toggles or refreshes the row instead of duplicating. Body: { npi, notify_on_change?: boolean }.
Plan requirement: Rep tier capped at 10 saved providers; Team and Enterprise uncapped.
/api/saved-providerswriteRemove a saved provider. Pass ?npi=XYZ as a query parameter.
/api/preferencesreadRead digest email frequency preference.
/api/preferenceswriteUpdate digest frequency. Body: { digest_frequency: 'off' | 'weekly' | 'daily' }.
Plan requirement: digest_frequency='daily' requires Team tier or higher. 'off' and 'weekly' are available on all plans.
System
/api/healthnoneHealth check. Returns database connectivity status and latency. No authentication required.
/api/statsnonePublic statistics: provider counts, expiring licenses, DSO-flagged, solo practitioners, retirement-age providers, disciplinary actions, HPSA designations, active grants, enriched state counts. Cached 5 minutes. No authentication required.
/api/data-statusreadData source freshness status for all pipeline sources.
Error Codes
All error responses follow a consistent format:
Code Examples
cURL
Python
JavaScript
Example Response
GET /api/providers?state=TX&sole_prop=true&per_page=1
Get API access
API keys are available on all paid plans. Subscribe to generate your first key.
Start Free Trial