CredentialMatters API
A REST API for credential verification. Base URL https://api.credentialmatters.com/v1. All requests are authenticated with a bearer API key.
Overview
The CredentialMatters API verifies professional licenses and US board certifications against authoritative primary-source registries. Verifications are created asynchronously and resolve to an explicit status with evidence. Only the physician profession is currently enabled.
Base URL
https://api.credentialmatters.com/v1
Format
JSON (UTF-8)
Auth
Bearer API key
Authentication
Authenticate every request with an API key in the Authorization header. Keys carry scopes — verify for write operations and read for reads. Use a sandbox/test key during development.
Authorization: Bearer cm_live_xxx # production
Authorization: Bearer cm_test_xxx # sandbox / testKeep keys server-side. Never embed a key in a browser, mobile app, or public repository. Rotate from the operator portal if a key is exposed.
Quickstart
Create a verification. The call returns 202 Accepted with an id while work runs in the background.
# Create a verification (async → 202)
curl -sS -X POST https://api.credentialmatters.com/v1/verifications \
-H "Authorization: Bearer $CM_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{
"full_name": "Jane Smith",
"country_code": "US",
"state_or_region": "CA",
"profession_type": "physician"
}'Verifications
Poll the verification by id, block for a result with ?wait=true (up to 30s), or resolve an ambiguous needs_review result with the confirm endpoint.
# Block up to 30s for a terminal result instead of polling
curl -sS -X POST "https://api.credentialmatters.com/v1/verifications?wait=true&max_wait=30" \
-H "Authorization: Bearer $CM_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "full_name": "Jane Smith", "country_code": "US", "profession_type": "physician" }'Refining a search
Only full_name and country_code are required. The following optional fields sharpen disambiguation — they are signals, never hard filters, so a real match is never dropped for failing to match a hint.
npi— US National Provider Identifier. A valid 10-digit NPI resolves to that exact record deterministically, skipping name disambiguation.specialty— e.g."Cardiology"; ranks same-name candidates so the matching specialty leads.state_or_region,license_number,year_of_registration— additional corroborating signals.institution,degree_level,employer_name,linkedin_url,education[],jobs[]and more — the full profile signal set (see the field reference).
Supplying any refining signal (or hitting a same-name tie) triggers portal-grade disambiguation: a profession gate, rule tiers, an LLM candidate picker, and an evidence scorer. The response then includes a disambiguation object (selection basis + per-candidate trail) and an evidence summary. A weak lone match returns low_confidence rather than a false verified.
curl -sS -X POST https://api.credentialmatters.com/v1/verifications \
-H "Authorization: Bearer $CM_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"full_name": "Jane Smith",
"country_code": "US",
"state_or_region": "CA",
"specialty": "Cardiology",
"institution": "Johns Hopkins",
"degree_level": ["MD"],
"linkedin_url": "linkedin.com/in/jane-smith-md",
"education": [{ "school": "Johns Hopkins University", "degree": "MD" }]
}'Terminal statuses: verified, needs_review, not_found, low_confidence, unavailable, failed.
Batches
Verify up to 1,000 records in one request. Three input shapes are accepted — a JSON { items: [...] } body, a CSV file upload, or a raw text/csv body. Every batch is async: you get a 202 and an id to poll. Retrieve results as JSON or ?format=csv.
# application/json — canonical { items: [...] } body.
# Each item takes the SAME fields as a single verification, including the
# full refining set (specialty, institution, education[], jobs[], …) that
# triggers portal-grade disambiguation.
curl -sS -X POST https://api.credentialmatters.com/v1/batches \
-H "Authorization: Bearer $CM_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{
"items": [
{ "external_id": "1", "full_name": "Jane Smith",
"country_code": "US", "state_or_region": "CA",
"specialty": "Cardiology", "institution": "Johns Hopkins",
"degree_level": ["MD"],
"education": [{ "school": "Johns Hopkins University", "degree": "MD" }] },
{ "external_id": "2", "full_name": "John A Doe",
"country_code": "US", "npi": "1538291746" }
]
}'curl -sS "https://api.credentialmatters.com/v1/batches/$BATCH_ID?format=csv" \
-H "Authorization: Bearer $CM_API_KEY"Sample files
Download a ready-to-use template, fill in your records, and upload it as-is.
id,first_name,middle_name,last_name,country_code,state_or_region,license_number,year_of_registration,profession_type,specialty,institution,city,year_of_birth,employer_name,employer_location,linkedin_url,degree_level,sks_skills
1,Jane,,Smith,US,CA,,,physician,Cardiology,Johns Hopkins,San Francisco,1980,UCSF Medical Center,San Francisco CA,linkedin.com/in/jane-smith-md,MD,T2 Interventional Cardiology
2,John,A,Doe,US,NY,,,physician,Family Medicine,NYU,New York,1975,Mount Sinai,New York NY,,MD|MPH,
3,Aarav,,Sharma,IN,Maharashtra,,,physician,Orthopedics,AIIMS,Mumbai,1983,Kokilaben Hospital,Mumbai,,MBBS|MS,Fields & columns
The JSON body uses the same fields as a single verification (so npi and specialty work here too). CSV / JSON-array uploads split the name into first_name / middle_name / last_name columns.
| Field | Required | Description |
|---|---|---|
| full_name | required | Full name (given + family). |
| country_code | required | ISO-3166 alpha-2 (e.g. US, GB, IN). Routes to the registry. |
| state_or_region | optional | Quality signal; never a hard filter. |
| npi | optional | US NPI — a valid 10-digit value resolves deterministically. |
| specialty | optional | Specialty hint, e.g. "Cardiology". |
| license_number | optional | State / authority license number. |
| year_of_registration | optional | Year first registered/licensed. |
| institution | optional | Medical school / training institution. |
| city | optional | City — corroboration signal. |
| year_of_birth | optional | Estimates graduation era. |
| years_of_experience | optional | Approx. years in practice. |
| employer_name | optional | Current employer. |
| employer_location | optional | Employer location. |
| phone_number | optional | Contact phone — identity signal. |
| phone_number_verified | optional | Boolean; weights the phone signal. |
| linkedin_url | optional | LinkedIn profile — strong identity signal. |
| is_trainee | optional | Boolean; resident/fellow/trainee. |
| degree_level | optional | Array, e.g. ["MD","MPH"]. Rules out non-physician roles. |
| matched_medical_skills | optional | Array of curated medical-skill tags. |
| sks_skills | optional | Array of skill tags (e.g. "T2 Interventional Cardiology"). |
| education | optional | Array: [{school, degree, field_of_study, country, end_year}]. |
| jobs | optional | Array: [{job_title, field, location, ...}]. |
| accreditations | optional | Array: [{name, organization}]. |
| profession_type | optional | Defaults to "physician". |
| external_id | optional | Your correlation id; echoed back on each result. |
| Field | Required | Description |
|---|---|---|
| first_name | required | Given name. |
| last_name | required | Family name. |
| country_code | required | ISO-3166 alpha-2. |
| middle_name | optional | Middle name(s), if any. |
| state_or_region | optional | Quality signal; never a hard filter. |
| license_number | optional | State / authority license number. |
| year_of_registration | optional | Year first registered/licensed. |
| specialty | optional | Specialty hint. |
| institution | optional | Medical school / training institution. |
| city | optional | City — corroboration signal. |
| year_of_birth | optional | Estimates graduation era. |
| employer_name | optional | Current employer. |
| employer_location | optional | Employer location. |
| linkedin_url | optional | LinkedIn profile — strong identity signal. |
| degree_level | optional | Pipe-separated, e.g. "MD|MPH". |
| sks_skills | optional | Pipe-separated skill tags. |
| profession_type | optional | Defaults to "physician". |
| id | optional | Your correlation id → returned as external_id. |
Board certifications
Check US board certification (ABMS) for a physician. This call is synchronous and eligibility-gated by NPI taxonomy.
curl -sS -X POST https://api.credentialmatters.com/v1/board-certifications \
-H "Authorization: Bearer $CM_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "full_name": "Jane Smith", "npi": "1538291746", "state_or_region": "CA" }'Idempotency
Send an Idempotency-Key header on POST /v1/verifications and /v1/batches. A retry with the same key replays the original result instead of creating a duplicate — safe for network retries.
Idempotency-Key: 9f1c2e7a-4b6d-4a2f-9b0e-2c1d3e4f5a6bWebhooks
Register an endpoint to receive signed events instead of polling. The signing secret is returned once at creation — store it securely.
curl -sS -X POST https://api.credentialmatters.com/v1/webhooks \
-H "Authorization: Bearer $CM_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"url": "https://example.com/hooks/credentialmatters",
"events": ["verification.completed", "batch.completed"]
}'
# -> { "id": "...", "secret": "whsec_..." } (secret shown only now)Deliveries are signed and retried on failure. Verify the signature against your stored secret before trusting a payload.
Rate limits & errors
Rate limits
- 60 requests / minute per tenant
- 10,000 / 8h rolling cap per tenant
- Max 5 concurrent verifications
429withRetry-Afterwhen exceeded
Error envelope
Standard HTTP status codes with a consistent JSON body:
{
"type": "rate_limited",
"message": "Too many requests",
"request_id": "req_..."
}Endpoint reference
The complete /v1 surface. The live OpenAPI spec is the source of truth.
| Method | Path | Scope | Notes |
|---|---|---|---|
| POST | /v1/verifications | verify | Async → 202; ?wait=true&max_wait≤30; Idempotency-Key supported. |
| GET | /v1/verifications/{id} | read | Fetch one; tenant-scoped (404 if not yours). |
| POST | /v1/verifications/{id}/confirm | verify | Resolve needs_review by npi / license_number / index. |
| POST | /v1/batches | verify | JSON / CSV / multipart; ≤ 1000 items; Idempotency-Key. |
| GET | /v1/batches/{id} | read | JSON or ?format=csv (also Accept: text/csv). |
| POST | /v1/board-certifications | verify | US physicians; synchronous. |
| GET | /v1/usage | read | Count-only tenant usage; ?days=1..365. |
| GET | /v1/countries | read | Supported countries / coverage. |
| POST | /v1/webhooks | verify | Secret returned once; events verification.completed, batch.completed. |
| GET | /v1/webhooks | read | List endpoints. |
| DELETE | /v1/webhooks/{id} | verify | Remove endpoint (204). |
| GET | /v1/openapi.json | public | Live spec (no auth). |
| GET | /v1/docs | public | Swagger UI (no auth). |