Computing Korean Saju (사주 / Four Pillars of Destiny — Bazi in Chinese) in code usually means
pulling in a lunar-calendar package, a Bazi calculator, and reconciling their solar-term tables. This API
does the calendar math, true-solar-term boundaries, and chart logic on the server, so your client is a
thin fetch. Copy a wrapper below and you have working Four Pillars in minutes.
lang and labels come back localized —
no translation layer in your app.Base URL: https://saju-api.pages.dev · Auth header:
X-API-Key: <your key>. Grab a free key from the
quickstart (100 requests/day, no card).
One small client that covers every endpoint. Keep the key in an environment variable on your server, never in client-side code.
// saju.js — works in modern browsers, Deno, and Node 18+ (built-in fetch)
const BASE = "https://saju-api.pages.dev";
function client(apiKey) {
async function post(path, body) {
const res = await fetch(BASE + path, {
method: "POST",
headers: { "X-API-Key": apiKey, "Content-Type": "application/json" },
body: JSON.stringify(body),
});
if (!res.ok) throw new Error("saju_api_" + res.status);
return res.json();
}
async function get(path, params) {
const qs = new URLSearchParams(params).toString();
const res = await fetch(BASE + path + "?" + qs, {
headers: { "X-API-Key": apiKey },
});
if (!res.ok) throw new Error("saju_api_" + res.status);
return res.json();
}
return {
calculate: (b) => post("/api/v1/calculate", b),
interpret: (b) => post("/api/v1/interpret", b),
compatibility: (b) => post("/api/v1/compatibility", b),
daily: (p) => get("/api/v1/daily", p),
};
}
const saju = client(process.env.SAJU_API_KEY);
const chart = await saju.calculate({
year: 1990, month: 5, day: 15, hour: 13, gender: "M", lang: "en",
});
console.log(chart.day_master); // { stem, element, polarity }
# saju.py — requires: pip install requests
import os, requests
BASE = "https://saju-api.pages.dev"
class Saju:
def __init__(self, api_key):
self.s = requests.Session()
self.s.headers["X-API-Key"] = api_key
def _post(self, path, body):
r = self.s.post(BASE + path, json=body, timeout=10)
r.raise_for_status()
return r.json()
def _get(self, path, params):
r = self.s.get(BASE + path, params=params, timeout=10)
r.raise_for_status()
return r.json()
def calculate(self, **b): return self._post("/api/v1/calculate", b)
def interpret(self, **b): return self._post("/api/v1/interpret", b)
def compatibility(self, **b): return self._post("/api/v1/compatibility", b)
def daily(self, **p): return self._get("/api/v1/daily", p)
saju = Saju(os.environ["SAJU_API_KEY"])
chart = saju.calculate(year=1990, month=5, day=15, hour=13, gender="M", lang="en")
print(chart["day_master"]) # {'stem': ..., 'element': 'metal', 'polarity': 'yang'}
// saju.cjs — CommonJS, Node 18+ (global fetch)
const BASE = "https://saju-api.pages.dev";
function createClient(apiKey) {
async function post(path, body) {
const res = await fetch(BASE + path, {
method: "POST",
headers: { "X-API-Key": apiKey, "Content-Type": "application/json" },
body: JSON.stringify(body),
});
if (!res.ok) throw new Error("saju_api_" + res.status);
return res.json();
}
return {
calculate: (b) => post("/api/v1/calculate", b),
interpret: (b) => post("/api/v1/interpret", b),
compatibility: (b) => post("/api/v1/compatibility", b),
};
}
module.exports = { createClient };
// usage:
// const { createClient } = require("./saju.cjs");
// const saju = createClient(process.env.SAJU_API_KEY);
// const chart = await saju.calculate({ year:1990, month:5, day:15, hour:13, gender:"M" });
// saju.go
package saju
import (
"bytes"
"encoding/json"
"net/http"
)
const base = "https://saju-api.pages.dev"
type Client struct{ key string }
func New(apiKey string) *Client { return &Client{key: apiKey} }
func (c *Client) post(path string, body any, out any) error {
b, _ := json.Marshal(body)
req, _ := http.NewRequest("POST", base+path, bytes.NewReader(b))
req.Header.Set("X-API-Key", c.key)
req.Header.Set("Content-Type", "application/json")
res, err := http.DefaultClient.Do(req)
if err != nil {
return err
}
defer res.Body.Close()
return json.NewDecoder(res.Body).Decode(out)
}
func (c *Client) Calculate(body map[string]any) (map[string]any, error) {
var out map[string]any
err := c.post("/api/v1/calculate", body, &out)
return out, err
}
| Endpoint | Method | Returns |
|---|---|---|
/api/v1/calculate | POST | Four Pillars (stems & branches), Five Elements distribution, Day Master, zodiac. |
/api/v1/interpret | POST | Ten Gods, 12 Life Stages, hidden stems, branch interactions, Yongshin, Daeun (luck cycles). |
/api/v1/compatibility | POST | A 0–100 compatibility score with breakdown for two charts. |
/api/v1/daily | GET | A daily snapshot for a Day Master on a given date. |
POST endpoints take { year, month, day, hour, gender, lang } —
gender is "M" or "F", hour is a 24-hour integer
(-1 when the birth time is unknown). The daily endpoint takes query params
day_master, date (YYYY-MM-DD), and lang.
This is what one calculate call returns — everything a library would compute,
already shaped as JSON:
{
"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": 98
}
Errors return a JSON body with an error code and, for validation failures, a
reason. Watch for 429 when you exceed the free-tier rate — back off and retry.
| Status | Meaning |
|---|---|
400 | Invalid input — see reason (e.g. year_out_of_range, gender_must_be_M_or_F). |
401 | Missing or invalid API key. |
429 | Rate limit reached for your tier. Retry after a short delay. |
Supported dates run roughly 1920–2050 (solar). Always send solar (Gregorian) dates — the server resolves the lunar and solar-term details for you.
Prefer a spec? Grab the OpenAPI file or the Postman collection and generate a client in your stack.