Kaal Jyoti APIGet a key

Widgets

Twenty-two web components that put the API in a page with one script tag: a day's panchang and muhurta, a month as a calendar, the planets now, drawn charts, a birth form with a tabbed kundli report, Ashtakoot matching, a horoscope by sign, written readings and eleven birth calculators. No framework and no backend. The calculation stays in the API; the widgets add the requests, a page cache, error states, English and Hindi, and the markup.

The widget catalogue lists all 22 in their four groups with the routes and the credits each one costs. This page is the reference: every attribute, the design system, and the parts that need a server.

The script tag#

<script src="https://cdn.kaaljyoti.com/widgets/v1.js" data-key="kj_pub_…" defer></script>

<kj-panchang city="delhi"></kj-panchang>
<kj-horoscope sign="aries"></kj-horoscope>
<kj-kundli-form></kj-kundli-form>

That is the whole install. v1.js is a loader of about 1 KB: it looks for kj- tags on the page and fetches each widget's code the first time the page has one, so a page with a panchang never downloads the kundli report. With defer, elements already in the markup load as soon as the script runs, and elements added later load when they appear.

The script reads these attributes. Each sets a default for every widget on the page; an attribute on an element wins.

AttributeValueDefault
data-keyA publishable key, kj_pub_…— (required, unless every request is proxied)
data-langen or hien
data-themeauto, light or dark — see modesauto
data-presetclassic, modern, minimal or traditional — see presetsclassic
data-fontinherit for your page's own font — see fontsthe widget's own
data-sign-iconsA sign-icon theme, or your own images — see zodiac sign iconselement
data-powered-byshown or hidden — see powered byhidden
data-time-format12 or 24, for the birth forms' time12
data-rememberoff to stop the forms remembering the last entry on the deviceon
data-place-providerauto, google, photon or kaaljyoti — see place searchauto
data-google-maps-keyYour Google Maps key, for the place search—
data-photon-urlYour own Photon server, for the place searchhttps://photon.komoot.io
data-pricing-urlWhere the monthly-limit and plan cards send a site owner; off for no linkhttps://kaaljyoti.com/api/pricing
data-proxyYour site's proxy endpoint — see the server proxy—
data-proxy-allPresent: send every request through the proxy, for a page that carries no keyabsent
data-proxy-docsWhere the "needs a server connection" note sends a site owner; off for no linkthis page's #proxy
data-pdfbasic, professional, both, or on — the PDF editions your proxy relays— (no PDF button)
data-chunksThe directory the widgets' code is loaded from — see hosting the filesbeside v1.js
data-baseThe API origin, for staging — see staginghttps://api.kaaljyoti.com

The same code is published to npm as @kaaljyoti/widgets, for a page with a bundler — see from npm.

The key and the origin rule#

Create a publishable key in the dashboard and list the origins it will be used from — exactly, including scheme and port: https://example.com, https://www.example.com, http://localhost:3000. A publishable key used from an origin it does not list gets 403 forbidden_origin, and the widget says so in the page, with the origin to add.

The key is meant to be readable; the origin list is what protects it. See authentication.

Attributes every widget takes#

AttributeValueDefault
langen or hithe script's data-lang
themeauto, light or darkthe script's data-theme
presetclassic, modern, minimal or traditionalthe script's data-preset
fontinherit, or system for the widget's ownthe script's data-font
sign-iconselement, glyph, devanagari or customthe script's, or element
headingA title for the card, or off to drop the headerthe widget's own title
framenone to draw the widget without its cardthe card
pricing-urlAs data-pricing-url, for this widgetthe script's
powered-byshown or hiddenthe script's

A place is given one of two ways: city, one of eight bundled ids — delhi, mumbai, kolkata, chennai, bengaluru, hyderabad, jaipur, varanasi — or a lat and lon pair, which wins over city, with an optional IANA timezone (leave it out and the API derives it) and a place label.

A birth is a datetime — the clock on the wall where the birth happened, YYYY-MM-DDTHH:MM:SS, never UTC; see time zones — and a place given the same way. From script, element.birth = { datetime, timezone, latitude, longitude, place } sets them in one go on any widget that takes a birth.

Daily widgets#

A place and a day; no birth. Without a place, the panchang says so and asks for nothing; the other five default to New Delhi.

<kj-panchang> — 1 call#

The day's five limbs as tiles — tithi (with every tithi that touches the day: "until 14:56, then Panchami"), nakshatra and pada, yoga, karana and vara — the masa with the Vikram Samvat year, sunrise and sunset, and the day's windows on a timeline from sunrise to the next sunrise, with the same windows and disha shool as a list under it.

<kj-panchang city="varanasi" lang="hi"></kj-panchang>
The panchang widget for Varanasi in the classic look: tiles for tithi, nakshatra, yoga, karana, weekday, masa, sunrise and sunset, then the day as a timeline with Brahma muhurta, Abhijit, Rahu kaal, Yamaganda and Gulika kaal listed under it.
AttributeValueDefault
city, lat, lon, timezoneThe place, as above—
placeA label for the headingthe city's name
dateYYYY-MM-DD, or todaytoday
showSpace-separated subset of header tithi nakshatra yoga karana sun windows masaall

