Skip to main content
xcircl
Developers · reference

API documentation

A small, predictable REST API over the source-linked facts layer. Base URL: https://xcircl.com/api/v1. Responses are JSON by default; pass format=csv for a download.

Authentication

Public endpoints (coverage, sample) need no key. Every provider-endpoint call requires one — get a free key in 30 seconds at signup (identity fields, 500 calls/mo, evaluation & non-commercial use). Paid keys come in three plans — Builder ($99/mo: full fields, one vertical, 5,000 calls/mo), Developer ($750/mo: multi-vertical, 25,000 calls/mo) and Enterprise (scoped by phone). A Builder key is bound to its vertical — querying another returns an upgrade notice; exceeding a quota returns 429. See pricing.

auth
# Pass your key as a Bearer token
curl https://xcircl.com/api/v1/providers/ \
  -H "Authorization: Bearer YOUR_API_KEY"

# x-api-key header and ?key= are also accepted

Publication boundary

Every tier delivers the verified publication set by default (publish_tier='verified' in our pipeline). The paid tier, the public sample, and all CSV exports are strictly verified-only. The wider tracked set exists for transparency: the free tier can inspect it by adding include=tracked (JSON only), and each non-verified record self-declares with "publish_tier": "internal_only". Responses state their boundary in the publish_boundary field.

List providers

Filter by vertical, state, city, business_mode. Page with limit (1–1000) and offset (a non-integer, negative, or out-of-range offset returns 400 invalid_offset). Free tier may add include=tracked for the transparency view.

Vertical availability: glp1 is the verified vertical today. pet_health and medspa are loading; dental and senior are queued. Non-live verticals are reserved in the schema but return no data yet, so don’t integrate against them until they’re announced.

request
GET /api/v1/providers/?vertical=glp1&state=CA&business_mode=online&limit=20&offset=0

# format=csv for a CSV download (column set follows your tier)
response (paid tier)
{
  "tier": "paid",
  "pagination": { "total": 142, "limit": 20, "offset": 0, "returned": 20 },
  "filters": { "vertical": "glp1", "state": "CA", "city": null, "business_mode": "online" },
  "data": [
    {
      "entity_id": "glp1_…",
      "slug": "…",
      "vertical": "glp1",
      "name": "Example Weight Care",
      "city": "Los Angeles",
      "state": "CA",
      "business_mode": "online",
      "latitude": 34.05, "longitude": -118.24,
      "npi": "1234567890",
      "legitscript": { "status": "unverified", "source": null, "verified_at": null },
      "license":    { "status": "reported", "states_count": 3, "source": "Provider-reported", "verified_at": "2026-05-01T…" },
      "fda":        { "status": "clear", "source": "FDA Warning Letters database", "verified_at": "2026-06-01T…" },
      "price":      { "range": "$200–$400/mo", "monthly_min": 200, "monthly_max": 400, "source": "…", "verified_at": "…" }
    }
  ]
}

L0 identity layer (free, all specialties)

Beyond the verified verticals, xcircl exposes a free horizontal identity layer sourced from CMS NPPES — every US provider, all specialties. Query it by NUCC taxonomy code (with no vertical), optionally filtered by state / city, paged with the same limit / offset.

Rows are identity-only and self-declare "layer": "identity" and "source": "NPPES" with a checked_at date. Each carries a human-readable taxonomy_display (e.g. “Family Medicine”, resolved from the NUCC taxonomy map) and a free oig screening field (see below). The vertical compliance fields (legitscript, license, fda, price) are returned as locked placeholders — identity rows never carry a verified signal, so they must not be read as the verified tier.

Query by name, too. taxonomy accepts either a 10-character NUCC code or a classification name — ?taxonomy=Family Medicine — which we resolve to codes via the taxonomy map.

L0 identity — free key
# Free key. Query by NUCC code OR a classification name:
curl "https://xcircl.com/api/v1/providers/?taxonomy=122300000X&state=TX&limit=5" \
  -H "Authorization: Bearer YOUR_FREE_KEY"
curl "https://xcircl.com/api/v1/providers/?taxonomy=Family%20Medicine&state=TX" \
  -H "Authorization: Bearer YOUR_FREE_KEY"

# → rows self-declare "layer":"identity", "source":"NPPES",
#   carry a human "taxonomy_display", and a free "oig" screening field

OIG exclusion screening (free)

