Kaal Jyoti APIGet a key

Authentication

Every request carries a key. There are three kinds and the difference is not cosmetic — it decides where the key may travel and what it may call.

The three kinds#

PrefixKindWhere it goesWhat it may call
kj_live_…LiveYour server. Authorization: Bearer only.Everything your plan includes.
kj_test_…TestYour server, staging, CI. Authorization: Bearer only.The same as live. Separate key, same quota — easy to revoke on its own.
kj_pub_…PublishableA web page. It is checked against the browser's Origin and capped per day (below). May travel as ?key=….Every widget route; not batch matching, the transit scan, PDFs or embed_font on a chart.

A key is its prefix plus 43 random base62 characters — about 256 bits. We store only its SHA-256 and the first twelve characters, so the dashboard can tell you which key is which and a stolen database dump cannot call the API. You see a key once, when you create it. Lose it and you make a new one.

Sending a key#

On a server, always the header:

curl -s https://api.kaaljyoti.com/v1/kundli \
  -H 'Content-Type: application/json' \
  -H "Authorization: Bearer $KAALJYOTI_API_KEY" \
  -d '{"birth": {"datetime": "1990-05-14T10:30:00", "timezone": "Asia/Kolkata",
                 "latitude": 28.6139, "longitude": 77.2090}}'

From a browser, the publishable key may go in the query string, because a browser cannot keep a secret anyway:

await fetch(`https://api.kaaljyoti.com/v1/panchang?key=${PUBLISHABLE_KEY}`, {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({ latitude: 28.6139, longitude: 77.209, timezone: 'Asia/Kolkata' }),
});

A secret key sent as ?key=… is refused outright, even if it is otherwise valid. A URL ends up in server logs, browser history, proxy caches and referrer headers; a live key that has been in one is a live key that has leaked. The API would rather fail your request than let that happen quietly.

Origins, and what they do and do not protect#

A publishable key is only accepted from an origin it lists. You give the list when you create the key — https://example.com, https://app.example.com — and the browser sends Origin on every request without being asked. An origin that is not on the list gets 403 forbidden_origin.

The match is exact, and * is not a wildcard: a publishable key that worked from anywhere would just be a secret published on purpose. Origins are matched case-insensitively, and scheme and port are part of the origin, so http://localhost:3000 and https://localhost:3000 are two different entries.

The origin check is a browser control, not authentication. A browser cannot lie about Origin, so another website cannot use your key in its pages. But a publishable key is in your page's source, and anyone who copies it into a script can send it with whatever Origin header they like. Treat a publishable key as public, and rely on its limits for what it can cost.

What limits the cost of a publishable key#

  • A daily credit cap per key. By default a tenth of your plan's monthly credits per UTC day; you can set your own number for each key on the dashboard's Sites page. Over it the key gets 402 quota_exceeded — the message says it is the key's daily cap — until 00:00 UTC, and we e-mail you once that day. Your other keys and the rest of your credits are untouched.
  • No credit packs unless you allow them for that key on the Sites page. A publishable key spends the period's credits only, so a copied key cannot reach credits you paid for in advance.
  • A per-minute limit per site, counted at our edge in front of every server, and a tighter one on the routes that cost five credits or more a call (readings and the kundli report, the horoscope, the month-long panchang and ephemeris, transit events, kundli events and sade-sati).
  • An e-mail on unusual use: a key whose day is five times its average over the week before, and at least 1,000 credits.
  • It cannot call the batch or the scan (/v1/match/batch, /v1/transit/scan) or the PDFs, and cannot ask for embed_font: true on a chart.

If you see traffic you do not recognise, revoke the key and create a new one with the same origins. The caps limit the damage; they do not stop someone spending the key's daily cap every day until you rotate it.

Rate limits are per site, not only per key#

A publishable key is on a page, and every visitor of that page is using it. So a publishable key is measured twice: each Origin it is used from gets its own per-minute allowance, and the key as a whole still cannot exceed your plan's req_per_min.

One busy site can therefore no longer spend the whole key's minute and leave your other sites with nothing — but the key-wide rate is still the ceiling, and it is the one your plan quotes. Over either, the answer is the same 429 rate_limited with a Retry-After; the message says which of the two refused. A publishable key that arrives without an Origin at all — a curl, a native app — is refused as forbidden_origin before either bucket is consulted. The per-site count is kept at our edge as well as on each server, so it does not grow as the API scales out.

How many keys#

The free plan allows one key of each kind at a time — one live, one test, one publishable. Revoke one to make another. Paid plans allow up to fifty of each, which is more than anyone needs and exists only so a loop cannot fill a table.

Rotating and revoking#

Revoke a key in the dashboard and it stops working — within about a minute. The gateway caches a key's record for sixty seconds so that a customer looping on one key is not a database query per request; the trade is that a revoked key may answer for up to a minute after you revoke it. Plan a rotation as: create the new key, deploy it, then revoke the old one.

If a key has leaked, revoke first and deploy second. A minute of overlap is better than a day of exposure.

What the API never sees#

The key is the only credential. There is no account password, no OAuth dance and no session: the API does not know who you are beyond which key you used and which account it belongs to. And what you send it — a birth, a place, a date — is used to compute an answer and never stored: bodies are not logged, and the response cache is keyed on a hash. (The two GET lookups, /v1/timezone and /v1/places, take their input in the URL, and URLs are in the access logs for up to 30 days.)

Errors you may see#

CodeStatusMeans
invalid_key401No key, a key we do not know, or a secret key in the query string.
key_revoked401The key existed and was revoked.
forbidden_origin403A publishable key from an origin it does not list.
plan_required403The key's kind does not cover this endpoint, or a PDF on Free.

Each of them is described on the errors page, and none of them costs a credit.