today sends no date, which the API reads as today at the place — the same civil day wherever the reader is.

<kj-muhurta> — 1 call#

The day's choghadiya and windows (POST /v1/panchang/muhurta): a timeline from sunrise through sunset to the next sunrise with the sixteen choghadiyas on one lane and Rahu kaal, Yamaganda, Gulika and Abhijit on the other; the windows as a list; and the choghadiyas as a table with a Day / Night switch that opens on the half the place is in now, the running one marked. The same place and date attributes as <kj-panchang>, and no show.

It is its own request, so beside a <kj-panchang> for the same place and day it is a second call. Two muhurtas for one place and day are one.

<kj-panchang-month> — 1 call per month shown#

A month as a calendar: one cell per day with the tithi at sunrise and the nakshatra; Purnima, Amavasya and the Ekadashis marked, with a key. A click opens the day's full panchang under the grid — every tithi, nakshatra, yoga and karana that touches the day with its end time, sunrise and sunset, the masa and the Vikram Samvat year. The API gives no festival list, so none is shown.

AttributeValueDefault
monthYYYY-MM; the ‹ › buttons step it, a call per monththis month at the place
masapurnimanta or amanta; the switch flips it, no callpurnimanta
proxyYour site's proxy endpointthe script's

/v1/panchang/month is a heavy route: 20 credits a month shown, and ten requests a minute per key. The widget calls it with the publishable key, or through your proxy when the page has one, which can cache the month.

<kj-calendar> — 2 calls per date#

The Hindu calendar converter. A date in (day, month and year selects; date, default today), and out: its Vikram Samvat year, the masa in the purnimanta and the amanta reckonings, the paksha, the tithi at sunrise and the one after it, the vara and the nakshatra. POST /v1/panchang for the day — shared with a <kj-panchang> for the same place and day — then POST /v1/calendar/vikram-samvat at its sunrise.

<kj-transits> — 2 calls#

"Planets now": the chart of this minute at the place, with its North / South / Circular switch, and a table of each graha's sign, degree, nakshatra and pada and retrograde motion, with the lagna rising there. POST /v1/transit/now and POST /v1/kundli/chart for the same instant; "Refresh" asks again. chart="off" drops the chart and its call; chart-style and size pass to it.

<kj-ephemeris> — 1 call per month and zodiac#

A month of daily longitudes at 00:00 UT: a row a day, a column a graha (and the ascendant at the place), each cell the degree in the sign, the sign's icon where it changes, ℞ while a graha is retrograde; the month's ingresses and stations as a list under it. month (default this month) and system="tropical" (default sidereal; the switch flips it, one call). Heavy, like the month of panchang: 20 credits, ten a minute, and through your proxy when the page has one.

Calculators#

<kj-chart> — 1 call#

A drawn kundli, inlined as SVG so it takes the page's colours.

<kj-chart
  datetime="1990-05-14T10:30:00"
  timezone="Asia/Kolkata"
  lat="28.6139"
  lon="77.209"
  place="New Delhi"
  style="north"
  size="360"
></kj-chart>
AttributeValueDefault
datetimeThe birth's wall clock, YYYY-MM-DDTHH:MM:SS— (required)
lat, lon, timezone, city, placeThe birth place, as above—
stylenorth, south or circularnorth
chart-styleThe same as style, for a page that needs style for CSS—
size200 to 2000 pixels; a value outside is clamped360
vargad1, d9, d10, … d60d1
show-degreesfalse to drop the degree labelstrue

Without a datetime and a place it says there is no birth and asks for nothing.

A language or a style change on a chart is another call. The other widgets get both languages in one answer and repaint when lang changes; a chart's labels are drawn into the SVG by the API, so another language — or another style — is another drawing.

The birth calculators#

Eight calculators here, and three of the reports below (life areas, varshphal and the dasha reading), take one person's birth, two ways:

  • A form (the default): the kundli form's fields without gender — name (optional), date, time and a place search. Nothing is sent until a visitor submits; then the form folds into one line ("Asha · 14 May 1990, 10:30 · Varanasi — Edit details") and the result opens under it. The entry is remembered on the device, so a visitor finds it filled in on the next calculator.
  • Attributes: datetime and lat / lon (or city), timezone, place, and name for the header — or .birth from script. No form; the result at once.

All of them take time-format, remember, place-provider, photon-url, google-maps-key and city (which pre-fills the place) as the kundli form does, and disclaimer, disclaimer-name and disclaimer-url for the readings they draw. Each part of a result has its own state, so a refused request shows its card in its place and the rest stands.

