Skip to content
CredentialMatters
Developers

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 header
Authorization: Bearer cm_live_xxx        # production
Authorization: Bearer cm_test_xxx        # sandbox / test

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

Refined request (profile signals → portal-grade disambiguation)
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" }
    ]
  }'
Fetch results as CSV
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.

physicians-sample.csv
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.

JSON body — items[] fields
FieldRequiredDescription
full_namerequiredFull name (given + family).
country_coderequiredISO-3166 alpha-2 (e.g. US, GB, IN). Routes to the registry.
state_or_regionoptionalQuality signal; never a hard filter.
npioptionalUS NPI — a valid 10-digit value resolves deterministically.
specialtyoptionalSpecialty hint, e.g. "Cardiology".
license_numberoptionalState / authority license number.
year_of_registrationoptionalYear first registered/licensed.
institutionoptionalMedical school / training institution.
cityoptionalCity — corroboration signal.
year_of_birthoptionalEstimates graduation era.
years_of_experienceoptionalApprox. years in practice.
employer_nameoptionalCurrent employer.
employer_locationoptionalEmployer location.
phone_numberoptionalContact phone — identity signal.
phone_number_verifiedoptionalBoolean; weights the phone signal.
linkedin_urloptionalLinkedIn profile — strong identity signal.
is_traineeoptionalBoolean; resident/fellow/trainee.
degree_leveloptionalArray, e.g. ["MD","MPH"]. Rules out non-physician roles.
matched_medical_skillsoptionalArray of curated medical-skill tags.
sks_skillsoptionalArray of skill tags (e.g. "T2 Interventional Cardiology").
educationoptionalArray: [{school, degree, field_of_study, country, end_year}].
jobsoptionalArray: [{job_title, field, location, ...}].
accreditationsoptionalArray: [{name, organization}].
profession_typeoptionalDefaults to "physician".
external_idoptionalYour correlation id; echoed back on each result.
CSV / JSON-array — upload columns
FieldRequiredDescription
first_namerequiredGiven name.
last_namerequiredFamily name.
country_coderequiredISO-3166 alpha-2.
middle_nameoptionalMiddle name(s), if any.
state_or_regionoptionalQuality signal; never a hard filter.
license_numberoptionalState / authority license number.
year_of_registrationoptionalYear first registered/licensed.
specialtyoptionalSpecialty hint.
institutionoptionalMedical school / training institution.
cityoptionalCity — corroboration signal.
year_of_birthoptionalEstimates graduation era.
employer_nameoptionalCurrent employer.
employer_locationoptionalEmployer location.
linkedin_urloptionalLinkedIn profile — strong identity signal.
degree_leveloptionalPipe-separated, e.g. "MD|MPH".
sks_skillsoptionalPipe-separated skill tags.
profession_typeoptionalDefaults to "physician".
idoptionalYour 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.

POST /v1/board-certifications
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 header
Idempotency-Key: 9f1c2e7a-4b6d-4a2f-9b0e-2c1d3e4f5a6b

Webhooks

Register an endpoint to receive signed events instead of polling. The signing secret is returned once at creation — store it securely.

Register a webhook
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
  • 429 with Retry-After when 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.

MethodPathScopeNotes
POST/v1/verificationsverifyAsync → 202; ?wait=true&max_wait≤30; Idempotency-Key supported.
GET/v1/verifications/{id}readFetch one; tenant-scoped (404 if not yours).
POST/v1/verifications/{id}/confirmverifyResolve needs_review by npi / license_number / index.
POST/v1/batchesverifyJSON / CSV / multipart; ≤ 1000 items; Idempotency-Key.
GET/v1/batches/{id}readJSON or ?format=csv (also Accept: text/csv).
POST/v1/board-certificationsverifyUS physicians; synchronous.
GET/v1/usagereadCount-only tenant usage; ?days=1..365.
GET/v1/countriesreadSupported countries / coverage.
POST/v1/webhooksverifySecret returned once; events verification.completed, batch.completed.
GET/v1/webhooksreadList endpoints.
DELETE/v1/webhooks/{id}verifyRemove endpoint (204).
GET/v1/openapi.jsonpublicLive spec (no auth).
GET/v1/docspublicSwagger UI (no auth).