Korean Saju (사주 / Four Pillars of Destiny — Bazi in Chinese) reads a person’s birth moment as four columns of Heavenly Stems and Earthly Branches. This tutorial converts a plain birthdate into that full chart — pillars, Five Elements balance, Day Master, and zodiac animal — in a single REST call. Clean JSON, free tier, 10 languages.
Many apps already collect a date of birth — at signup, on a profile, in a horoscope feature, or for a
cultural personalization touch. The calculate endpoint turns that single field into a structured
Saju chart you can store, display, or branch logic on. Because it is a deterministic calendar computation,
the same birthdate always returns the same chart, so you compute it once and cache it forever.
The response is small, typed, and language-aware, which makes it easy to render a birth-chart card, seed a personality blurb, or feed downstream features such as compatibility scoring or a daily fortune loop.
Saju is a traditional cultural framework offered for entertainment and self-reflection. The chart is derived from the calendar — it is not a prediction and should not drive medical, financial, legal, or other consequential decisions.
Keys are self-serve. Post an email and you get a free-tier key instantly. The key is shown once — store it as a server-side secret and never ship it in client code.
curl -X POST https://saju-api.pages.dev/api/v1/keys/create \
-H "Content-Type: application/json" \
-d '{ "email": "dev@yourcompany.io" }'
const res = await fetch("https://saju-api.pages.dev/api/v1/keys/create", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ email: "dev@yourcompany.io" })
});
const { api_key } = await res.json();
// store api_key server-side as a secret
import requests
r = requests.post(
"https://saju-api.pages.dev/api/v1/keys/create",
json={"email": "dev@yourcompany.io"},
)
api_key = r.json()["api_key"] # keep this server-side
Free tier returns 100 requests/day at 1 request/second. Authenticate every call with the header
X-API-Key: <your key>.
Post the solar birthdate as integers: year, month, day, an
hour in 24-hour form, and gender as "M" or "F". Pass
lang to localize the labels. The response is the four pillars plus a Five Elements count, the
Day Master, and the zodiac animal.
curl -X POST https://saju-api.pages.dev/api/v1/calculate \
-H "X-API-Key: sajuapi_free_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{
"year": 1990, "month": 5, "day": 15,
"hour": 13, "gender": "M", "lang": "en"
}'
async function chartFromBirthdate(birth) {
const res = await fetch("https://saju-api.pages.dev/api/v1/calculate", {
method: "POST",
headers: {
"X-API-Key": process.env.SAJU_API_KEY,
"Content-Type": "application/json",
},
body: JSON.stringify({ ...birth, lang: "en" }),
});
if (!res.ok) throw new Error("saju_api_" + res.status);
return res.json();
}
const chart = await chartFromBirthdate({
year: 1990, month: 5, day: 15, hour: 13, gender: "M",
});
console.log(chart.day_master.element); // "metal"
console.log(chart.zodiac); // "horse"
import os, requests
def chart_from_birthdate(birth):
r = requests.post(
"https://saju-api.pages.dev/api/v1/calculate",
headers={"X-API-Key": os.environ["SAJU_API_KEY"]},
json={**birth, "lang": "en"},
timeout=10,
)
r.raise_for_status()
return r.json()
chart = chart_from_birthdate(
{"year": 1990, "month": 5, "day": 15, "hour": 13, "gender": "M"}
)
print(chart["day_master"]["element"]) # "metal"
print(chart["zodiac"]) # "horse"
{
"input": { "year": 1990, "month": 5, "day": 15, "hour": 13, "gender": "M", "lang": "en" },
"pillars": {
"year": { "stem": "경", "branch": "오", "stem_hanja": "庚", "branch_hanja": "午" },
"month": { "stem": "신", "branch": "사", "stem_hanja": "辛", "branch_hanja": "巳" },
"day": { "stem": "경", "branch": "진", "stem_hanja": "庚", "branch_hanja": "辰" },
"hour": { "stem": "계", "branch": "미", "stem_hanja": "癸", "branch_hanja": "未" }
},
"elements": { "wood": 0, "fire": 2, "earth": 2, "metal": 3, "water": 1 },
"day_master": { "stem": "경", "element": "metal", "polarity": "yang" },
"zodiac": "horse",
"tier": "free",
"remaining": 99
}
| Field | Shape | Meaning |
|---|---|---|
pillars | 4 objects | The Year, Month, Day, and Hour pillars. Each has a Heavenly Stem (stem) and Earthly Branch (branch) in Korean, plus stem_hanja and branch_hanja for the Chinese characters. |
elements | 5 integers | How many of the chart’s eight characters fall into each of the Five Elements: wood, fire, earth, metal, water. Useful for a balance bar or radar chart. |
day_master | object | The Day Pillar’s stem — the chart’s anchor. Includes its element and polarity (yang or yin). |
zodiac | string | The Earthly-Branch animal of the Year Pillar — the familiar twelve-animal sign (here horse), localized by lang. |
remaining | integer | Requests left in today’s quota for your key. |
The day_master is the single most useful field for lightweight personalization: it is one of ten
stems and pairs cleanly with the daily fortune endpoint,
which keys off the Day Master.
Birth hour is often missing. Send hour: -1 and the endpoint computes the Year, Month, and Day
pillars and returns "hour": null. The Five Elements counts and zodiac are still returned, so you
get a usable chart even without a time.
curl -X POST https://saju-api.pages.dev/api/v1/calculate \
-H "X-API-Key: sajuapi_free_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{ "year": 1985, "month": 11, "day": 3, "hour": -1, "gender": "F" }'
// → "pillars": { ..., "hour": null }, "zodiac": "ox"
Inputs are validated: year is 1920–2050, month 1–12,
day 1–31, hour −1–23, and gender must be
"M" or "F". Out-of-range values return a 400 with an
invalid_input reason.
Because the chart is a pure function of the birthdate, the right pattern is to call the API once — when a user first provides their date of birth — and persist the result. You never need to recompute it for the same person.
// pseudo: enrich a user record on first save of birthdate
async function enrichUser(user) {
if (user.sajuChart) return user; // already cached
const chart = await chartFromBirthdate({
year: user.birthYear, month: user.birthMonth, day: user.birthDay,
hour: user.birthHour ?? -1, gender: user.gender,
});
user.sajuChart = chart; // store; it never changes
return await db.save(user);
}
A date of birth is personal data. Keep the API key on your server, call the API server-side, and store only what your privacy policy covers. The API is stateless — it does not retain the birthdates you send.
Need Ten Gods, hidden stems, the Yongshin (useful element), and the Daeun luck cycles instead of just the bare chart? Switch to the interpret endpoint, which accepts the same birthdate input and returns the full reading.
Pass lang on either endpoint to localize element and zodiac labels. Supported values include
en, ko, ja, zh, es, pt,
id, vi, th, and hi — 10 languages total.
Building a relationship feature? See the dating app compatibility use case. Want a recurring engagement loop? See the daily fortune & horoscope API.