Benchmark API Documentation
Programmatic access to physician compensation benchmark data
Quick Start
All API requests require an API key passed via the X-API-Key header. Your key is provided after subscribing to an API plan.
# 1. Check which specialties have data curl -H "X-API-Key: sdr_live_your_key_here" \ "https://www.salarydr.com/api/v1/specialties" # 2. Check data availability for your query curl -H "X-API-Key: sdr_live_your_key_here" \ "https://www.salarydr.com/api/v1/available-data?specialty=Family+Medicine&state=New+York" # 3. Retrieve full benchmark data curl -H "X-API-Key: sdr_live_your_key_here" \ "https://www.salarydr.com/api/v1/benchmarks?specialty=Family+Medicine&state=New+York" # 4. Check your usage and remaining limits curl -H "X-API-Key: sdr_live_your_key_here" \ "https://www.salarydr.com/api/v1/me/usage"
Endpoints
/specialtiesList all specialties with available data counts and geographic coverage. Useful for populating dropdowns or validating inputs. Cached for 24 hours.
Example Request
curl -H "X-API-Key: sdr_live_abc123..." \ "https://www.salarydr.com/api/v1/specialties"
Example Response
{
"specialties": [
{
"name": "Family Medicine",
"sample_size": 142,
"states_with_data": 28,
"publishable": true,
"sufficient": true
},
{
"name": "Cardiology",
"sample_size": 67,
"states_with_data": 18,
"publishable": true,
"sufficient": true
}
],
"count": 35
}/available-dataPreview data availability before querying benchmarks. Returns sample sizes at each geographic scope, available years, and BLS data availability. Cached for 1 hour.
Query Parameters
| Parameter | Required | Description |
|---|---|---|
| specialty | Required | Physician specialty (e.g., "Family Medicine") |
| state | Optional | US state name (e.g., "New York") |
Example Request
curl -H "X-API-Key: sdr_live_abc123..." \ "https://www.salarydr.com/api/v1/available-data?specialty=Family+Medicine&state=New+York"
Example Response
{
"specialty": "Family Medicine",
"availability": {
"state": {
"label": "New York",
"n": 12,
"years": [2023, 2024, 2025, 2026],
"sufficient": false
},
"regional": {
"label": "Northeast",
"n": 38,
"years": [2022, 2023, 2024, 2025, 2026],
"sufficient": true
},
"national": {
"label": "National",
"n": 142,
"years": [2021, 2022, 2023, 2024, 2025, 2026],
"sufficient": true
}
},
"bls_available": true,
"bls_soc_code": "29-1215"
}/benchmarksRetrieve full compensation percentile benchmarks with multi-scope data, statistical confidence, and optional breakdowns. Primary data endpoint.
Query Parameters
| Parameter | Required | Description |
|---|---|---|
| specialty | Required | Physician specialty (e.g., "Family Medicine", "Cardiology") |
| state | Optional | US state (e.g., "New York", "California") |
| region | Optional | US region: Northeast, Midwest, South, West |
| year_start | Optional | Filter start year (e.g., 2024) |
| year_end | Optional | Filter end year (e.g., 2026) |
| practice_setting | Optional | Hospital Employed, Private Practice, Academic, etc. |
| employment_type | Optional | Full-Time (W-2), Independent Contractor (1099), etc. |
Example Request
curl -H "X-API-Key: sdr_live_abc123..." \ "https://www.salarydr.com/api/v1/benchmarks?specialty=Family+Medicine&state=New+York"
Example Response
{
"data": {
"specialty": "Family Medicine",
"geography": {
"state": "New York",
"region": "Northeast",
"scope_used": "state"
},
"sample_size": 12,
"percentiles": {
"p10": 195000,
"p25": 220000,
"p50": 285000,
"p75": 340000,
"p90": 395000
},
"mean": 298000,
"confidence": {
"sufficient": false,
"publishable": true,
"standard_deviation": 62000,
"confidence_interval_95": {
"lower": 249900,
"upper": 320100
}
},
"scopes": {
"state": {
"label": "New York",
"n": 12,
"publishable": true,
"sufficient": false,
"percentiles": { "p10": 195000, "p25": 220000, "p50": 285000, "p75": 340000, "p90": 395000 },
"mean": 298000
},
"regional": {
"label": "Northeast",
"n": 38,
"publishable": true,
"sufficient": true,
"percentiles": { "p10": 190000, "p25": 215000, "p50": 278000, "p75": 335000, "p90": 390000 },
"mean": 290000
},
"national": {
"label": "National",
"n": 142,
"publishable": true,
"sufficient": true,
"percentiles": { "p10": 180000, "p25": 210000, "p50": 270000, "p75": 330000, "p90": 385000 },
"mean": 282000
}
},
"bls_comparison": {
"available": true,
"bls_data": {
"soc_code": "29-1215",
"soc_title": "Family Medicine Physicians",
"median": 224460,
"year": 2024
},
"salarydr_median": 285000,
"variance_pct": 26.9,
"methodology_note": "BLS OEWS uses employer-reported W-2 wages; SalaryDr includes total compensation."
},
"methodology": {
"min_sample": 5,
"outlier_handling": "Winsorized at $0 and $2,000,000",
"fte_basis": "40 hours/week (2,080 hours/year)",
"data_date_range": {
"earliest": "2023-06-15",
"latest": "2026-02-28"
}
},
"data_as_of": "2026-03-03"
},
"meta": {
"request_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"rate_limit": {
"remaining_daily": 99,
"remaining_monthly": 2999,
"reset": 1741219200
}
}
}reset is a Unix epoch timestamp (seconds) for the next daily limit reset at midnight UTC.
Professional/Enterprise: Additional Fields
Higher tiers include FTE-normalized data, trending, breakdowns, and compensation components:
// Additional fields in the response for Professional/Enterprise tiers:
{
"data": {
...
"fte_normalized": {
"percentiles": { "p10": 200000, "p25": 228000, "p50": 290000, "p75": 345000, "p90": 400000 },
"mean": 302000,
"n": 12
},
"components": {
"base_salary": { "p25": 200000, "p50": 250000, "p75": 300000 },
"bonus": { "p25": 15000, "p50": 35000, "p75": 55000 }
},
"trending": [
{ "year": 2024, "n": 8, "p25": 210000, "p50": 265000, "p75": 320000, "mean": 275000, "publishable": true },
{ "year": 2025, "n": 12, "p25": 220000, "p50": 285000, "p75": 340000, "mean": 298000, "publishable": true }
],
"breakdowns": {
"by_experience": [
{ "label": "0-5 years", "n": 6, "p25": 195000, "p50": 240000, "p75": 280000, "mean": 245000, "publishable": true }
],
"by_practice_setting": [...],
"by_employment_type": [...]
}
}
}JavaScript Example
const response = await fetch(
'https://www.salarydr.com/api/v1/benchmarks?specialty=Family+Medicine&state=New+York',
{ headers: { 'X-API-Key': 'sdr_live_your_key_here' } }
);
const { data, meta } = await response.json();
// Primary benchmark data
console.log(`Median: $${data.percentiles.p50.toLocaleString()}`);
console.log(`Sample: n=${data.sample_size}`);
console.log(`95% CI: $${data.confidence.confidence_interval_95.lower.toLocaleString()} – $${data.confidence.confidence_interval_95.upper.toLocaleString()}`);
// Compare scopes
if (data.scopes.state) {
console.log(`State median: $${data.scopes.state.percentiles.p50.toLocaleString()} (n=${data.scopes.state.n})`);
}
console.log(`National median: $${data.scopes.national.percentiles.p50.toLocaleString()} (n=${data.scopes.national.n})`);
// Rate limit tracking
console.log(`Remaining requests today: ${meta.rate_limit.remaining_daily}`);Python Example
import requests
response = requests.get(
"https://www.salarydr.com/api/v1/benchmarks",
params={"specialty": "Family Medicine", "state": "New York"},
headers={"X-API-Key": "sdr_live_your_key_here"},
)
data = response.json()["data"]
median = data["percentiles"]["p50"]
n = data["sample_size"]
ci = data["confidence"]["confidence_interval_95"]
print(f"Median: ${median:,}")
print(f"Sample: n={n}")
print(f"95% CI: ${ci['lower']:,} to ${ci['upper']:,}")
# Compare all geographic scopes
for scope in ["state", "regional", "national"]:
s = data["scopes"].get(scope)
if s:
p50 = s["percentiles"]["p50"]
print(f" {s['label']}: ${p50:,} (n={s['n']})")/me/usageCheck your API key's plan details, current usage, and remaining request limits. Does not count toward rate limits.
Example Request
curl -H "X-API-Key: sdr_live_abc123..." \ "https://www.salarydr.com/api/v1/me/usage"
Example Response
{
"plan_tier": "starter",
"subscription_status": "active",
"key_prefix": "sdr_live_abc",
"created_at": "2026-03-01T10:00:00Z",
"usage": {
"requests_today": 15,
"requests_this_month": 420,
"daily_limit": 100,
"monthly_limit": 3000,
"remaining_daily": 85,
"remaining_monthly": 2580,
"daily_reset": 1741219200
},
"features": {
"trending": false,
"fte_normalized": false,
"breakdowns": false
}
}daily_reset is a Unix epoch timestamp for the next midnight UTC reset.
Response Headers
Rate limit information is included in every response as both headers and the meta.rate_limit object.
| Header | Description |
|---|---|
| X-RateLimit-Limit-Daily | Your daily request limit |
| X-RateLimit-Remaining-Daily | Remaining daily requests |
| X-RateLimit-Limit-Monthly | Your monthly request limit |
| X-RateLimit-Remaining-Monthly | Remaining monthly requests |
| X-RateLimit-Reset | Unix epoch (seconds) — next daily reset at midnight UTC |
Plans & Rate Limits
| Tier | Price | Daily | Monthly | Features |
|---|---|---|---|---|
| Starter | $999/mo | 100 | 3,000 | Percentiles, mean, multi-scope, BLS comparison |
| Professional | $2,499/mo | 500 | 15,000 | + FTE, trending, breakdowns, components |
| Enterprise | Custom | 2,000 | 60,000 | + custom date ranges, priority support, SLA |
Tier Feature Details
All Tiers
- Percentile data: P10, P25, P50 (Median), P75, P90
- Mean compensation
- Sample size and data quality indicators
- Multi-scope data: state, regional, and national in one response
- Statistical confidence: standard deviation, 95% confidence interval
- BLS OEWS cross-reference comparison
- Practice setting and employment type filters
- Data date range in methodology
Professional & Enterprise
- FTE Normalized: Part-time compensation scaled to 40hr/week equivalent
- Trending: Year-over-year percentile data with per-year sample sizes
- Breakdowns: By experience level, practice setting, and employment type
- Components: Base salary + bonus/incentive percentiles separately
Enterprise Only
- Custom Date Ranges: Filter data by submission year range
- Priority Support: Dedicated support channel and SLA
- Higher Limits: 2,000 daily / 60,000 monthly requests
Data Quality Indicators
Every response includes quality indicators to help you assess data reliability:
| Field | Description |
|---|---|
| confidence.publishable | n ≥ 5 — minimum for reporting |
| confidence.sufficient | n ≥ 15 — high-confidence threshold |
| confidence.standard_deviation | Compensation variance in the sample |
| confidence.confidence_interval_95 | 95% CI around the median (lower/upper bounds) |
| scopes.*.publishable | Per-scope data quality flag |
Error Codes
| Code | Meaning | Solution |
|---|---|---|
| 400 | Missing or invalid parameters | Check required params; use full state names (e.g., "New York") |
| 401 | Invalid or missing API key | Check your X-API-Key header |
| 403 | Subscription inactive or feature not in tier | Update payment or upgrade tier |
| 404 | Insufficient data for the query | Try broader geography or use /available-data first |
| 429 | Rate limit exceeded | Wait until reset (see X-RateLimit-Reset header) or upgrade tier |
| 503 | Service unavailable | Retry after a brief delay |
All error responses return {"error": "Description of the issue"}.
Best Practices
- 1. Check availability first. Call
/available-databefore/benchmarksto avoid 404s on low-data queries. - 2. Use multi-scope data. The
scopesobject returns state, regional, and national data in a single request — no need for multiple calls. - 3. Cache responses client-side. Benchmark data is cached for 1 hour server-side. Respect the
Cache-Controlheader. - 4. Check confidence indicators. Use
confidence.sufficient(n≥15) for high-stakes decisions.publishable(n≥5) is the minimum threshold. - 5. Monitor rate limits. Track
X-RateLimit-Remaining-Dailyheaders to avoid hitting limits. - 6. Handle 404 gracefully. Broaden your geographic scope (remove state filter) or check a different specialty.
Need Help?
Contact us for custom integrations, enterprise support, or questions about the API.