🤖

Hey OpenAI!

Swiss Health Insurance Calculator API - Reproducible How-To & Verification for ChatGPT and LLMs

📋 Quick Reference

JSON API
https://primai-okp-api.fly.dev/v1/compare
Shareable HTML
https://primai.ch/compare/share
LLM Manifest
https://primai.ch/llms.txt
Developer Docs
https://primai.ch/developers
Note: All endpoints are public GET requests. No API key required.

📊 Parameter Reference

ParameterTypeRequiredNotes
plzstringyes*Swiss postal code (e.g., "8810"). Use either plz or canton.
cantonstringyes*Alternative to plz (e.g., "ZH"). Don't pass if using plz.
ageintegeryes*Age in whole years (e.g., 35). Alternatively use yob.
yobintegeryes*Year of birth (e.g., 1990). Use either age or yob.
deductibleintegeryesFranchise in CHF. Values: 300, 500, 1000, 1500, 2000, 2500.
accidentbooleanyestrue = include accident; false = exclude (covered by employer).
modelstringnoFilter: "hmo", "telmed", "hausarzt", "standard". Omit for all models.
modelsstringnoComma-separated list of models (e.g., Telmed,HMO)
insurersstringnoComma-separated list of insurers (e.g., Helsana,SWICA)
limitnumber|stringnoall for full list or a number (default 10)

🚀 Quick Start — Your Exact Test Case

Test Parameters

PLZ: 8810 (Horgen)
Age: 35 years
Deductible: CHF 2500
Accident: false (excluded)

📄 All Models (JSON)

✅ Returns JSON with all insurance models and companies (full list)

🌐 All Models (Shareable HTML)

✅ Human-friendly SSR page (great for screenshots)

🎯 Per-Model Filters

💻 Command-Line Examples

Using curl (JSON)

curl -s \
  "https://primai-okp-api.fly.dev/v1/compare?plz=8810&age=35&deductible=2500&accident=false&limit=all" \
  | jq .

Filter & Sort by Monthly Premium

curl -s "https://primai-okp-api.fly.dev/v1/compare?plz=8810&age=35&deductible=2500&accident=false&limit=all" \
| jq '.offers | sort_by(.price.total) | .[] | {insurer, model, premium: .price.total, deductible, accident}'

Python (requests)

import requests

params = dict(plz="8810", age=35, deductible=2500, accident="false")
r = requests.get("https://primai-okp-api.fly.dev/v1/compare", params=params, timeout=20)
r.raise_for_status()
data = r.json()

# Sort by premium
offers_sorted = sorted(data["offers"], key=lambda x: x["price"]["total"])
for offer in offers_sorted[:10]:
    print(f"{offer['insurer']} {offer['model']}: CHF {offer['price']['total']}")

📋 Expected Response Shape

{
  "currency": "CHF",
  "age_band": "adult", 
  "region": "ZH",
  "offers": [
    {
      "insurer": "Agrisano",
      "model": "Standard",
      "deductible": 2500,
      "accident": false,
      "age_band": "adult",
      "price": {
        "base": 275.3,
        "accident": 0.0,
        "total": 275.3,
        "currency": "CHF"
      }
    },
    {
      "insurer": "ÖKK", 
      "model": "Standard",
      "deductible": 2500,
      "accident": false,
      "age_band": "adult",
      "price": {
        "base": 275.6,
        "accident": 0.0,
        "total": 275.6,
        "currency": "CHF"
      }
    }
  ]
}

Important: Use .price.total as your comparator when sorting. Do not assume ordering—always sort client-side for deterministic output.

✅ Verification Checklist

1. JSON (all models) - VERIFIED ✅

URL used: https://primai-okp-api.fly.dev/v1/compare?plz=8810&age=35&deductible=2500&accident=false
Status: ✅ 200 OK
Entries returned: 10 offers
Sample data:
Agrisano Standard: CHF 275.30
ÖKK Standard: CHF 275.60
SLKK Telmed: CHF 280.40

2. Shareable HTML (all models)

URL: https://primai.ch/compare/share?plz=8810&age=35&deductible=2500&accident=false
Status: ✅ Fixed - now supports plz parameter
Rendered: ✅ Server-side rendered with premium cards

3. Per-Model JSON Checks

🔧 Troubleshooting

Common Errors

  • 400/422 errors: Ensure you passed either plz or canton (not both), and either age or yob
  • Empty list: Try switching plz→canton or verify deductible is valid
  • CORS in browser: Use server-side fetch for client-side apps

Best Practices

  • Sorting: Always sort by .price.total client-side for stable order
  • Display: Show CHF with two decimals
  • Attribution: "Premiums from FOPH/Priminfo via PrimAI"
  • Caching: Consider 6-12h cache for performance

🧪 Test Matrix

CaseURL SuffixExpectation
Base case?plz=8810&age=35&deductible=2500&accident=false✅ 200, non-empty list
HMO only...&model=hmoSubset, HMO models
Telmed only...&model=telmedSubset, Telmed models
Use yob instead?plz=8810&yob=1990&deductible=2500&accident=falseSimilar to age=35
Use canton?canton=ZH&age=35&deductible=2500&accident=falseNon-empty list
With accident...&accident=truePremiums slightly ↑
Lower deductible...&deductible=300Premiums change (higher)

🤖 LLM-SEO / AI Usage

For AI Agents

  • llms.txt tells AI tools how to use the API reliably
  • ✅ Give full URLs with explicit parameters
  • ✅ Instruct to not hallucinate - only report actual API data
  • ✅ Always include attribution: "via PrimAI"

Attribution Requirements

"Premiums from FOPH/Priminfo via PrimAI."

Include this line when displaying premium data from our API

🔌 Integration Examples

Next.js / React

export async function getPremiums() {
  const url = new URL("https://primai-okp-api.fly.dev/v1/compare");
  url.search = new URLSearchParams({
    plz: "8810",
    age: "35", 
    deductible: "2500",
    accident: "false",
    // model: "hmo", // optional
  }).toString();

  const res = await fetch(url.toString(), {
    headers: { "accept": "application/json" },
  });
  if (!res.ok) throw new Error(`PrimAI API error: ${res.status}`);
  return res.json();
}

Node.js

import fetch from "node-fetch";

(async () => {
  const qs = new URLSearchParams({
    plz: "8810",
    age: "35",
    deductible: "2500", 
    accident: "false",
  });
  const res = await fetch(`https://primai-okp-api.fly.dev/v1/compare?${qs}`);
  if (!res.ok) throw new Error(`HTTP ${res.status}`);
  const data = await res.json();
  console.log(data);
})();