Documentation

API documentation for HealthSherpa One.

Start with the tools available today: check that the service is up, find the right county for a ZIP code, and return plan quote results. Enrollment access is reviewed by our team and enabled separately after onboarding.

What you can do today
GET /v1/ping

Check if the service is up and your API key is working before you try county lookups or quotes.

POST /v1/quotes

Get plan and premium results for one coverage type at a time, including issuer, pricing, plan details, and document links when available.

GET /v1/reference/counties

Find the county options for a ZIP code so the agent can choose the right county before requesting quotes.

GET /v1/reference/issuers

List the QHP issuers active in a state for a plan year so you can build issuer filters and validate quote requests against the canonical HIOS issuer IDs.

Future-friendly responses

Responses may add optional fields over time. Build your integration to ignore fields it does not recognize.

GET

/v1/ping

Live View in OpenAPI

Check that HealthSherpa One is reachable and that your API key is active before making county lookup or quote requests.

Full URL /v1/ping

Send the same x-api-key header you use for other API calls. A successful response means the service accepted the request and recognized your key.

request.http
GET /v1/ping
x-api-key: YOUR_API_KEY
200 response.json
{
  "environment": "production",
  "ok": true
}
200
The service is up and your key was accepted.
403 forbidden
API Gateway rejected the key as missing, invalid, not active yet, or not authorized for this API, or the request was blocked by an API edge policy.
429 rate_limited
Too many requests were sent in a short period. Slow down and retry.
POST

/v1/quotes

Live View in OpenAPI

Return plan quote results for one coverage type at a time. Look up the county first, then send the market, location, and household details needed to calculate plan pricing.

Full URL /v1/quotes

Use this structure to describe what the agent is quoting: the market, the county, and the household members. The logged-in portal uses the same shape for its prefilled medical example.

Field Use
context Required. Product, exchange, coverage, and plan-year details.
location Required. ZIP, county FIPS, and state when supplied.
household Required. Applicants and applicant screening flags.
annual_income
effective_date
Optional for quote-only requests.
filters
sort
page
Optional controls for filtering, ordering, and pagination.
include Deprecated and ignored. Quotes always return the canonical payload and add a deprecation message to meta.warnings when include is supplied.

These values are checked before the quote is processed. Invalid values return a 400 invalid_request response with field-level details.

context.product
Allowed
aca, ichra
Notes
aca is the current portal example. ichra uses the same request shape.
context.exchange
Allowed
on_exchange, off_exchange
Rule
Exchange scope is singular per request. Do not send a combined market value.
context.coverage_family + context.coverage_type
medical
medical
ancillary
dental, vision, supplemental_other
Rule
The controller validates that coverage_type matches the selected coverage_family.
context.plan_year
Range
Integer greater than or equal to 2020
Default
Uses the current published plan year when omitted.
location.zip_code + location.fips_code
Format
Both fields must be exactly five digits.
Lookup
Use GET /v1/reference/counties to resolve the county FIPS code before quoting.
location.state
Allowed
Optional two-letter quoteable state code for one of the 50 states or DC.
Rule
When present, it must match the leading state digits in fips_code.
household.applicants[].relationship
Allowed
primary, spouse, dependent
household.applicants[].gender
Allowed
female, male, other, unknown
household.applicants[].uses_tobacco and screening flags
Boolean
true or false
Rule
uses_tobacco is required for each applicant. pregnant, blind_or_disabled, and native_american are optional booleans.
filters.medical.metal_levels[]
Allowed
bronze, expanded_bronze, silver, gold, platinum, catastrophic
Scope
Medical-only filter. Invalid values are rejected by the request model.
filters.medical.plan_types[]
Allowed
hmo, ppo, epo, pos, indemnity
Scope
Medical-only filter. Invalid values are rejected by the request model.
sort.direction
Allowed
asc, desc
sort.field
medical
premium, deductible, moop
dental
premium, deductible, moop, annual_maximum
vision
premium, exam_copay, frames_allowance
supplemental_other
premium, issue_age_max
page.number + page.size
Default
page.number defaults to 1. page.size defaults to 20.
Limit
page.size is capped at 500.
request.http
POST /v1/quotes
x-api-key: YOUR_API_KEY
Content-Type: application/json