ElementShowsCalls per birth
<kj-moon-sign>The Moon sign and its degree, the janma nakshatra and pada, the nakshatra reading2 — /v1/kundli, /v1/reports/nakshatra; with reading="off", 1
<kj-lagna>The lagna and the ascendant's degree in it, the Moon sign, the lagna reading2 — /v1/kundli, /v1/reports/lagna; with reading="off", 1
<kj-manglik>Manglik or not, the house Mars is in from the lagna, the classical mitigation; the other doshas the chart forms, with their grahas1 — /v1/kundli/yogas
<kj-sade-sati>Whether sade sati or a dhaiya is running now; a calendar year's phases — rising, peak, setting, dhaiya — with Saturn's sign and dates; a stepper1 per year shown — /v1/kundli/sade-sati
<kj-dasha>The running periods, the mahadashas as a band and a table, drill-down to pratyantardasha; a Yogini switch (yogini="off" hides it)1 — /v1/kundli/dasha; Yogini 1 more
<kj-vargas>A select of D1 to D60; the varga's chart and each graha's sign in it beside its D1 sign, vargottama marked. varga (default d9)1 — /v1/kundli/vargas, plus 1 /v1/kundli/chart per varga or style
<kj-kp>Tabs: cusps and planets with sign, star, sub and sub-sub lords; significators by house and by graha; ruling planets. tab opens one first1 — /v1/kp/chart
<kj-strength>Tabs: Shadbala against the required minimum and the six strengths; Ashtakavarga, the SAV square and each graha's BAV1 per tab opened — /v1/kundli/shadbala, /v1/kundli/ashtakavarga

Sade sati is priced as a scan across time — 10 credits a year shown — and the rest at 1 credit a calculation and 5 a reading.

Reports#

Written text an astrologer wrote, in English or Hindi, ending with the disclaimer. Every report is on every plan, at 5 credits. Nothing is scored: there are no percentages, star ratings or lucky numbers, because the API has none and the widgets invent none.

<kj-horoscope> — 1 call per sign, period and day#

For a Moon sign, the horoscope over a period: one summary, then a card for each of five areas — work, money, relationships, health, education — each with a level, Favourable, Mixed or Needs care, and a short text that dates any change inside the period. A reader picks the sign from the twelve, drawn as sign icons, and the period from four tabs (and, for a day, yesterday, today or tomorrow). Each pick writes the element's own attribute, so a page can read back what was chosen.

The horoscope widget before a sign is chosen: twelve sign buttons, Aries to Pisces, each with its symbol on a tile coloured by element — fire, earth, air and water.
AttributeValueDefault
signaries … pisces — preselects the picker— (no call)
perioddaily, weekly, monthly or yearlydaily
dateYYYY-MM-DD, or today; a week runs seven days from ittoday
timezoneThe IANA zone the days are counted inAsia/Kolkata
show-basisPresent: also list the transits behind it, folded awayabsent
disclaimeroff to drop the closing linethe API's line
disclaimer-nameYour astrologer's name, for the closing line—
disclaimer-urlA link shown with the name, http or https—

Without a sign it shows the picker and asks for nothing until one is picked.

<kj-reading> — 1 call#

What a lagna (type="lagna", the default) or a janma nakshatra (type="nakshatra") says about a person — for one the reader picks, one you preset, or a birth's — or one of the personal readings of a birth.

AttributeValueDefault
typelagna, nakshatra, house_lords, grahas, yogas, vimshottari, varshphal, life_areas or kundlilagna
signaries … pisces, for a lagna—
nakshatraashwini … revati (purva_phalguni, …)—
datetime, lat, lon, city, timezone, placeA birth, as on <kj-chart>—
yearThe varshphal's year, from the birthday in it (1800–2400)running
partsFor type="kundli": the readings to include, spaces or commasall eight
disclaimer, disclaimer-name, disclaimer-urlAs on <kj-horoscope>the API's

A preset sign or nakshatra needs no birth, for a "know your lagna" page. With neither a preset nor a birth it shows a picker. The personal readings need a birth — without one they say so and ask for nothing:

typeRouteWhat it draws
house_lordsPOST /v1/reports/house-lordsTwelve houses in order: "1st house · Gemini · lord Mercury in the 9th", then the reading
grahasPOST /v1/reports/grahasNine grahas: "Sun · Aries · 10th house", then what it says in that sign and in that house
yogasPOST /v1/reports/yogasEach yoga that forms: its name, the grahas in it, the text
vimshottariPOST /v1/reports/vimshottariEach mahadasha with its dates, a level and the text; the running one marked
varshphalPOST /v1/reports/varshphalThe year from the birthday in year: a summary, seven areas, and the year's periods as dated chips
life_areasPOST /v1/reports/life-areasThe summary, then a card for each of eleven areas of life, each with a level
kundliPOST /v1/reports/kundliSeveral of the above in one request — parts names them — each drawn as above, with one disclaimer

Each is 5 credits; type="kundli" is 5 per part, so parts="lagna, nakshatra, yogas" is 15 and all eight are 40. Without year the varshphal is the one running now: this year's from the birthday on, last year's before it.

