Developer Reference

API Documentation

RESTful JSON API for programmatic access to dental provider data, market intelligence, license monitoring, and acquisition scoring. 272,000+ providers across all 50 states.

Authentication

Include your API key in the Authorization header of every request.

Authorization: Bearer ps_live_your_api_key_here

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 — 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.

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

Growth (Dashboard)

200 req/min

$499/mo

Response Headers

X-RateLimit-Limit: 100 # Max requests per window X-RateLimit-Remaining: 87 # Requests remaining X-RateLimit-Reset: 1712000000 # Window reset timestamp (ms)

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.

readread

Search providers, retrieve profiles, alerts, market intelligence data. Included in all API keys.

Endpoints: All GET endpoints except exports

exportexport

Bulk data export (CSV and JSON). Required for /api/providers/export and /api/scoring/export.

Endpoints: Export endpoints

writewrite

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 Growth+ badge in the endpoint list below. Pro plan keys receive a 403 when hitting these endpoints with an explanatory error message. The Growth+ tier covers Growth, Enterprise, and any API plan that bundles Growth-level data access.

403 Error Example

{ "data": null, "error": "Insufficient scope. Required: \"export\". Your key has: [\"read\"]." }

Endpoints

Providers

GET/api/providersread

Search 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).

ParameterTypeDescription
qstringFull-text search across name, NPI, city, ZIP, organization
statestringState abbreviation. Comma-separated for multi-state (e.g. TX,CA,FL)
citystringCity name (partial match)
zipstringZIP code prefix match
specialtystringDental specialty (e.g. general-dentistry, orthodontics)
sole_propbooleanFilter sole proprietors. true or false
independentbooleanFilter independent practices (no parent organization)
is_dsobooleanFilter DSO-affiliated providers. true or false
has_cmsbooleanFilter providers with CMS Medicare Part B billing history. Approximately 1.76% of dental providers nationally (mostly oral surgery and maxillofacial).
practicestringPartial match on inferred practice name (the co-location-derived practice attribution).
min_practice_sizeintegerMinimum number of co-located providers at the same practice address (e.g. 5 = group practices only).
license_statusstringLicense status (Active, Expired, Suspended, Cancelled, Deceased, Retired, Vol Surrender). Comma-separated for multiple.
exp_monthsintegerLicense expiring within the next N months (forward-looking).
expired_withinintegerLicense expired within the last N months (backward-looking; useful for recently-inactive providers).
disciplinebooleanHas disciplinary action on record
enriched_onlybooleanOnly providers with state board license enrichment
anesthesia_levelintegerMinimum anesthesia permit level (1-4)
entity_typestringNPI entity type: 1 (Individual) or 2 (Organization)
parent_orgstringParent organization name (partial match)
grad_beforeintegerGraduation year <= value (retirement age filter)
radius_zipstringCenter ZIP for radius search (use with radius_miles)
radius_milesstringRadius in miles from radius_zip
sortstringSort column (default: last_name). Options: last_name, city, state, specialty, license_expiration_date, graduation_year
orderstringSort direction: asc (default) or desc
pageintegerPage number (default: 1). Ignored when using cursor.
per_pageintegerResults per page. Dashboard: 1-100, API keys: 1-1000 (default: 25)
cursorstringNPI of last item for cursor-based pagination. Forces order by NPI ascending. Use for bulk data iteration — stable under concurrent writes.

Returns { data, error, meta: { total, page, per_page, total_pages, next_cursor? } }

GET/api/providers/:npiread

Retrieve a complete provider profile by NPI number. Includes co-located providers at the same address and any resolved practice-level entities.

ParameterTypeDescription
npi*string10-digit National Provider Identifier (path parameter)

Returns { data: { ...provider, co_located: [...], practice_entities: [...] }, error }

GET/api/providers/mapread

Map-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.

ParameterTypeDescription
bounds*stringBounding box as CSV: south,west,north,east (e.g. 25.5,-106.5,36.5,-93.5 for Texas)
statestringOptional state filter
specialtystringSpecialty filter
sole_propbooleanSole proprietors only
license_statusstringLicense status filter
enriched_onlybooleanOnly providers with state board enrichment
is_dsobooleanDSO-flagged filter
has_cmsbooleanMedicare billers only
disciplinebooleanHas disciplinary action
grad_beforeintegerRetirement-age filter
independentbooleanIndependent practices only

Returns { data: [{ npi, last_name, first_name, specialty, latitude, longitude, ... }], error }

Export

GET/api/providers/exportexport

Bulk export provider data as CSV or JSON. Supports all provider search filters. 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).

ParameterTypeDescription
formatstringResponse format: csv (default) or json
cursorstringNPI-based cursor for JSON pagination. Returns next_cursor in response.
per_pageintegerResults per page for JSON format (default: 1000, max: 10000 for API keys)
...All /api/providers filter parameters are supported

CSV: file download. JSON: { data, error, meta: { total, count, per_page, next_cursor } }

