npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

astrologyjs

v2.0.2

Published

An astrological charting library, backed by the Morphemeris ephemeris API.

Downloads

109

Readme

astrologyjs

An astrological charting library for JavaScript and TypeScript. Give it a name, a moment, and a place; get back body positions, house cusps, angles, and the aspects between them.

import { configure, createPerson, createChart } from 'astrologyjs';

configure({ apiKey: process.env.MORPHEMERIS_API_KEY });

const person = await createPerson(
  'Subject',
  { local: '1990-06-15T14:30' }, // local wall clock at the place
  { lat: 51.5074, lng: -0.1278 }, // London
);

const chart = await createChart('Subject — natal', person);

for (const planet of chart.planets) {
  console.log(`${planet.name}: ${planet.signDegree.toFixed(2)}° ${planet.sign}`);
}

If you are here from an issue

You were right. Versions up to 1.3.1 computed charts by calling a free ephemeris service that no longer exists, so every chart operation fails — usually with Unexpected token <, which is the library trying to parse an HTTP redirect page as JSON. Issues reporting this have been open since 2017.

The short version of what happened: this was a part-time project that outran its author's bandwidth, and the backend went away. There is a real, maintained service behind it now.

Do not install 1.x. Beyond the dead backend, some of its arithmetic was wrong even while the service was alive. The reported orb of an aspect was the fractional part of the angular separation rather than the distance from exactness, so a trine at 118.5° reported 0.5 instead of 1.5 — an aspect looked three times tighter than it was. And Person.create() accepted a Date, which meant the host process's timezone silently decided what moment the chart was cast for: the same code produced different charts on a laptop and in a UTC container, with no error either time.

If you stored charts produced by 1.x, treat their orbs as unreliable and re-derive them.

Install

npm install astrologyjs

ESM only. 2.x ships no CommonJS build. Node 20.19+ or 22.12+ (both support require(esm)), or any modern browser — read the browser note first.

Getting an API key

astrologyjs needs an ephemeris. It uses Morphemeris, which wraps the Swiss Ephemeris behind a REST API. You bring your own key — the library ships no shared key and runs no free proxy, because a shared free endpoint is exactly the arrangement that broke last time and stranded everyone using it.

  1. Create an account at morphemeris.com.
  2. Generate an API key.
  3. Give it to the library, by any of:
// Explicitly, per call — wins over everything else
await createChart('natal', person, { apiKey: 'mk_live_…' });

// Once, for the process
configure({ apiKey: 'mk_live_…' });

// Or set MORPHEMERIS_API_KEY in the environment (Node only; never read in a browser build)

Disclosure: Morphemeris is run by this library's maintainer. It is a young service — newer than the library it now backs — so judge it on that basis rather than on a track record it does not yet have. It is not a hobby endpoint on shared hosting, which is what the original was, and it does not depend on anyone's spare weekend to stay up. That is the honest extent of the claim.

What it costs

The free tier is 500 credits per calendar month, replenished on the 1st, with no payment method required. One /v1/chart request is one credit, and a chart costs as many credits as it needs requests:

| Chart type | Credits | Charts per free month | | --- | ---: | ---: | | Basic (natal) | 1 | 500 | | Davison | 1 | 500 | | Transits | 2 | 250 | | Synastry | 2 | 250 | | Combined | 2 | 250 | | DavisonTransits | 2 | 250 | | CombinedTransits | 3 | 166 |

Identical requests inside one process are deduplicated, so building the same chart twice — or a synastry chart for two people born at the same instant in the same place — costs one credit, not two.

refreshTransits() is the one thing that can surprise you: it is cheap to call in a loop, and at millisecond precision an animated transit clock would drain the free tier in minutes. Transit instants are therefore rounded to 60 seconds by default, which costs at most half an arcminute on the Moon — below the resolution a chart displays. Set transitGranularitySec: 0 to disable it, and watch your balance.

What you get

const chart = await createChart('natal', person, {
  houseSystem: 'placidus', // default
  sidereal: 'lahiri', // omit for tropical
  node: 'true', // or 'mean'
});