<kj-reading
  type="vimshottari"
  datetime="1990-05-14T10:30:00"
  timezone="Asia/Kolkata"
  lat="28.6139"
  lon="77.209"
></kj-reading>

<kj-life-areas>, <kj-varshphal>, <kj-vimshottari-reading>#

Three readings as birth calculators, with the form in front of them:

ElementShowsCalls per birth
<kj-life-areas>The life-areas reading: one summary, eleven areas with a level each1 — /v1/reports/life-areas
<kj-varshphal>A year selector (year, default the one running now); when the year begins, varsha lagna, muntha and its house, year lord; the varsha kundli; the year's reading3 per year — /v1/varshphal, /v1/kundli/chart, /v1/reports/varshphal
<kj-vimshottari-reading>Each mahadasha from birth to eighty with its dates, level and reading, the running one marked1 — /v1/reports/vimshottari

A varshphal year is 7 credits: two calculations and a reading.

The disclaimer#

Every reading and horoscope ends with "These predictions are indicative. For a reading of your own chart, consult an astrologer." (and its Hindi), small under the text. disclaimer-name="Acharya …", with an optional disclaimer-url, names your own astrologer instead; disclaimer="off" drops it — for an astrologer handing a reading to their own client.

Kundli and match#

<kj-kundli-form> — 3 calls per submit, then per tab#

A birth form and, on submit, a report in tabs.

<kj-kundli-form tabs="overview charts planets dasha" chart-style="south"></kj-kundli-form>
The kundli form after a submit: the birth folded into one line with Edit details, then the report tabs — Overview, Charts, Planets, Dasha, Life areas, Readings — with the overview open: lagna Cancer, Moon sign Sagittarius, nakshatra Purva Ashadha, the running mahadasha and antardasha, the tithi, the yogas in the chart, and the lagna reading.

On submit the form folds into one line — "Asha · 14 May 1990, 10:30 · Varanasi" with an "Edit details" button — and the report opens under it, scrolled into view:

TabShowsCalls, on first open
OverviewLagna, Moon sign, nakshatra and pada, the running mahadasha and antardasha, the tithi, the yogas, the lagna reading3 — /v1/kundli, /v1/kundli/dasha, /v1/reports/lagna
ChartsD1, D9 and the Moon chart, North, South or Circular, and the bhava chalit table1 per chart and style shown; 1 for chalit
PlanetsSign, degree, nakshatra and pada, house; ℞, dignity and vargottama badges2 — /v1/kundli/pace, /v1/kundli/vargas
DashaThe Vimshottari mahadashas and the running one's antardashasnone (the overview's)
Life areasThe life-areas reading1 — /v1/reports/life-areas
ReadingsOnly with readings: the readings it names1 per reading

Only the overview loads on submit; each other tab asks the first time it is opened. A visitor who reads only the overview costs 7 credits — two calculations and a reading. A tab whose request is refused shows its card in its place; the rest of the report is unaffected.

AttributeValueDefault
tabsSpace-separated: overview charts planets dasha life-areasall
showThe first release's spelling: summary is Overview and Planets, chart Charts—
cityA bundled city to pre-fill the place search withnone
time-format12 or 2412
rememberoff to not remember the last entry on the deviceon
chart-style, sizePassed to the chartsas <kj-chart>
readingsAdds a Readings tab: alone, the lagna and nakshatra; or a list of reading typesoff
disclaimer, disclaimer-name, disclaimer-urlPassed to the readingsthe API's line
pdfoff to hide the PDF button here, or a narrower list of editionsthe script's
place-provider, photon-url, google-maps-keyThe place searchauto

readings takes any of lagna, nakshatra, house_lords, grahas, yogas, vimshottari, varshphal and life_areas, spaces or commas, drawn in that order, each 5 credits. With PDFs on, the report has a "Download PDF" bar under the name.

<kj-match-form> — 3 calls per submit#

Ashtakoot guna milan. Two birth fieldsets, bride and groom, each with the kundli form's fields and each remembered separately, side by side on a wide card and stacked on a phone. The result: the total out of 36 as a ring with the verdict (green from 25, gold from 18 to 24, red below 18), each person's Moon sign and nakshatra, the eight kootas with their points, a bar and what each looks at, and each side's Mangal dosha, with a warning when only one has it.

AttributeValueDefault
cityA bundled city to pre-fill both places withnone
time-format, remember, pdfAs on <kj-kundli-form>
place-provider, photon-url, google-maps-keyThe place searchauto

Each submit is one POST /v1/match/ashtakoot with both births and one POST /v1/kundli per person for the Moon signs: 3 credits. Resubmitting the same pair costs nothing, and a lang change repaints from the answers held.

The birth form#

