Build a daily fortune feature — Korean Saju, not sun-signs.

Most "daily horoscope" APIs return one of twelve sun-sign blurbs. The Saju API daily endpoint returns a Korean Four Pillars snapshot keyed on the Day Master — a 0–100 score and an action / planning / rest advice key, ready for push notifications, home-screen widgets, and cron jobs. One cacheable call per Day Master per date. Free tier, no credit card, 10 languages.

1Why Day Master beats sun-sign for a fortune feed

In Korean Saju, your daily reading hangs off your Day Master — the Heavenly Stem of your birth day, one of ten (갑/을/병/정/무/기/경/신/임/계, i.e. yin & yang of wood, fire, earth, metal, water). The daily endpoint scores how a given calendar day’s pillar interacts with each Day Master, so the result is a culturally grounded reading rather than a generic Western zodiac line.

Don’t know a user’s Day Master yet? Call POST /api/v1/calculate once with their birthdate, read day_master.stem, store it, and feed it to daily every day after. See the quickstart.

2Get a free key, then one GET per day

Keys are self-serve and free (100 requests/day, no card). Send your email to the key endpoint, store the key, and send it on every request as X-API-Key.

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(); // sajuapi_free_...  shown once, store it
import requests

res = requests.post(
    "https://saju-api.pages.dev/api/v1/keys/create",
    json={"email": "dev@yourcompany.io"},
)
api_key = res.json()["api_key"]  # sajuapi_free_...  shown once, store it

Fetch today’s fortune for a Day Master

day_master is required — pass a Korean stem () or an English alias (wood_yang, water_yin, …). date and lang are optional; date defaults to today.

curl "https://saju-api.pages.dev/api/v1/daily?day_master=wood_yang&date=2026-06-14&lang=en" \
  -H "X-API-Key: sajuapi_free_YOUR_KEY"
const params = new URLSearchParams({
  day_master: "wood_yang",
  date: "2026-06-14",
  lang: "en",
});
const res = await fetch(
  `https://saju-api.pages.dev/api/v1/daily?${params}`,
  { headers: { "X-API-Key": "sajuapi_free_YOUR_KEY" } },
);
const day = await res.json();
console.log(day.score);        // 0..100
console.log(day.advice_key);   // favor_action | favor_planning | rest_recover
console.log(day.advice_label); // localized, e.g. "Day to rest and recover"
import requests

res = requests.get(
    "https://saju-api.pages.dev/api/v1/daily",
    headers={"X-API-Key": "sajuapi_free_YOUR_KEY"},
    params={"day_master": "wood_yang", "date": "2026-06-14", "lang": "en"},
)
day = res.json()
print(day["score"])        # 0..100
print(day["advice_key"])   # favor_action | favor_planning | rest_recover
print(day["advice_label"]) # localized label

Example response

{
  "date":       "2026-06-14",
  "day_pillar": { "stem": "...", "branch": "...", "stem_hanja": "...", "branch_hanja": "..." },
  "day_master": "...",
  "score":      31,
  "advice_key": "rest_recover",
  "advice_label": "Day to rest and recover",
  "lang":       "en",
  "tier":       "free",
  "remaining":  99
}

The three possible advice_key values let you branch on intent without parsing text:

advice_keyMeaning
favor_actionA day that traditionally reads as favorable for taking action and starting things.
favor_planningA day that reads as better for planning, preparing, and steady work.
rest_recoverA day that reads as better for rest, recovery, and avoiding big moves.

3Cache once, serve everyone

Because the reading depends only on the Day Master and the date, the daily response ships with a CDN-friendly cache header:

Cache-Control: s-maxage=3600, stale-while-revalidate=86400

A shared cache (Cloudflare, Fastly, a Redis key, or your edge runtime) can store one entry per (Day Master, date) and reuse it for every user who shares that Day Master. With only ten Day Masters, a whole day of fortunes for your entire user base is at most ten upstream calls — the rest are cache hits.

Warm all ten at midnight (Node, pseudo-cron)

const DAY_MASTERS = [
  "wood_yang", "wood_yin", "fire_yang", "fire_yin", "earth_yang",
  "earth_yin", "metal_yang", "metal_yin", "water_yang", "water_yin",
];

async function warmDailyCache(date, lang = "en") {
  const today = {};
  for (const dm of DAY_MASTERS) {
    const params = new URLSearchParams({ day_master: dm, date, lang });
    const res = await fetch(
      `https://saju-api.pages.dev/api/v1/daily?${params}`,
      { headers: { "X-API-Key": process.env.SAJU_API_KEY } },
    );
    today[dm] = await res.json(); // store in your cache / DB keyed by (dm, date)
  }
  return today; // ten fortunes; serve to every user from here
}
Tip: run this on a scheduled job (cron, Cloudflare Cron Triggers, a serverless schedule) shortly after local midnight, then your push and widget code reads from your own store and never blocks on the network.

4Wire it to push notifications & widgets

Once the ten fortunes are cached, sending a morning push is a lookup, not an API call. Map each user to their stored Day Master, read the cached entry, and format with advice_key:

// after warmDailyCache(date) has populated `today`
function buildPush(user, today) {
  const f = today[user.dayMaster];               // cached, no network
  const emojiByKey = {
    favor_action:   "⚡",                   // map to your own icon set
    favor_planning: "📝",
    rest_recover:   "🌙",
  };
  return {
    title: `Today's fortune · ${f.score}/100`,
    body:  `${emojiByKey[f.advice_key] || ""} ${f.advice_label}`,
    data:  { advice_key: f.advice_key, date: f.date },
  };
}

5Languages, errors & limits

Pass lang to localize advice_label. Supported: ko, en, ja, zh, es, pt-br, vi, id, hi, th. The stable advice_key lets you keep your own copy in any language you like.

StatusMeaning
400invalid_input — e.g. a day_master that isn’t a valid stem or alias, or a malformed date.
401Missing or malformed X-API-Key.
429Daily quota for your tier exceeded — with the cache pattern above you should rarely hit this.

Check remaining quota any time with GET /api/v1/usage (send your key in the X-API-Key header). Every successful response also includes a remaining field.

FAQ

Is the daily fortune a Western zodiac horoscope?

No. It is a Korean Saju (Four Pillars of Destiny) daily snapshot keyed on the Day Master — the Heavenly Stem of the birth day — rather than a sun-sign. You pass a Day Master (a Korean stem such as , or an English alias such as wood_yang) and a date, and get back a 0–100 score and an advice key. There are ten Day Masters, so a feed caches cleanly as ten entries per day.

How should I cache the daily endpoint?

The response sets Cache-Control: s-maxage=3600, stale-while-revalidate=86400, so a CDN or your own cache can store one entry per (Day Master, date) and reuse it for every user who shares that Day Master. With only ten Day Masters, a full day of fortunes for your whole user base is at most ten upstream calls.

Can I localize the daily fortune text?

Yes. Pass lang on the query string (ko, en, ja, zh, es, pt-br, vi, id, hi, th). The advice_label field is localized; advice_key stays stable so you can map it to your own copy or icons.

Is this for entertainment?

Yes. Saju is a traditional cultural and fortune-telling system. The daily score and advice are for entertainment and reflection only — they are not predictions, guarantees, or financial, medical, legal, or professional advice.

Next steps

Saju (사주팔자, Korean Four Pillars of Destiny) is a traditional cultural and fortune-telling system. Output from this API, including the daily score and advice, is provided for entertainment and reflection only and must not be presented as a prediction or as financial, medical, legal, or professional advice.