{
  "context": {
    "product": "aca",
    "exchange": "on_exchange",
    "coverage_family": "medical",
    "coverage_type": "medical",
    "plan_year": 2026
  },
  "location": {
    "zip_code": "30301",
    "fips_code": "13121",
    "state": "GA"
  },
  "household": {
    "household_size": 1,
    "applicants": [
      {
        "member_id": "primary",
        "age": 30,
        "date_of_birth": "1996-05-01",
        "relationship": "primary",
        "uses_tobacco": false,
        "pregnant": false,
        "blind_or_disabled": false,
        "native_american": false
      }
    ],
    "annual_income": 50000,
    "effective_date": "2026-05-01"
  },
  "sort": {
    "field": "premium",
    "direction": "asc"
  },
  "page": {
    "number": 1,
    "size": 20
  }
}
Field Use
plans[] Homogeneous for the requested coverage_type.
Plan data Identifiers, issuer, network, pricing, documents, availability, subtype-specific details, and release metadata.
meta coverage_type, page_number, page_size, result_count, and warnings.
200 response.json
{
  "plans": [
    {
      "plan_id": "38344AK1060001",
      "variant_id": "01",
      "coverage_family": "medical",
      "coverage_type": "medical",
      "name": "Premera Blue Cross Preferred Gold 1500",
      "context": {
        "product": "aca",
        "exchange": "on_exchange",
        "coverage_family": "medical",
        "coverage_type": "medical",
        "plan_year": 2026
      },
      "issuer": {
        "issuer_id": "38344",
        "name": "Premera Blue Cross Blue Shield of Alaska",
        "state": "AK"
      },
      "pricing": {
        "gross_premium": 844.15,
        "ehb_premium": 843.39,
        "subsidy_applied": 0.0,
        "net_premium": 844.15,
        "currency": "USD",
        "billing_period": "monthly"
      },
      "documents": {
        "sbc_url": "https://example.com/sbc.pdf",
        "formulary_url": "https://example.com/formulary"
      },
      "availability": {
        "service_area_id": "AKS001",
        "rating_area": "1",
        "state": "AK"
      },
      "details": {
        "type": "medical",
        "metal_level": "gold",
        "plan_type": "ppo",
        "hsa_eligible": false,
        "deductible_individual": 1500,
        "deductible_family": 3000,
        "moop_individual": 9200,
        "moop_family": 18400,
        "csr_level": "none"
      },
      "release": {
        "release_id": "2026-public-release",
        "plan_year": 2026
      }
    }
  ],
  "meta": {
    "coverage_type": "medical",
    "page_number": 1,
    "page_size": 20,
    "result_count": 1,
    "warnings": []
  }
}
200
Returns matching plans. If no plans match, the response is still 200 with an empty plans[] array and meta.result_count: 0.
400 invalid_request
The request is missing required fields, uses the wrong JSON type, has an invalid ZIP or FIPS code, or includes values that do not match the allowed options.
403 forbidden
API Gateway rejected the key as missing, invalid, not active yet, or not authorized for this API, or the request was blocked by an API edge policy.
429 rate_limited
Too many quote requests were sent in a short period. Slow down and retry.
503 service_unavailable
A plan data, catalog, or marketplace dependency could not complete the quote request.
500 internal_error
Something unexpected happened on the server. Retry later or contact support if it continues.
400 error.json
{
  "error": {
    "code": "invalid_request",
    "message": "Validation failed.",
    "details": {
      "context.product": [
        "Product is not included in the list"
      ],
      "location.zip_code": [
        "ZIP code must be 5 digits"
      ],
      "household.applicants[0].member_id": [
        "Member ID can't be blank"
      ],
      "household.applicants[0].uses_tobacco": [
        "Uses tobacco is required"
      ]
    }
  }
}
GET

/v1/reference/counties

Live View in OpenAPI

Find the county choices for a ZIP code before quoting. If a ZIP crosses county lines, show the choices to the agent and send the selected fips_code in POST /v1/quotes.

Full URL /v1/reference/counties

Send one required zip_code query parameter. HealthSherpa checks its ZIP-to-county data first and falls back to the CMS Marketplace lookup when needed.

Field Use
zip_code Required five-digit query parameter.
fips_code Returned county choice to carry into the quote request.
API Explorer Uses this same one-field request in the logged-in portal.
request.http
GET /v1/reference/counties?zip_code=42223
x-api-key: YOUR_API_KEY

The public response returns only the fields you need for the next step: county name, county fips_code, and state.

