Documentation
Key restrictions
Scopes, publishable keys for browser widgets, and IP allow-lists. When to use a publishable key and when a secret key.
Every key can be narrowed so that a leaked or misused key can do less. Restrictions are set when you create a key under API keys in the dashboard, and can be changed later; a change applies to the key's next call. A refused call is answered with 403 and is never charged, and nothing is counted against your rate limit or credits.
| Restriction | Applies to | Refused with |
|---|---|---|
| Scopes | Secret and publishable keys | 403 key_scope_denied |
| Allowed origins | Publishable keys | 403 origin_not_allowed |
| IP allow-list | Secret keys | 403 ip_not_allowed |
Keys created before restrictions existed have none: they can call every capability from anywhere, exactly as before.
Secret or publishable?
Secret key (sk_test_, sk_live_) | Publishable key (pk_test_, pk_live_) | |
|---|---|---|
| Where it lives | Your server only | Inside your web page, visible to every visitor |
| Capabilities | All, or the scopes you choose | Only widget capabilities (below) |
| Restricted by | Optional IP allow-list | Required list of allowed websites (origins) |
| Rate limit | Your plan's units per second | Its own units-per-second allowance, plus 30 calls a minute per visitor IP |
| PDF and audio jobs, job polling, conversations | Yes | No (publishable_key_not_allowed) |
X-Credits-Remaining header | Live keys | Never: a browser does not see your balance |
| CORS (callable from a browser) | No | Yes, from its allowed origins only |
Use a secret key for anything server-side: your backend, scripts, mobile app backends, reports, PDFs, audio, photo and AI readings. Never ship it in a browser, app bundle or theme.
Use a publishable key only for a small widget on your own website that calls the API straight from the visitor's browser: today's panchang, a daily horoscope, a basic kundli, a quick match. It is not a secret. Anyone can copy it from your page, so it can do only cheap, read-only widget calls, only from your websites, and only a few times a minute per visitor. Its calls still spend your credits (live) or count toward the sandbox limits (test).
Scopes
A scope list limits a key to some capability families (for example panchang or matching) and, optionally, single capabilities written as family/capability (for example predictions/sign-horoscope-daily). An empty list means every capability.
panchang
matching/quick-match
predictions/sign-horoscope-dailyWith these scopes the key can call every panchang capability, the quick match and the daily horoscope, and nothing else. Anything else answers:
{
"error": {
"code": "key_scope_denied",
"message": "This key is not allowed to call /v1/reports/report-marriage. Widen the key's scopes in the dashboard (API keys) or use another key.",
"field": null,
"request_id": "req_…",
"retryable": false
}
}Scopes are checked right after the key, before the rate limit, validation or any credit reservation. They also apply to POST /v1/jobs/pdf: the report being rendered must be in scope.
Publishable keys
When you create a publishable key you choose:
-
Allowed origins: the websites that may use it. Each entry is a host, optionally with a scheme and port:
Entry Allows www.example.comhttps://www.example.comonly*.example.comAny subdomain over https ( https://app.example.com,https://a.b.example.com), but notexample.comitself: add it separatelyhttps://shop.example.com:8443That exact scheme, host and port http://staging.example.comThat host over plain http localhosthttp://orhttps://localhoston any port, for developmentWithout a scheme only https is allowed (except
localhost,127.0.0.1and[::1]). The browser'sOriginheader is checked, or theRefererwhen there is noOrigin; a request with neither (a server or script) is refused withorigin_not_allowed. A script can forge these headers, which is why publishable keys are limited to widget capabilities and per-visitor rate limits: use a secret key for anything that matters. -
Capabilities, from the widget list: daily panchang and its parts (five limbs, sunrise and moonrise, Rahu Kaal and auspicious periods, choghadiya, hora, festivals of the day), daily, weekly and monthly sign horoscopes, kundli basics (planet positions, birth summary, and the chart drawing as SVG:
vedic-charts/chart-image-styles), quick, Ashtakoot and Dashakoot matching, place search and the Moon sign, Sun sign and ascendant finders. AI chat can be added too, but only when you switch it on for that key: each reading spends real credits, so set it only for a widget that needs it.
Responses to an allowed origin carry Access-Control-Allow-Origin with that origin, and the browser's CORS preflight is answered for widget capabilities from any origin one of your publishable keys allows. Other origins get no CORS headers, so the browser blocks them.
<div id="panchang"></div>
<script>
// A publishable key restricted to this website. Not a secret.
fetch("https://api.96astroapi.com/v1/panchang/daily-panchang", {
method: "POST",
headers: {
"Authorization": "Bearer pk_live_…",
"Content-Type": "application/json"
},
body: JSON.stringify({ date: "2026-10-27", latitude: 28.6139, longitude: 77.209, timezone: "Asia/Kolkata" })
})
.then((r) => r.json())
.then((body) => { document.getElementById("panchang").textContent = JSON.stringify(body.data); });
</script>Rate limits for publishable keys
- Their calls count in a separate units-per-second allowance of your plan's size, so a busy widget cannot slow your server-side integration (and creating more publishable keys does not add to it).
- Each key also allows 30 calls a minute per visitor IP address; past that, the visitor gets
429 rate_limitedwithRetry-After.
IP allow-lists
A secret key can be limited to your servers' addresses: a list of IPv4 or IPv6 addresses and CIDR ranges, such as 203.0.113.4, 198.51.100.0/24 or 2001:db8::/48 (up to 100 entries). A call from any other address answers 403 ip_not_allowed, which names the address we saw, so you can tell which one to add. An empty list allows every address.
The address is the one that connects to our edge; forwarded headers your client sends are ignored, so they cannot be used to get around the list. If your servers reach the internet through a NAT gateway or proxy, allow-list its public address. The dashboard's playground calls from our web server, so a key with an IP allow-list is refused there; our hosted MCP server passes on the address of your MCP client, which must be in the list.