Both forms, and every calculator, ask for a birth the same way:

  • Name and, on the kundli form, gender — both optional; gender is not sent, because the API takes none.
  • Date as day, month and year selects. A real calendar check: 31 February is refused.
  • Time as hour, minute and AM / PM, or 0–23 with time-format="24". AM / PM has no default, because a silent AM is a wrong chart.
  • Place as one search box. After a pick, the place, its coordinates and its time zone (or "worked out from the coordinates") show under it, and "Edit coordinates" opens latitude, longitude and an optional zone.

Each missing field says so under its own group, in English or Hindi, and nothing is sent until the date, the time and the place are all there. A valid submit stores the entry on the device, and the next visit fills it in with a "Clear" link; remember="off" (or data-remember="off") turns that off and forgets what was stored.

The place field searches from the third letter, after a pause in typing. A pick fills the coordinates; "Edit coordinates" lets a visitor type them instead. Where it searches is place-provider on the form or data-place-provider on the script:

ValueSearches
auto (default)Google Places with a Google Maps key, else Photon
googleGoogle Places only; needs a key
photonPhoton only, even with a Google Maps key
kaaljyotiKaal Jyoti's own GET /v1/places only: no third party is asked anything
  • Photon is komoot's free, keyless place search over OpenStreetMap, and it finds villages. Its searches cost no Kaal Jyoti credits, and the list credits "© OpenStreetMap contributors", as OpenStreetMap's licence asks. A busy site can run its own Photon and point photon-url at it.
  • Google Places needs your own key, google-maps-key="AIza…" or data-google-maps-key, with Places API (New) and Maps JavaScript API enabled and restricted to your site. Google bills its searches to your Google account; they cost no Kaal Jyoti credits. The list shows the attribution Google requires.
  • Kaal Jyoti is every town and city of 1,000 people or more, in English or Hindi, with its time zone. Each search is 1 credit.

If Photon or Google fails, is slow or refuses the key, the form goes on with the next one — Google to Photon under auto, and anything to /v1/places — for the rest of the page. A Photon or Google pick sends only coordinates, and the API works out the historical time zone there.

What a visitor types in the place box goes to Photon or Google, whichever searches. Say so in your privacy policy, or set place-provider="kaaljyoti".

What a page costs#

Every widget runs in each visitor's browser, so every call is per page view. The catalogue lists each widget's routes and credits; in short:

  • A calculation is 1 credit — a panchang, a muhurta, a chart, a kundli, a dasha, a match, a place search on /v1/places.
  • A reading or a horoscope is 5. A sade sati year is 10. A month of panchang or ephemeris is 20. A kundli PDF is 1,000 and a match PDF 500.
  • Nothing is called until there is something to show: the forms and calculators until a submit, the horoscope until a sign, a reading until a sign, a nakshatra or a birth.
  • Identical requests on a page are made once, whether the first is still in flight or already answered. Two identical charts are one call, and submitting the same birth again costs nothing.

A refused request costs nothing. An answer from the API's cache costs the same as a fresh one, which is why the page-level memo matters. A page seen ten thousand times a day with one panchang on it is ten thousand credits; if that is your page, render it on your server and cache it — the WordPress plugin does that in its server mode. See Credits per API.

Theming#

Every widget renders into an open shadow root, so your page's dl and table rules cannot take a widget apart — and a widget's rules cannot reach your page. Two ways in are supported: the --kj-* custom properties and ::part().

The card#

Every widget is a card: a header band with a title and a line for the place and date, the body, and a footer with the reading's disclaimer and the powered-by link. heading="Aaj ka panchang" replaces the title and heading="off" drops the header; frame="none" draws the widget without its card — no border, background or footer — for a page that frames it itself.

Presets#

preset on an element, data-preset on the script, picks one of four looks — the same four as the PDF templates, so a site's widgets and reports match:

PresetLook
classic (default)Cream and maroon, serif headings, a thin gold rule
modernTeal and amber, sans headings on a solid accent band
minimalInk on white, hairlines only
traditionalMaroon, saffron and gold; a tinted header with a gold rule and a saffron diamond

A preset sets the accent family, the heading face and the header; the mode below still sets light, dark or auto, and every custom property still wins.

Modes#

themeWhat it looks like
auto (default)Blends in. No card background; text, labels and chart lines take the page's text colour, so it reads on a light page and a dark one. Accent and graha colours lighten on a dark page.
lightThe preset's light card with dark text.
darkA dark card with light text and lightened graha colours.

auto reads the page itself: the widget looks at the text colour it inherits, and light text means a dark page (it sets data-scheme="dark" on itself). Before that is known, CSS light-dark() follows the page's color-scheme, and a browser without it follows the visitor's prefers-color-scheme. A dark site no longer has to declare color-scheme: dark for the widgets to read on it.

data-theme on the script sets a default for every widget that has no theme of its own. A theme written on an element is never replaced, and <kj-kundli-form> passes its theme to the charts it draws.

Fonts#