GET/api/scoring/exportexport

Export acquisition target scores as CSV or JSON. Includes 6-factor scoring algorithm results. Supports all scoring filters.

Trial behavior: Trial users are capped at 25 rows per export.

ParameterTypeDescription
formatstringResponse format: csv (default) or json
min_scoreintegerMinimum acquisition score (0-100)
...All /api/providers filter parameters are supported

CSV: file download. JSON: { data: [{ ...provider, score }], error, meta: { total, count } }

Scoring

GET/api/scoringread

Acquisition target scoring. Returns individual providers ranked by a six-factor algorithm: solo status (25%), retirement risk (20%), practice vintage (15%), practice size (15%), license health (15%), and clean record (10%). Each response row also includes a confidence score and (when applicable) Medicare Part B aggregates.

ParameterTypeDescription
min_scoreintegerMinimum acquisition score threshold (0-100)
sortstringSort field. Options: score (default), last_name, city, enumeration_date, license_expiration_date, co_located_count, inferred_practice_name, cms_paid (CMS lifetime paid).
orderstringSort direction: asc or desc (default: desc for score, asc for name)
...All /api/providers filter and pagination parameters

Returns { data: [{ ...provider, score, score_factors, confidence, cms_medicare? }], error, meta }

Market Intelligence

GET/api/whitespaceread

Market 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.