200 response.json
{
  "counties": [
    {
      "fips_code": "21047",
      "name": "Christian County",
      "state": "KY"
    },
    {
      "fips_code": "47125",
      "name": "Montgomery County",
      "state": "TN"
    }
  ]
}
200
Returns one or more county choices for the ZIP.
400 invalid_request
zip_code is missing or is not a five-digit ZIP code.
403 forbidden
API Gateway rejected the key as missing, invalid, not active yet, or not authorized for this API, or the request was blocked by an API edge policy.
404 not_found
No county data was found for that ZIP code.
429 rate_limited
Too many lookup requests were sent in a short period. Slow down and retry.
503 service_unavailable
The fallback county lookup could not complete the request.
500 internal_error
Something unexpected happened on the server. Retry later or contact support if it continues.
404 error.json
{
  "error": {
    "code": "not_found",
    "message": "No counties found for ZIP 12345."
  }
}
GET

/v1/reference/issuers

Live View in OpenAPI

List the QHP issuers active in a state for a plan year. Use this to build issuer filters in your shopping UI or to validate that an inbound quote request targets a known HIOS issuer ID.

Full URL /v1/reference/issuers

Send the required state query parameter and an optional plan_year. When plan_year is omitted the response defaults to the current ACA plan year — supply it explicitly during open enrollment rollover or when reconciling a prior-year enrollment.

Field Use
state Required two-letter uppercase US state code (e.g. FL, CA, DC). Lowercase values are rejected with 400 invalid_request.
plan_year Optional integer between 2020 and 2099. Defaults to the current ACA plan year.
hios_issuer_id Returned 5-digit HIOS issuer ID to carry into quote filters or downstream lookups.
request.http
GET /v1/reference/issuers?state=FL
x-api-key: YOUR_API_KEY

One entry per HIOS issuer with a sortable display name (issuer marketing name when present, legal name otherwise) and the canonical 5-digit hios_issuer_id. Entries are sorted by name (case-insensitive) and then by hios_issuer_id as a stable tiebreaker, so clients can iterate the array directly without re-sorting.

200 response.json
{
  "issuers": [
    {
      "name": "Gulf Coast Coverage Co.",
      "hios_issuer_id": "33333"
    },
    {
      "name": "Sunshine State Health",
      "hios_issuer_id": "12345"
    }
  ]
}
200
Returns one or more issuers for the state and plan year.
400 invalid_request
state is missing or is not a US state code, or plan_year is not an integer in 2020..2099.
403 forbidden
API Gateway rejected the key as missing, invalid, not active yet, or not authorized for this API, or the request was blocked by an API edge policy.
404 not_found
No active issuers were found for the state in the requested plan year.
429 rate_limited
Too many requests in a short period. Slow down and retry.
500 internal_error
Something unexpected happened on the server. Retry later or contact support if it continues.
404 error.json
{
  "error": {
    "code": "not_found",
    "message": "No issuers found for state FL in plan year 2026."
  }
}
Access requiring our team's approval

Some workflows involve application data, document collection, carrier submission, or hosted consumer enrollment flows. These are not enabled by default. Our team reviews each request, confirms the right setup, and enables access during onboarding.

Sign in to the portal to request access. We will follow up with the next steps for the workflow you need.

On-exchange link

Send shoppers into a hosted HealthSherpa flow.

Start in your system, then send the shopper to HealthSherpa to review plans and complete enrollment. Use POST /v1/enrollment-sessions.

Off-exchange direct enrollment

Submit off-exchange applications directly.

For approved partners, create drafts, fix validation issues, submit applications, handle payment steps, and track status.

POST

/v1/enrollment-sessions

Approval required View in OpenAPI

Use this endpoint to send the browser to a HealthSherpa-hosted enrollment flow for guided shopping, application preparation, and enrollment completion. For the direct-to-consumer flow (context.flow = "self_service"), approval is required and must be requested in the developer portal before use.

Full URL /v1/enrollment-sessions
Idempotency Optional Idempotency-Key header. Keys are treated as unique within a 24-hour window.

The context object is required, as is every field inside it. This keeps product, exchange, coverage, plan year, flow, and locale explicit on every request. Callers must also supply at least one of location.state or top-level plan_id. Unsupported fields anywhere in the body are rejected with 400 invalid_request.