By default a widget sets its own type: the system UI face for body text, Noto Serif for the classic and traditional headings, and a Devanagari stack (Noto Sans Devanagari, Mukta, …) for Hindi. No web font is loaded. font="inherit" (or data-font="inherit") uses your page's own font for everything, headings included; a Hindi card puts the Devanagari faces behind it, since few Latin faces carry Devanagari. --kj-font and --kj-font-heading win over both.

Size and width#

A widget lays itself out by its own width, with container queries, not by the screen's: in a 300px sidebar it takes the phone layout on a desktop page, and tables too wide for the card turn into one small card per row. The base size is the page's, between 15 and 18px (--kj-size fixes it). On a wide block the card stops at --kj-max-width (72em) and centres itself; readings stop at a reading width inside it.

Custom properties#

Colour and type cross the shadow boundary as custom properties. Set them on the element, on a wrapper or on :root. They win over every mode and preset, so a mode is a starting point, not a lock:

kj-panchang,
kj-chart {
  --kj-bg: #ffffff;
  --kj-text: #1a1a1a;
  --kj-accent: #7a1f2b;
  --kj-font: 'Inter', system-ui, sans-serif;
}
PropertyWhat it colours
--kj-bgThe chart's ground
--kj-surfaceThe widget's own background
--kj-textBody text
--kj-mutedLabels, captions, secondary text
--kj-lineChart rules and table borders
--kj-lagnaThe lagna, and the accent's default
--kj-accentHeadings, tabs and the submit button
--kj-accent-2, --kj-on-accentOrnaments (gold, saffron), and text on the accent
--kj-tint, --kj-border, --kj-shadowTiles and the header band, hairlines, the card's shadow
--kj-planetA graha with no colour of its own
--kj-retroThe ℞ marker
--kj-planet-sun, -moon, -mars, -mercury, -jupiter, -venus, -saturn, -rahu, -ketuOne per graha, in the chart and the planet tables
--kj-good, --kj-badAuspicious and inauspicious windows; --kj-good is also a Favourable level
--kj-careThe Needs care level
--kj-font, --kj-font-headingBody type and heading type
--kj-size, --kj-radius, --kj-max-widthThe base size, the corner radius, and where a card stops growing
--kj-sign-*The sign icons — see zodiac sign icons

The chart's names are the ones the API's own chart SVG declares (see embed recipes), so a chart and the widgets around it take one theme.

Parts#

For structure rather than colour, style the named parts with ::part():

card, header, title, subtitle, body, footer, tiles, tile (each also tile-<name>), label, value (each also <name>-label and <name>-value, e.g. tithi-value), timeline, legend, windows, window, chart, form, submit, result, tabs, tab, panel, segmented, segment, badge, summary, areas, area, planets, yogas, gauge, kootas, loading, error, plan-required, plan-owner, disclaimer, powered-by-row, powered-by.

The newer widgets add collapsed, birth-summary, edit, calendar, day, day-detail, month-masa, marks, stepper, verdict, phases, phase, crumbs, periods, period, band, running, varga-table, cusps, significators, ruling, shadbala, sav, sign-cell, bav, ephemeris, events, transits, proxy-required and proxy-owner. Every sign's icon is sign-icon, with sign-icon-l in the horoscope's picker and sign-icon-m beside a value or a heading.

kj-panchang::part(tithi-value) {
  font-weight: 700;
}

kj-panchang-month::part(day) {
  min-height: 6em;
}

Zodiac sign icons#

Wherever a sign is shown — the horoscope's picker, the kundli overview, the lagna and Moon-sign calculators, the match result, and the planet, transit and varga tables — it carries an icon the widget draws, not a font character, so it looks the same whatever fonts the visitor has. Four themes:

ThemeWhat it draws
element (default)The sign's symbol on a rounded tile, coloured by its element: fire, earth, air or water
glyphThe same symbol in a thin ring, in the accent colour
devanagariThe Hindi name (मेष … मीन) in a double-ring seal
customYour own images

In a table the icon is the compact form: the symbol alone, in its element's colour. sign-icons on an element (or any element around it) chooses the theme there; data-sign-icons on the script sets it for the page.

Your own images are a URL template with {sign}, which becomes aries … pisces, or a map:

<script
  src="https://cdn.kaaljyoti.com/widgets/v1.js"
  data-key="kj_pub_…"
  data-sign-icons="https://example.com/signs/{sign}.png"
  defer
></script>
KJWidgets.configure({
  signIcons: { aries: 'https://example.com/ram.webp', taurus: 'https://example.com/bull.webp' },
});

A map may also name the page's default theme as theme — { theme: 'glyph', aries: … } — and the images are then used only where an element asks for sign-icons="custom". Each image is drawn as an <img> with the sign's name as its alt text, never inlined, so an SVG of yours cannot put markup or script into the widget. Only https:// URLs and paths on your own site are used; anything else is ignored. A sign the template or map does not give, or an image that fails to load, falls back to the element icon. Square images look best: about 48px in the picker and 20–32px elsewhere.

Colours are custom properties like any other:

PropertyWhat it colours
--kj-sign-fire-bg, --kj-sign-earth-bg, --kj-sign-air-bg, --kj-sign-water-bgThe tile behind an element icon
--kj-sign-fire-circle, -earth-circle, -air-circle, -water-circleIts soft inner circle
--kj-sign-fire-ink, -earth-ink, -air-ink, -water-inkThe symbol's stroke, the seal's rings and name
--kj-sign-glyph, --kj-sign-ringThe glyph theme's symbol and its ring

The defaults follow the preset and the mode: traditional leans to saffron and gold, minimal is one ink with no tint, and a dark card gets deep tiles with light strokes.

A styling guide#

  • One brand colour: set --kj-accent alone. Tints, rules, the selected tab and the text on buttons are worked out from it.
  • Your font: font="inherit", or --kj-font for a specific stack.
  • A dark site: leave theme unset; auto switches to its dark hues on a dark page. Or set theme="dark" for an opaque dark card.
  • Frame it yourself: frame="none", and heading="off" for the header.
kj-panchang-month,
kj-dasha {
  --kj-accent: #0f766e;
  --kj-radius: 6px;
}

kj-kundli-form::part(card) {
  box-shadow: none;
  border-width: 2px;
}

Powered by#

A "Powered by Kaal Jyoti" link under a widget is opt-in, on every plan. Add data-powered-by="shown" to the script tag to show it under every widget, or powered-by="shown" to one widget; powered-by="hidden" leaves it out of one widget when the script shows it. Without either, there is no link.

States#

A widget never shows a raw error. Each state is a small card inside the widget:

StateWhenWhat it says
LoadingA request is in flightA shimmer shaped like the widget
No place, no birthNothing to ask for yetA line saying so; nothing is sent
Monthly limit reached402 quota_exceeded: the account's credits for the month are used up"Monthly limit reached", and a small line for the site owner linking pricing-url to add credits or upgrade
Available on a paid plan403 plan_required: a PDF on the Free planThat PDFs need a paid plan, with the same owner line
Needs a server connectionA route closed to publishable keys, and no proxy on the pageThat the widget needs the WordPress plugin or a server proxy, with a link for the owner (proxy-docs)
ErrorAnything elseOne translated line. forbidden_origin names the origin to add; a 429 is retried once after Retry-After

Inside the kundli report a state fills only the tab whose request was refused.

Events#

All three bubble and cross the shadow boundary, so one listener on document hears every widget.

EventFired bydetail
kj-readyEvery widget, after its main answerThe response data
kj-errorEvery widget, after a failurecode, status, message, requestId
kj-submit<kj-kundli-form> and the birth calculatorsThe birth: datetime, timezone, latitude, longitude, place
kj-submit<kj-match-form>bride and groom, each a birth as above
document.addEventListener('kj-error', (event) => {
  console.warn(event.detail.code, event.detail.requestId);
});

The code is one of the API's — see errors.

Languages#

English (en) and Hindi (hi). The labels, the error lines and every name the answer carries — tithi, nakshatra, yoga, karana, paksha, weekday, the lunar month, the signs, and the readings' text — are shown in the language asked for. Set it for the page with data-lang or per widget with lang. A widget can be switched after it has drawn: requests ask for both languages, so only a chart, whose labels are drawn into the SVG, spends a call to do it.

The server proxy

Some routes are closed to publishable keys whatever the plan — the PDFs, /v1/transit/scan and /v1/match/batch. A widget that needs one sends its request to your site's own endpoint, which calls the API with your secret key. The month routes are open to publishable keys, but the two month widgets use the proxy when the page has one, so your site can cache each month.

<script
  src="https://cdn.kaaljyoti.com/widgets/v1.js"
  data-key="kj_pub_…"
  data-proxy="/wp-json/kaaljyoti/v1/proxy"
  defer
></script>

data-proxy on the script, or proxy on an element, is an https:// URL or a path on your site. The WordPress plugin provides one. To write your own:

  • The request. POST {proxy} with Content-Type: application/json and no key. The body is {"path": "/panchang/month", "body": {…}}: path is the API route after /v1, and body the JSON the widget would have sent. The page's cookies go with it (credentials: same-origin).
  • What your proxy must do. Allow only the paths it means to serve — the PDFs, the month routes it caches — and refuse anything else. Refuse a body over 16 KB. Call POST https://api.kaaljyoti.com/v1{path} with Authorization: Bearer kj_live_… (never the key in the query), and pass the widget's X-KJ-Client header through. Rate-limit each visitor: the heavy routes cost your credits, and the API's ten-a-minute limit is per key, shared by every visitor. It may cache a month's answer by path and body; twelve hours is safe.
  • The response. Relay the API's status code and JSON body as they are, with X-KJ-Plan, X-KJ-Request-Id and Retry-After when present. The widget reads it exactly as a direct answer, so a 402 quota_exceeded becomes the monthly-limit card. Never put X-KJ-Credits-Remaining in a page: it is the account's balance.
  • Your own refusals use the same envelope: {"status": "error", "error": {"code": "proxy_path_not_allowed", "message": "…"}} with 403, proxy_rate_limited with 429 and Retry-After, or proxy_error with 502.
  • A nonce is optional. The endpoint is read-only and allowlisted, and a nonce would break page caching; if you want one, put it in the URL you write into data-proxy, and the widget sends that URL as given.

