Skip to main content

Calling from a browser

Short version: don't. Call the API from your server and pass the data to your frontend.

Why a direct fetch fails

Two independent reasons:

1. CORS is off by default. The endpoint sends no Access-Control-Allow-Origin header unless a specific origin has been configured. Without it, the browser blocks the response. You will see a CORS error in the console even though the request itself succeeded — the server responded, the browser refused to hand it over.

2. The key would be public. Any key in browser JavaScript is readable by anyone who opens DevTools. Since keys are currently shared across all partners (Authentication explains why), leaking one is worse than leaking a key scoped to you alone.

The second reason is the important one. Even with CORS enabled, calling directly from a browser is the wrong design.

The proxy pattern

Put a thin endpoint on your own server. It holds the key, calls Kamee, caches the result, and serves your frontend from the same origin — so no CORS is involved at all.

// GET /api/kamee-plans on your own server
const ONE_HOUR = 60 * 60 * 1000;
let cache = {plans: null, fetchedAt: 0};

export async function handler(req, res) {
if (!cache.plans || Date.now() - cache.fetchedAt > ONE_HOUR) {
try {
const upstream = await fetch('https://kamee.fit/api/plans', {
headers: {'X-Api-Key': process.env.KAMEE_API_KEY},
});

if (upstream.ok) {
const {plans} = await upstream.json();
cache = {plans, fetchedAt: Date.now()};
}
} catch {
// Network failure. Fall through to whatever is cached.
}

if (!cache.plans) {
return res.status(502).json({error: 'Upstream unavailable'});
}
}

res.setHeader('Cache-Control', 'public, max-age=300');
return res.json({plans: cache.plans});
}

Your frontend then calls /api/kamee-plans — same origin, no key, no CORS.

This also puts you in control: you can reshape the payload, drop fields you do not use, and keep serving your cached copy when the upstream has a bad minute.

If you genuinely need direct browser access

We can enable CORS for a specific origin. It is a server-side configuration change on our end, so contact PLACEHOLDER_CONTACT with the exact origin.

Be aware of what this does and does not solve: it makes the browser accept the response, but it does nothing about the exposed key. We will want to talk about your use case first.

Also be aware this is a single setting for the whole API, not a per-partner allowlist: there is exactly one allowed origin, so enabling it for you turns it off for anyone who had it before. Talk to us before building on it.

Preflight

OPTIONS returns 204. It includes Access-Control-Allow-Methods: GET, OPTIONS and Access-Control-Allow-Headers: X-Api-Key — but only when an origin has been configured. By default the preflight succeeds with no CORS headers, and the browser blocks the real request.