Field Use
context Required. Product, exchange, coverage, plan year, flow, and locale.
plan_id Plan ID (HIOS). Supply when not providing location.state.
external_id Strongly recommended. Partner-supplied identifier echoed in the response and forwarded for CRM correlation. Must not contain PII.
location Optional. Accepts zip_code, fips_code, and state. Address-level fields (address, address_2, city) are rejected.
client Optional contact identity (first_name, last_name, email, phone_number).
household Optional household details and applicants. household_size must be positive; applicants can include at most one primary and one spouse.
enrollment.hra Optional HRA funding (amount, frequency). Other enrollment subobjects are rejected.
campaign Optional marketing attribution (UTM, cid, display_phone_number). Only allowed when context.flow is self_service.

These values are checked when the request is validated. Invalid or unsupported values return 400 invalid_request with field-level details.

context.product
Allowed
aca
context.exchange
Allowed
on_exchange
context.coverage_family + context.coverage_type
Allowed
medical (both fields)
context.plan_year
Range
Integer between 2020 and 2099.
context.flow
agent_assisted
Use when an agent is helping the shopper. The agent must sign in to HealthSherpa in the browser before continuing.
self_service
Use when the shopper is completing the flow without an agent sign-in. Approval for the On-Exchange Direct-to-Consumer Enrollment API is required before using this flow.
context.locale
Allowed
en-US, es-MX
Notes
es-MX renders the hosted page in Spanish.
location.zip_code + location.fips_code
Format
Both fields must be exactly five digits.
Notes
Use GET /v1/reference/counties to resolve a ZIP to county FIPS codes.
location.state
Allowed
Two-letter US state code (50 states plus DC).
Rule
Required when plan_id is not supplied.
household.household_size
Range
Integer greater than or equal to 1.
household.applicants[].relationship
Allowed
primary, spouse, dependent
Notes
Required on every applicant. A household can include at most one primary and one spouse.
household.applicants[].date_of_birth + household.applicants[].age
Rule
date_of_birth and age are mutually exclusive on the same applicant. date_of_birth must not be in the future.
Range
age is an integer between 0 and 130.
household.applicants[].sex
Allowed
female, male
household.applicants[] screening flags
Boolean
true or false
Fields
uses_tobacco, pregnant, parent_caretaker, rejected_by_medicaid_or_chip, unemployment, has_existing_coverage
household.applicants[].ichra
Fields
offered, offered_cafeteria (booleans), employee_amount, family_amount (numbers >= 0).
enrollment.hra.amount + enrollment.hra.frequency
amount
Required when hra is supplied. Number >= 0. 0 means the consumer declined HRA funding; a positive value means funding was accepted.
frequency
Required when amount > 0. Allowed: annually, monthly, quarterly, one_time.
campaign
Allowed
cid, utm_source, utm_medium, utm_campaign, utm_term, utm_content, display_phone_number
Rule
Only accepted when context.flow is self_service. Sending any field with flow: "agent_assisted" is rejected with 400 invalid_request.
Notes
display_phone_number is rendered in the page header. Must be a 10-digit US phone number (e.g. 8005551234, 800-555-1234, (800) 555-1234); +1-prefixed values are rejected with 400 invalid_request.
request.http
POST /v1/enrollment-sessions
x-api-key: YOUR_API_KEY
Content-Type: application/json
Idempotency-Key: 9b1c4f4e-9c2c-4f8f-9a3b-1e2d3a4b5c6d

{
  "external_id": "crm-lead-abc-123",
  "context": {
    "product": "aca",
    "exchange": "on_exchange",
    "coverage_family": "medical",
    "coverage_type": "medical",
    "plan_year": 2026,
    "flow": "agent_assisted",
    "locale": "en-US"
  },
  "client": {
    "first_name": "Jane",
    "last_name": "Doe",
    "email": "jane@example.com",
    "phone_number": "5551234567"
  },
  "plan_id": "12345NY0010001",
  "location": {
    "zip_code": "10001",
    "fips_code": "36061",
    "state": "NY"
  },
  "household": {
    "annual_income": 42000,
    "household_size": 1,
    "applicants": [
      {
        "relationship": "primary",
        "date_of_birth": "1990-01-01",
        "sex": "female",
        "uses_tobacco": false
      }
    ]
  }
}

Successful responses always return links.shopping_url and links.client_apply_url. The external_id field is echoed from the request when supplied; otherwise it is null.