data-proxy-all sends every request through the proxy, for a page that carries no key at all. A widget on a closed route with no proxy shows "Needs a server connection" and sends nothing.

PDF downloads#

The kundli report and the match result can offer a printable PDF (/v1/pdf/kundli, /v1/pdf/match). A PDF is never made for a publishable key, so it always goes through your proxy, and the proxy has to say it relays them: data-pdf="basic professional" (or basic, professional, or on for basic) names the kundli editions your site offers.

With that, the kundli report (under its name) and the match result (under its score) show a "Download PDF" bar: the edition when there is a choice, the language, and the button. The widget sends {"path": "/pdf/kundli", "body": {birth, options: {language}, edition, name, template, chart_style}} — template is the widget's preset, since the four presets are the four PDF templates — and saves the file under the name the API gave it. The PDF carries your account's branding from the dashboard; the page cannot change it. pdf="off" on an element hides the bar there, and a list narrows the editions.

A kundli PDF costs 1,000 credits and a match PDF 500, and each uses one PDF of the month's allowance: Starter 50, Growth 200, Scale 500, Enterprise 2,500. PDFs are on every paid plan and not on Free; a site on Free gets the "Available on a paid plan" card, and one past its PDFs for the month a line saying so. See PDFs.

From npm#

Not on npm yet. @kaaljyoti/widgets is published to npm with the next widgets release; until then, use the script tag. The code below is how it will be used.

npm install @kaaljyoti/widgets
import { configure, define } from '@kaaljyoti/widgets';

configure({ key: 'kj_pub_…', lang: 'en', theme: 'auto', preset: 'classic' });
define(); // registers all 22; safe to call twice

Importing the module registers nothing until define() is called, so a bundler or a framework stays in control, and on a server render it evaluates without touching customElements. Every script attribute has a configure() option of the same name in camelCase: signIcons, placeProvider, googleMapsKey, proxyUrl, pricingUrl, timeFormat, remember, pdf, baseUrl. The 22 element classes (KjPanchang, KjKundliForm, KjHoroscope, …) are exported as well.

Versions and hosting#

Which file to load#

https://cdn.kaaljyoti.com/widgets/v1.js is the rolling loader: each new release replaces it, so a site on v1.js gets fixes and new widgets without changing its tag. A breaking change would get a new name, not a new v1.js. Browsers cache it for five minutes.

A site that wants to choose when it upgrades loads a pinned copy instead: each release also publishes https://cdn.kaaljyoti.com/widgets/v<version>.js, named with the widgets' full version number. A pinned file never changes once published, and the widget code it loads is never deleted, so a pinned page keeps working exactly as it is until you change the tag.

Hosting the files#

v1.js loads each widget's code from the directory it was loaded from, the first time the page has one of its tags. To serve the widgets from your own site, copy the loader and every chunk the build lists in cdn-files.json into one directory, flat. The chunks are ES modules, fetched with CORS: serve them from the page's own origin, or send Access-Control-Allow-Origin with them. If a script optimiser moves or combines v1.js, put the directory on the tag as data-chunks="https://example.com/path/to/widgets/". The WordPress plugin ships the files this way.

From script, KJWidgets.define() loads every widget at once, and KJWidgets.load('kj-chart') loads one — for a tag inside another shadow root, which the loader cannot see.

Staging#

One bundle, one attribute:

<script
  src="https://cdn.kaaljyoti.com/widgets/v1.js"
  data-key="kj_pub_…"
  data-base="https://api-staging.kaaljyoti.com"
  defer
></script>

Give an origin only. /v1 is added for you, and a trailing /v1 you paste in is removed. From npm it is configure({ baseUrl }).

What it never does#

  • It never uses a secret key. Only kj_pub_… works from a page. The API refuses kj_live_… and kj_test_… in a query string, and the widgets never send an Authorization header.
  • It never calls a closed route with your publishable key. The PDFs, the transit scan and the batch match go through your proxy or not at all. The month routes are open to publishable keys and are called directly when there is no proxy.
  • It never asks for embed_font. The chart is drawn with the web font, which is what a browser wants.
  • It never passes a time through new Date(). Every clock in an answer is a wall clock at the place; the widgets slice the string, so a reader in another zone sees the same sunrise.
  • It never scores. A horoscope or a reading is text; nothing turns it into a percentage, a star rating or a lucky number.
  • It never inserts answer text as markup. The only markup it inserts is the chart SVG the API drew.

MIT licensed. Source, issues and pull requests: kaaljyoti-integrations.