Every provider response — L0 identity and vertical alike — carries a free oig field that screens the provider’s NPI against the HHS-OIG List of Excluded Individuals/Entities (LEIE). A match returns the exclusion type, date, source, and check time.

A no-match is not a clearance. The LEIE NPI-keys only ~10.5% of its records, so the absence of a match tells you nothing about whether the provider is excluded. We never emit a clean / cleared value — only an explicit match, or an explicit "npi_match": "none" with a note that says so. Treat it as one signal among many, not a green light.

oig field — free
# OIG screening is FREE on every provider row (L0 and vertical):
# match:
"oig": { "excluded": true, "type": "1128b8", "date": "2022-03-20",
         "source": "oig_leie", "checked_at": "…" }
# no match (NOT a clearance):
"oig": { "npi_match": "none",
         "note": "No NPI-keyed match in the OIG LEIE. Absence of a match is
                  not a clearance — 89.5% of LEIE records carry no NPI." }

Single provider

Look up one provider by entity_id or slug. Same tiering as the list.

request
GET /api/v1/providers/{entity_id-or-slug}/

Coverage

Public aggregate — counts, business-mode split, and a per-state breakdown, computed live.

request
GET /api/v1/coverage/   # public — live counts, by_state breakdown

Rate limits & quota headers

Quotas are monthly, per key, on a UTC calendar month. The free key allows 500 provider calls/mo; Builder 5,000; Developer 25,000; Enterprise unmetered. There is no fixed per-second cap today — metering is by monthly volume.

Metered responses expose your live position in X-RateLimit-* headers, so you can back off before hitting the ceiling. When the monthly quota is exhausted, calls return 429 with a Retry-After header (seconds until reset). Metering fails open — if the counter is ever unavailable, your request still succeeds and no rate-limit headers are sent.

response headers
# Metered (Builder / Developer) responses carry:
X-RateLimit-Limit: 25000       # your plan's monthly call quota
X-RateLimit-Remaining: 24994   # calls left this UTC month
X-RateLimit-Used: 6            # calls used so far, including this one
X-RateLimit-Reset: 1717200     # seconds until reset (00:00 UTC on the 1st)

# On 429, additionally:
Retry-After: 1717200           # seconds until the quota resets

Fields & tiers

Free returns public identity. Paid adds verified compliance signals and price — each as an object with status, source, and verified_at.

Locked fields. On a free key the paid fields are still present in the response, as placeholders: {"locked":true,"unlock":"Builder $99/mo — https://xcircl.com/pricing/"}. “Locked” only means the field is paid — it is never a claim that the provider is verified. A paid key returns each field in its real state (verified / clear / flagged / reported / unverified) with its source and check date — we never invent one.

FieldTypeTierDescription
entity_idstringfreeStable cross-SoMo identifier.
namestringfreeProvider name.
verticalenumfreeglp1 · pet_health · …
city / statestringfreeLocation.
business_modeenumfreeonline · physical · both.
latitude / longitudenumberfreeGeocoordinates.
npistringfreeNPI from NPPES.
legitscriptsignalpaidCertification status + source + date.
licensesignalpaidState licensing + states_count + source + date.
fdasignalpaidWarning-letter screen + source + date.
priceobjectpaidMonthly range + source + date.

Errors

Every error returns { error, code } — a human message plus a stable machine-readable code. A 5xx never leaks internal detail; it always returns code: "internal_error".

  • 400 — bad parameter: invalid_vertical, invalid_business_mode, invalid_limit, invalid_offset.
  • 401 key_required — no key was supplied for a provider endpoint; get a free key at signup.
  • 401 invalid_key — invalid, unknown, rotated, or revoked API key.
  • 403 vertical_not_permitted — Builder key queried outside its bound vertical.
  • 404 not_found — no active provider matches.
  • 429 quota_exceeded — monthly call quota reached (see rate limits).
  • 500 internal_error — an unexpected server error (details are logged, never returned).

OpenAPI spec

The full contract is published as OpenAPI 3.1 at /openapi.json. Point any OpenAPI client (Swagger UI, Postman, or a code generator like openapi-generator) at that URL to explore the API or generate a typed client in your language.

generate a client
# Explore in Swagger UI, or generate a client:
npx @openapitools/openapi-generator-cli generate \
  -i https://xcircl.com/openapi.json \
  -g typescript-fetch -o ./xcircl-client

Try it now

Run your first call in five minutes with the quickstart.