chart.planets; // 20 bodies: Sun–Pluto, nodes, Lilith, Chiron, Pholus, Ceres, Pallas, Juno, Vesta
chart.houses; // 12 cusps
chart.ascendant;
chart.midheaven;
chart.aspects; // every pair in aspect, with orb measured from exactness
chart.warnings; // anything the library assumed on your behalf

Aspects, midpoints, composites, and Davison charts are computed locally, so a chart is fully usable offline once fetched. The service supplies ephemeris and house cusps; nothing else.

Times, and why the API looks like this

There is deliberately no way to pass a Date or a bare date-time string. Both carry an implicit zone, and every chart cast from one is a coin flip. A moment enters the library in exactly one of three forms:

{ utc: '1815-12-10T19:00:00Z' }                     // already unambiguous
{ local: '1815-12-10T19:00' }                       // wall clock at the place
{ date: '1815-12-10', timeUnknown: true }           // the date is known, the time is not

The zone comes from the coordinates, and the historical offset from the platform's own timezone database — so wartime DST, half-hour and quarter-hour zones, and pre-standard-time local mean time all resolve correctly. Every assumption is readable afterwards on the Person: zone, utcOffsetMinutes, timeKnown, and zoneFromDefaultResolver.

When a local time is genuinely ambiguous — the hour that repeats when clocks go back — the library refuses rather than guessing, and hands you both candidates:

try {
  await createPerson('Subject', { local: '2023-11-05T01:30' }, { lat: 40.7128, lng: -74.006 });
} catch (error) {
  if (error instanceof AmbiguousTimeError) {
    error.candidates; // both instants, with their offsets — pick one
  }
}

// Then answer it:
await createPerson('Subject', { local: '2023-11-05T01:30', offsetMinutes: -240 }, place);

An unknown birth time omits the houses and angles by default rather than inventing them: an ascendant sweeps the whole zodiac in 24 hours, so a noon value is not an approximation of the true one, it is unrelated to it. If you prefer the common noon convention, ask for it explicitly with { unknownTime: 'noon' } — body positions are the same either way, and the chart tells you which you got in warnings.

Errors

Every failure is a typed class extending AstrologyError, each with a code and a retryable flag: ConfigurationError, ValidationError, AmbiguousTimeError, NonexistentTimeError, UnsupportedBodyError, AuthError, OriginError, InsufficientCreditsError, RateLimitError, UpstreamError, ServiceUnavailableError, AdapterError, TransportError.

InsufficientCreditsError and RateLimitError are separate types on purpose — one needs a purchase, the other needs a wait — and a response that is not JSON raises a TransportError naming the status, the content type, and the first bytes of what actually came back. Unexpected token < does not happen here.

Using this in a browser

The library runs in a browser, and process.env is never read in a browser build. But any key in a browser bundle is public — this library does not obfuscate it, because obfuscation implies a protection that does not exist.

If you ship a key to browsers, restrict it with Morphemeris's require_origin setting. Note that origin restriction alone does not protect a leaked key, since a non-browser caller simply omits the header. For anything beyond a demo, proxy through your own backend and keep the key there.

Notes on accuracy

  • Declination and out-of-bounds are computed locally, not read from the API response. The derivation uses true obliquity — mean plus nutation — and agrees with the engine's own equatorial output to within 0.1 arcsecond. It began as a workaround for morphemeris#83, since fixed upstream, and stays because a number the library computes is one it can verify.
  • orb is the distance from exactness, always non-negative. This is cross-checked against Morphemeris's independently implemented server-side aspect endpoint.
  • Two charts are comparable only if their frames match. House system, zodiac, and node choice travel with the chart on chart.options rather than being ambient.
  • A body has no symbol property; see docs/glyphs.md for Unicode and Kairon Semiserif glyph tables, including the ASCII mapping 1.x encoded.

Documentation

  • Glyph tables — Unicode codepoints and the Kairon Semiserif ASCII map
  • API reference — generated with pnpm docs
  • Specification — the binding contract, if you want to know why something behaves the way it does

Contributing

Tests come before code, and a failing test means the code is wrong until proven otherwise. Run pnpm test — that is unit tests plus the Gherkin acceptance suite, and both must pass. Tests against the live API read MORPHEMERIS_API_KEY from the environment and no-op cleanly without it.

License

MIT. See LICENSE.