Field Use
external_id Echoed from the request when supplied; otherwise null.
links.shopping_url HealthSherpa public shop URL (https://healthsherpa.com/public/shop?...).
links.client_apply_url HealthSherpa public apply URL (https://healthsherpa.com/public/apply?...). Always present.
200 response.json
{
  "external_id": "crm-lead-abc-123",
  "links": {
    "shopping_url": "https://healthsherpa.com/public/shop?_agent_id=agent_admin_42&external_id=crm-lead-abc-123&fip_code=36061&household_income=42000&household_size=1&people%5Bprimary%5D%5Bage%5D=36&people%5Bprimary%5D%5Bgender%5D=female&people%5Bprimary%5D%5Btobacco%5D=false&state=NY&user_type=agent&year=2026&zip_code=10001",
    "client_apply_url": "https://healthsherpa.com/public/apply?_agent_id=agent_admin_42&external_id=crm-lead-abc-123&fip_code=36061&household_income=42000&household_size=1&people%5Bprimary%5D%5Bage%5D=36&people%5Bprimary%5D%5Bgender%5D=female&people%5Bprimary%5D%5Btobacco%5D=false&plan_hios_id=12345NY0010001&state=NY&user_type=agent&year=2026&zip_code=10001"
  }
}
200
Enrollment session started successfully. URLs are returned in links.
400 invalid_request
Body validation failed. Common causes: missing required context fields, unsupported field anywhere in the body, neither location.state nor plan_id supplied, both date_of_birth and age on the same applicant, or campaign fields with flow: "agent_assisted".
401 unauthorized
Backend runtime authentication failure when a request reaches a runtime that performs its own authentication.
403 forbidden
The key was rejected at the API edge, or the developer's HealthSherpa Marketplace setup is not in the state the requested flow needs. The check is flow-scoped: agent_assisted requires a healthy HealthSherpa Marketplace link with an active OAuth connection and unexpired or refreshable access token; self_service requires approved D2C deep-link access and a configured agent ID.
413 payload_too_large
The request body exceeded the maximum size accepted by the API edge.
415 unsupported_media_type
The request Content-Type is not supported by the endpoint.
429 rate_limited
Too many requests were sent in a short period. Back off and retry after the delay advertised in the Retry-After header.
500 internal_error
Something unexpected happened on the server. Retry later or contact support if it continues.
502 bad_gateway
The API edge received an invalid response from the backend.
503 service_unavailable
The API edge is failing to reach the backend, or an upstream catalog or lookup service is unavailable.
504 gateway_timeout
The API edge did not receive a timely response from the backend.
400 error.json
{
  "error": {
    "code": "invalid_request",
    "message": "Validation failed.",
    "details": {
      "zip_code": [
        "ZIP code must be 5 digits"
      ]
    }
  }
}
Approval required

Off-Exchange Enrollment API

Approved partners can use this workflow to move an off-exchange application from draft to submission. The lifecycle is draft-based: create an application, update it until validation is clean, submit to the carrier, manage payment or post-submission actions, and track final status.

Step Use
Create Open a draft application and attach plan and applicant data.
Validate Inspect the returned errors[] array until the payload is ready.
Submit Send the completed application to the downstream carrier.
Pay Use carrier payment instructions or a payment redirect when supported.
Track Poll status or consume webhook updates after submission.
  • Every create, update, and read response includes an errors[] array.
  • An empty errors[] array means the application is ready to submit.
  • Supporting documents, payment steps, cancellation, and termination are modeled explicitly.
Method Endpoint
POST /api/v1/applicationsCreate a draft application.
PUT /api/v1/applications/:idUpdate a draft application.
GET /api/v1/applications/:idRetrieve details, status, and validation errors.
GET /api/v1/applicationsList applications with pagination and filters.
POST /api/v1/applications/:id/supporting_documentationUpload required documents.
POST /api/v1/applications/:id/submitSubmit the application to the carrier.
POST /api/v1/applications/:id/cancelCancel prior to effectuation.
POST /api/v1/applications/:id/terminateTerminate after effectuation.
GET /api/v1/applications/:id/payment_redirectGet carrier payment form data when redirect payment is supported.
  • Submission confirmation and policy-status events can be delivered by webhook.
  • Payment support varies by carrier and may be inline, redirect-based, or phone-assisted.
  • Environment-specific access details are shared during approval and onboarding.

Public object schemas are intentionally open so the platform can add optional fields over time without forcing a version bump for every enhancement.

  • Ignore response properties they do not recognize.
  • Assume object responses may gain new optional fields over time.
  • Preserve the fields they understand and safely ignore the rest.
  • Use strict serialization or strict deserialization settings that fail on unknown fields.
  • Assume object property order is meaningful.
  • Treat current response shapes as permanently closed or exhaustive.