ParameterTypeDescription
statestringState abbreviation (default: TX)
zipstringCenter ZIP for radius search (use with radius)
radiusintegerRadius in miles from zip (max 200)
citystringCity centroid for a 25-mile radius search (fallback when zip/radius aren't provided)
limitintegerMax results (default: 500)
GET/api/reimbursementread

Medicaid fee schedule rates by CDT code and geography.

ParameterTypeDescription
statestringState abbreviation (default: TX)
codestringCDT code filter (e.g. D0120)
localitystringMedicare locality filter (e.g. "TEXAS HEALTH STEPS - DENTAL")
limitintegerMax results (default: 100)
GET/api/demographicsread

ZIP-level demographics from Census ACS data. Supports state-wide or geo-scoped queries.

ParameterTypeDescription
statestringState abbreviation
metricstringSort metric: total_population, median_household_income, median_age, pct_under_18, pct_65_plus
zipstringCenter ZIP for radius search (use with radius)
radiusintegerRadius in miles from zip
citystringCity centroid for a 25-mile radius search
limitintegerMax results (default: 500)
GET/api/npdbread

Aggregate NPDB malpractice payment and adverse action trends by state and year. Anonymized — no individual provider data.

ParameterTypeDescription
statestringState abbreviation filter
GET/api/reports/marketread

Market 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.

ParameterTypeDescription
statestringState abbreviation (default: TX)
citystringCity name for a 25-mile radius scope
zipstringCenter ZIP for radius search (use with radius)
radiusintegerRadius in miles from zip
GET/api/hpsa/mapread

HRSA Health Professional Shortage Area designations for dental care, deduplicated by county and enriched with county centroid coordinates for map overlays.

ParameterTypeDescription
statestringState abbreviation (default: TX)

Returns { data: [{ hpsa_id, designation_type, hpsa_score, county_name, county_fips, latitude, longitude }], error }

GET/api/cms/mapreadGrowth+

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: Growth tier or higher. Pro plan receives 403.

ParameterTypeDescription
statestringOptional state filter

Returns { data: [{ npi, first_name, last_name, specialty, latitude, longitude, total_paid_lifetime, total_paid_latest_year, latest_year, years_active }], error }

Alerts

GET/api/alerts/eventsread

License change events: expirations, status changes, new registrations, NPI deactivations, and disciplinary actions. Paginated.

ParameterTypeDescription
event_typestringFilter by event type
statestringFilter by state
pageintegerPage number (default: 1)
per_pageintegerResults per page (default: 25, max: 100)

Returns { data, error, meta: { total, page, per_page, total_pages } }

GET/api/alerts/grantsread

Dental-relevant federal grant opportunities from Grants.gov and SAM.gov. Paginated.

ParameterTypeDescription
sourcestringFilter by source: grants_gov or sam_gov
pageintegerPage number (default: 1)
per_pageintegerResults per page (default: 25, max: 100)
GET/api/alerts/ratesreadGrowth+

Medicaid/Medicare reimbursement rate change events detected by the quarterly diff pipeline. Surfaces CPT/CDT codes whose rates moved significantly.

Plan requirement: Growth tier or higher. Pro plan receives 403.

ParameterTypeDescription
statestringState abbreviation (default: TX)
significant_onlybooleanLimit to >=5% or >=$5 changes (default: true)
pageintegerPage number (default: 1)
per_pageintegerResults per page (default: 25, max: 100)
GET/api/alerts/npdbreadGrowth+

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: Growth tier or higher. Pro plan receives 403.

ParameterTypeDescription
statestringState abbreviation filter
significant_onlybooleanLimit to significant anomalies only (default: true)
pageintegerPage number (default: 1)
per_pageintegerResults per page (default: 25, max: 100)

Account

GET/api/profileread

Retrieve the authenticated user's profile (role, territory, company).

PATCH/api/profilewrite

Update profile fields: role, display_name, company_name, territory_states, territory_zips, territory_metros.

GET/api/saved-searchesread

List the authenticated user's saved searches.

POST/api/saved-searcheswrite

Create a saved search. Body: { name, filters, notify_email }. Plan limits apply (Pro: 10, Growth: unlimited).

DELETE/api/saved-searches/:idwrite

Delete a saved search by ID. Ownership is verified.

GET/api/preferencesread

Read digest email frequency preference.

PATCH/api/preferenceswrite

Update digest frequency. Body: { digest_frequency: 'off' | 'weekly' | 'daily' }.

Plan requirement: digest_frequency='daily' requires Growth tier or higher. 'off' and 'weekly' are available on all plans.

System

GET/api/healthnone

Health check. Returns database connectivity status and latency. No authentication required.

GET/api/statsnone

Public 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.

ParameterTypeDescription
statesstringComma-separated state abbreviations to scope the counts (e.g. TX or TX,CA,FL). Omit for nationwide totals.
GET/api/data-statusread

Data source freshness status for all pipeline sources.

Error Codes

All error responses follow a consistent format:

{ "data": null, "error": "Human-readable error message" }
CodeStatusDescription
400Bad RequestInvalid parameters, missing required fields, or malformed request body.
401UnauthorizedNo valid authentication provided. Check your API key or session.
403ForbiddenValid authentication but insufficient scope. Your API key needs the required scope for this endpoint.
404Not FoundThe requested resource does not exist (e.g., invalid NPI).
429Too Many RequestsRate limit exceeded. Check X-RateLimit-* headers for reset timing.
500Internal Server ErrorAn unexpected error occurred. Details are logged server-side — contact support if persistent.

Code Examples

cURL

bash
# Search TX solo dentists expiring within 12 months curl -s "https://providersignal.com/api/providers?state=TX&sole_prop=true&exp_months=12&per_page=10" \ -H "Authorization: Bearer ps_live_your_api_key_here" # Bulk export with cursor pagination (JSON) curl -s "https://providersignal.com/api/providers/export?state=TX&format=json&per_page=1000" \ -H "Authorization: Bearer ps_live_your_api_key_here" # Page 2: use next_cursor from previous response curl -s "https://providersignal.com/api/providers/export?state=TX&format=json&per_page=1000&cursor=1234567890" \ -H "Authorization: Bearer ps_live_your_api_key_here"

Python

python
import requests API_KEY = "ps_live_your_api_key_here" BASE = "https://providersignal.com/api" HEADERS = {"Authorization": f"Bearer {API_KEY}"} # Search providers r = requests.get(f"{BASE}/providers", headers=HEADERS, params={ "state": "TX", "sole_prop": "true", "exp_months": "12", "per_page": "50", }) data = r.json() print(f"Found {data['meta']['total']} providers") # Bulk iteration with cursor pagination cursor = None all_providers = [] while True: params = {"state": "TX", "format": "json", "per_page": "1000"} if cursor: params["cursor"] = cursor r = requests.get(f"{BASE}/providers/export", headers=HEADERS, params=params) page = r.json() all_providers.extend(page["data"]) cursor = page["meta"].get("next_cursor") if not cursor: break print(f"Exported {len(all_providers)} providers total")

JavaScript

javascript
const API_KEY = "ps_live_your_api_key_here"; const BASE = "https://providersignal.com/api"; const headers = { Authorization: `Bearer ${API_KEY}` }; // Search providers const res = await fetch( `${BASE}/providers?state=TX&sole_prop=true&exp_months=12`, { headers } ); const { data, meta } = await res.json(); console.log(`Found ${meta.total} providers`); // Bulk iteration with cursor let cursor = null; const allProviders = []; do { const params = new URLSearchParams({ state: "TX", format: "json", per_page: "1000", }); if (cursor) params.set("cursor", cursor); const r = await fetch( `${BASE}/providers/export?${params}`, { headers } ); const page = await r.json(); allProviders.push(...page.data); cursor = page.meta.next_cursor; } while (cursor); console.log(`Exported ${allProviders.length} providers`);

Example Response

GET /api/providers?state=TX&sole_prop=true&per_page=1

json
{ "data": [ { "npi": "1234567890", "first_name": "John", "last_name": "Smith", "specialty": "General Practice Dentistry", "city": "Austin", "state": "TX", "zip": "78701", "is_sole_proprietor": true, "parent_organization_name": null, "license_status": "Active", "license_expiration_date": "2027-03-31", "graduation_year": 1985, "has_disciplinary_action": false, "is_dso": false } ], "error": null, "meta": { "total": 14523, "page": 1, "per_page": 25, "total_pages": 581 } }

Get API access

API keys are available on all paid plans. Subscribe to generate your first key.

Start Free Trial