mysticapi
v0.1.0
Published
Official isomorphic TypeScript/JavaScript client for MysticAPI — deterministic Human Design–compatible bodygraphs, personal sky charts, and ephemeris. Zero dependencies; runs on Node, Bun, browsers, and Cloudflare Workers.
Maintainers
Readme
mysticapi
Official isomorphic client for MysticAPI — a metered engine API for deterministic, Human Design–compatible bodygraphs, personal sky charts, and ephemeris. Same input always returns the same output (JPL-validated pure-JS ephemeris; no generative output).
- Zero dependencies. Uses the runtime's global
fetch. - Runs everywhere: Node 18+, Bun, browsers, and Cloudflare Workers.
- Fully typed, including a typed error that surfaces the JSON error body, HTTP status, and the
429Retry-After.
npm install mysticapiGet a key (30 seconds, no card)
Free tier is 50 calls/month, no card. The key is emailed to you.
import { MysticApi } from 'mysticapi';
await MysticApi.getFreeKey('[email protected]'); // → check your inbox for mk_live_...Paid tiers (Maker $19 / Pro $49 / Scale $149 per month, flat — never credits) are self-serve Stripe links listed at https://mysticapi.com/llms.txt; your key is emailed within a minute of checkout.
Quick start
import { MysticApi } from 'mysticapi';
const mystic = new MysticApi({ apiKey: process.env.MYSTIC_API_KEY! });
// baseUrl defaults to https://mysticapi.com; pass { baseUrl } to override.1. Today's sky — skyToday()
GET /v1/sky/today — active retrogrades, moon phase, geocentric positions for 13 bodies (zodiac + Energy Blueprint gate/line), and a 30-day forward scan of stations and new/full moons.
const sky = await mystic.skyToday();
console.log(sky.date); // "2026-07-07"
console.log(sky.moon.phaseName); // "Waxing Gibbous"
console.log(sky.moon.illumination); // 0.82
console.log(sky.retrogrades.active); // [{ planet: "mercury", label: "Mercury Retrograde" }]
console.log(sky.positions.sun.zodiac); // { sign: "Cancer", degree: 15.2 }
console.log(sky.positions.sun.gate); // { gate: 4, line: 3 }Response shape:
{
"date": "2026-07-07",
"moon": { "phaseName": "Waxing Gibbous", "illumination": 0.82, "nextFullMoon": { "date": "2026-07-10", "type": "full_moon", "daysAway": 3 } },
"retrogrades": { "active": [{ "planet": "mercury", "label": "Mercury Retrograde" }], "upcomingStations": [ /* SkyEvent[] */ ] },
"positions": { "sun": { "longitude": 105.2, "zodiac": { "sign": "Cancer", "degree": 15.2 }, "gate": { "gate": 4, "line": 3 }, "retrograde": false } /* …13 bodies */ },
"upcoming": [ /* SkyEvent[] — next 30 days */ ]
}2. Bodygraph — bodygraph(birth)
POST /v1/bodygraph — a birth instant in UTC → the full Energy Blueprint (Human Design) chart JSON plus a rendered body-graph SVG. No latitude/longitude needed; the body-graph is location-independent.
const { chart, svg } = await mystic.bodygraph({
year: 1979, month: 8, day: 5, hour: 22, minute: 51, // UTC — convert local time first
});
console.log(chart.chart?.type); // "Manifesting Generator"
console.log(chart.chart?.authority); // "Sacral"
console.log(chart.chart?.profile); // "3/5"
// `svg` is a complete <svg>…</svg> string — write it to a file or inline it.Response shape:
{
"chart": {
"birth": { "year": 1979, "month": 8, "day": 5, "jdn": 2444091.45 },
"design": { /* the 88°-solar-arc design moment */ },
"personalityGates": { "sun": { "gate": 4, "line": 3 } /* … */ },
"designGates": { "sun": { "gate": 49, "line": 1 } /* … */ },
"chart": { "type": "Manifesting Generator", "authority": "Sacral", "strategy": "…", "profile": "3/5", "definition": "…", "cross": "…", "channels": [ /* … */ ], "centers": { /* … */ } }
},
"svg": "<svg …>…</svg>"
}3. Personal sky — skyPersonal(birth)
POST /v1/sky/personal — a birth instant → the natal sky rendered as an SVG string (image/svg+xml). Deterministic per birth. Retrogrades render ℞; fixed stars conjunct natal points light up.
const svg = await mystic.skyPersonal(
{ year: 1990, month: 1, day: 2, hour: 3, minute: 4 },
{ animate: false }, // optional — request a static render
);
// svg === "<svg xmlns=\"http://www.w3.org/2000/svg\" …>…</svg>"There is also skyPersonalGet(birth, opts?) if you want the GET query-param form (handy for shareable URLs).
Error handling
Every non-2xx response throws a typed MysticApiError. Network/transport failures throw with status: 0, code: 'network_error'.
import { MysticApi, MysticApiError } from 'mysticapi';
try {
await mystic.skyToday();
} catch (err) {
if (err instanceof MysticApiError) {
err.status; // 401 | 402 | 429 | 400 | 0 | …
err.code; // "missing_api_key" | "quota_exceeded" | "http_402" | …
err.message; // human-readable message from the API
err.docs; // documentation URL, when provided
err.retryAfter; // number of seconds (set on 429 when Retry-After is present)
if (err.isRateLimited) await sleep(err.retryAfter! * 1000); // back off, then retry
if (err.isUnauthorized) { /* bad or missing key */ }
if (err.isPaymentRequired) { /* 402 — supply a key or an x402 payment */ }
}
}Cloudflare Workers
Works out of the box — the client uses global fetch. Read the key from a secret binding:
export default {
async fetch(_req: Request, env: { MYSTIC_API_KEY: string }): Promise<Response> {
const mystic = new MysticApi({ apiKey: env.MYSTIC_API_KEY });
const sky = await mystic.skyToday();
return Response.json(sky);
},
};You can also inject a custom fetch via new MysticApi({ apiKey, fetch }).
API
| Method | HTTP | Returns |
| --- | --- | --- |
| new MysticApi({ apiKey, baseUrl?, fetch? }) | — | client |
| skyToday() | GET /v1/sky/today | Promise<SkyToday> |
| bodygraph(birth) | POST /v1/bodygraph | Promise<BodygraphResult> ({ chart, svg }) |
| skyPersonal(birth, opts?) | POST /v1/sky/personal | Promise<string> (SVG) |
| skyPersonalGet(birth, opts?) | GET /v1/sky/personal | Promise<string> (SVG) |
| MysticApi.getFreeKey(email, opts?) | POST /v1/keys/free | Promise<FreeKeyResponse> |
birth is { year, month, day, hour, minute, second? } in UTC. All types are exported. The machine-readable contract lives at https://mysticapi.com/.well-known/openapi.json.
License
MIT © Latimer Woods Tech
