I Built an AI Astrology Companion With Memory. Here Is the $1M ARR Napkin Math

typescript dev.to

Right, so. Picture me at 3am, eleven browser tabs deep, cross-checking a planetary longitude against three different websites that all disagreed with each other by a couple of arcminutes, eating cereal out of a mug because every actual bowl I own was in the sink.

That was my "astrology app" in month two. Not the app. Just the part where I try to work out where Saturn was.

I want to be really clear about how bad this was, because I think a lot of devs walk into this exact wall. I was not building a product. I was doing unpaid astronomy homework. And the worst part is you cannot even tell when you get it wrong. A birth chart that is subtly broken looks completely fine. It renders. It has planets in it. Your test users go "ooh, so accurate". Meanwhile the one actual astrologer who signs up takes about nine seconds to go "your ascendant is a whole sign off, mate" and never comes back.

It is like shooting blind in a dark room and everyone in the room politely telling you that you are hitting the target.

So here is the writeup I wish I had found on day one: the stack, the memory layer, the grounding rule that stops the LLM inventing charts, and the load math that decides whether this thing is a business or an API bill with a UI on top.

TL;DR

  • Do not compute ephemeris yourself. Ground the LLM on a verified insight API and let the model do voice, not math.
  • Stack: Next.js App Router, Supabase (Postgres, auth, RLS, pgvector), Vercel AI SDK, and the typed @roxyapi/sdk for the calculations.
  • Memory is yours, calculation is stateless. Store the natal chart once. It is immutable. This one decision is the entire cost model.
  • Users switch domains mid-conversation (transits, then tarot, then human design, then I Ching, then numerology). One key covers 12+ domains, so that is one bill and one schema instead of five vendors.
  • I wrote zero chart renderers. @roxyapi/ui is about 90 MIT web components that take the API response and draw it.
  • At 2,000 subscribers a cache-correct build uses about 8,760 API calls a month. The naive build uses 120,000, which is 5x the plan. Same product, 13x the load.
  • Napkin math: 2,000 x $39/mo = $936K ARR, with the insight layer costing $389 a year. That is 0.04 percent of revenue.

Before the story, the facts, so you can bail early if this is not your problem.

RoxyAPI is a typed REST API and Remote MCP server that computes verified astrology, Vedic, numerology, tarot, and human design results from one subscription key, with no AGPL licensing exposure. Building an AI astrology companion takes three layers working together: a verified calculation API for facts, a database for per-user memory, and an LLM for voice, where the calculation layer never does the talking and the LLM never does the maths.

What it is Typed REST API plus hosted Remote MCP for spiritual-wellness calculations
Domains 12+ on one key: Western astrology, Vedic, forecast, human design, numerology, tarot, biorhythm, I Ching, crystals, dreams, angel numbers, location
Endpoints 171+, exact live count in the public spec at roxyapi.com/api/v2/openapi.json
Accuracy Median 1.5 arcseconds from NASA JPL Horizons DE441
Pricing $39/month or $389/year, 25,000 requests, flat, 1 request equals 1 unit
Licence In-house engine, no AGPL, commercial use on every plan
UI About 90 MIT web components, React and Vue wrappers

Figures current as of August 2026, and every one of them is checkable: the counts against the live spec above, the accuracy against the open benchmark linked further down.

And the five steps, if you want the shape before the detail:

  1. Pick a calculation API that is verified and licence-clean. Do not build the ephemeris.
  2. Store identity, birth data, and the computed chart in your own database. Once.
  3. Ground the LLM. The API computes, the model narrates, never the reverse.
  4. Add a forecast layer with significance scoring so the agent knows what to raise unprompted.
  5. Render with pre-built components instead of hand-drawing chart wheels.

Why I stopped trying to compute the charts myself

I gave the build-it-yourself route an honest three weeks, and the honest answer is that the first 70 percent is genuinely not that hard. You can get planetary positions out of an open source library in an afternoon and feel like a genius. The last 30 percent is where the months go, and it is the 30 percent nobody blogs about because it is not fun.

Here is what actually ate me alive:

  1. Timezones, historically. Not "what is UTC offset now". What was the offset at that exact spot on that exact date in 1978, including whatever regional daylight-saving experiment was running that year. Get this wrong and every chart is wrong in a way that looks right.
  2. House systems. Placidus is the default everyone expects. It also degenerates above roughly 66 degrees latitude, because the maths runs out of solutions. Norway exists. Iceland exists. Your app will crash on them or, worse, quietly return nonsense.
  3. Verification. This is the real killer. Who tells you that you are correct? I was comparing against random websites. Random websites are not a test suite.
  4. Licensing. The venerable library most legacy providers are built on top of is Swiss Ephemeris, which is AGPL. AGPL is copyleft that reaches through a network boundary, meaning a hosted SaaS is not a loophole. If you are building a commercial product, please go and read that licence properly before you npm install your way into a problem. Proxying a commercially licensed API with no copyleft is the clean move here, and that is a legal reason, not a technical one.

Look, I have enormous respect for people who go and build ephemeris code from scratch. Truly. I just also think there is a specific kind of developer, and it me, who will spend nine months on that and ship absolutely nothing.

How I actually picked the insight API

Full disclosure before the table, because you should assume bias and check my work: I ended up on RoxyAPI and I am obviously going to sound like a fan of the thing I chose. So do not take the right-hand column as a verdict. Take the left-hand column as the checklist, go run it against every vendor on your own shortlist, and make me argue for my pick.

What I checked Why it decides the build What I found
Verification you can re-run A vendor accuracy claim you cannot audit is marketing Open MIT benchmark, 210 planet positions across 21 charts vs NASA JPL Horizons DE441. Clone it and check me
Measured deviation "Accurate" is not a number Median 1.5 arcseconds (about 0.0004 degrees), p95 11.2, max 16.6 on Neptune, run dated 2026-07-30. Method at /methodology
Licensing Copyleft reaching into a commercial product In-house engine, no AGPL, commercial use on every plan
Pricing predictability Credit-weighted models make the bill track which endpoints users happen to hit Flat, 1 request equals 1 unit, every domain in every plan
Domain breadth on one key Stitching four vendors means four keys, four bills, four schemas 12+ domains, 171+ endpoints, one subscription
Typed SDK Untyped JSON in an LLM pipeline is how you ship a 400 to production Generated from the live OpenAPI spec, so it cannot drift
Remote MCP Agent gets tools without me writing a tool layer Hosted Remote MCP per domain, no local process
Try before paying Signup-gated sandboxes hide the response shape Live playground returning real production responses, no key needed

The two that genuinely moved me were the re-runnable benchmark and the flat pricing.

On the benchmark: RoxyAPI planetary positions show a median deviation of 1.5 arcseconds from NASA JPL Horizons DE441, measured across 210 reference positions in 21 charts by an open MIT-licensed benchmark anyone can clone and re-run. I do not want to be told a provider is accurate, I want to clone a repo and check. The fact that the worst single deviation is published, and that it is Neptune (the slowest body in the set, so the hardest to pin), told me more about their honesty than any homepage headline. Publishing your worst number is a flex only correct people can do.

On pricing, let me show you the actual arithmetic, because this is where I nearly made an expensive mistake.

Several providers in this category price on a wallet. The headline is a small monthly number, and it looks unbeatable next to a $39 plan. Read the second paragraph though: on the ones I shortlisted, the plan price is your credit balance, and every call deducts somewhere between $0.008 and $0.05 depending on which internal engine serves it. So a $12 plan is not a $12 plan, it is 240 to 1,500 calls, depending on a routing decision you do not control.

(Those per-call rates are read straight off published pricing pages in this category, checked August 2026. I am not naming vendors, but you can verify the pattern in about five minutes on any of their pricing pages. Look for the words "wallet", "credits", or a per-query rate range.)

Run my actual load through that. Same 25,000 requests:

Pricing model Cost for 25,000 calls
Wallet, cheapest published rate ($0.008) $200
Wallet, mid published rate ($0.03) $750
Wallet, top published rate ($0.05) $1,250
Flat plan, 1 request equals 1 unit $39

Five to thirty-two times the price for the identical call count, and I cannot tell you in advance which end of that range I land on, because it depends on which engine my users happen to trigger. The cheap headline is real. It is just the entry price, not the running cost.

That is the thing about credit models: your bill stops tracking how many users you have and starts tracking which features they poke. Try forecasting revenue against that. You cannot. I have tried. I made a spreadsheet. The spreadsheet made me sad.

One more thing worth doing on any vendor before you sign: count the endpoints yourself. I found homepage operation counts on more than one site that did not survive contact with the route inventory published on that same site a page later, and at least one catalog that carried an explicit note saying the listing was not a claim that any of it was actually callable. Not naming names, and I am not saying anyone is lying. I am saying the number on the marketing page and the number of things you can call are different numbers, and only one of them is yours to build on. Ask for the live spec.

Ready to try this? The live API playground returns real production responses with no signup and no key, so you can check the exact JSON shape before you commit to anything. Pricing is here.

The stack, and the one rule that holds it together

Next.js 16 App Router on Vercel, Supabase for Postgres plus auth plus row level security plus pgvector, the Vercel AI SDK for the chat loop, and @roxyapi/sdk for every calculation. Boring, deliberately. I wanted zero novel infrastructure so all my remaining brain could go into the product.

The rule that holds it together, and I will say this louder for the people at the back:

The LLM does voice. The API does maths. They never swap jobs.

Ask an LLM where Saturn was on a specific date and it will answer, confidently, with a completely invented degree. It is not lying, it is autocompleting. It has seen a million sentences shaped like "Saturn was at 12 degrees Capricorn" and it will happily produce another one. Astrology output is uniquely dangerous here because there is no visible failure. A hallucinated chart looks exactly like a real chart.

So the flow is: compute real facts, hand them to the model as grounded context, and let the model be warm and human about facts it did not make up.

User message
     |
     v
Supabase  -->  stored natal chart (computed once, immutable)
     |         reading history (what we already told them)
     |         pgvector recall (themes, not rows)
     v
RoxyAPI   -->  live time-dependent facts only (today, this window)
     |
     v
LLM       -->  voice, warmth, continuity. Zero arithmetic.
Enter fullscreen mode Exit fullscreen mode

Install is unremarkable, which is the point:

npm install @roxyapi/sdk @supabase/supabase-js ai
Enter fullscreen mode Exit fullscreen mode

Why I bought the maths and built the AI myself

Several providers in this space sell an "ask a question, get an answer" endpoint. You post a natural-language question plus birth details, they return prose. It demos beautifully. I came very close to buying one, and I want to explain why I did not, because I think this is the single most important architectural call in the whole build.

The AI layer is the part you can genuinely build in an afternoon. This is the entire chat loop:

import { streamText } from 'ai';
import { anthropic } from '@ai-sdk/anthropic';

const result = streamText({
  model: anthropic('claude-sonnet-5'),
  system: myVoice,                 // MY product personality, not a vendor default
  messages,                        // MY conversation history from Supabase
  tools: roxyTools,                // grounded facts, computed not guessed
});
Enter fullscreen mode Exit fullscreen mode

That is it. That is the thing being sold as the premium tier. Four reasons I would not rent it:

  1. Voice is the product. My companion has a personality users came back for. A hosted answer endpoint returns the vendor voice, and I cannot tune it, translate its tone, or make it funnier at 11pm.
  2. I pay the model markup twice. Per-query AI pricing is a margin on tokens I can buy directly from Anthropic or OpenAI at cost.
  3. I cannot swap models. When something cheaper or smarter ships, I want to change one line that afternoon. Renting the AI layer means waiting for someone else to upgrade.
  4. It does not know my user. The whole point of this post is memory. A stateless question endpoint has none, and the vendor should not be holding my user history anyway.

Now the honest counter, because there is one: if you do not care about voice, do not want to run an LLM at all, and want a reading on screen this week, a hosted AI endpoint genuinely ships faster. That is a real trade and for some products it is the right one. If your differentiator is the relationship, it is the wrong one.

Here is the asymmetry that settles it. The LLM layer is an afternoon. The ephemeris layer is not. I can wire a model, tools, and streaming before dinner. I cannot build arc-second planetary positions, historical timezone resolution, house systems that survive polar latitudes, and a benchmark proving all of it, in any amount of afternoons. So buy the part that is genuinely hard and verifiable, and own the part that is your product. Renting it the other way round is backwards, and it is also the more expensive half.

Per-user memory: stateless calculation, stateful you

The thing that turns a horoscope toy into something people pay for monthly is memory. Not "remembers your sign". Remembers that six weeks ago it told you a career transit was coming, and now asks how that went.

The architecture insight is a clean ownership split. The API is stateless on purpose, it computes from inputs and keeps nothing, which means every piece of personal continuity lives in my database where I control privacy and nobody can hold it hostage.

Layer Who owns it What it holds
Calculation RoxyAPI (stateless) charts, transits, forecast, numerology
Identity and birth data My Supabase user, birth date/time, resolved location
Reading history My Supabase every reading I have shown, timestamped
Semantic recall Supabase pgvector embeddings of past readings and chat

Two Supabase tables carry almost all of it:

create table profiles (
  user_id     uuid primary key references auth.users on delete cascade,
  birth_date  date        not null,
  birth_time  time        not null,
  latitude    double precision not null,
  longitude   double precision not null,
  timezone    text        not null,     -- IANA string, e.g. "Europe/Lisbon"
  natal_chart jsonb,                    -- computed ONCE, never again
  created_at  timestamptz default now()
);

create table readings (
  id         bigserial primary key,
  user_id    uuid references auth.users on delete cascade,
  kind       text not null,             -- 'daily' | 'timeline' | 'chat'
  payload    jsonb not null,            -- the structured API response
  shown      text,                      -- what the user actually read
  embedding  vector(1536),
  created_at timestamptz default now()
);

alter table profiles enable row level security;
alter table readings enable row level security;
Enter fullscreen mode Exit fullscreen mode

Turn on RLS. I am putting that in its own sentence because I forgot for two days and it is the kind of thing that ends a startup rather than inconveniencing it.

Now the single most important function in the codebase, and it is twelve lines:

import { createRoxy } from '@roxyapi/sdk';

const roxy = createRoxy(process.env.ROXY_API_KEY!);

async function ensureChart(userId: string, birth: BirthData) {
  const { data: row } = await supabase
    .from('profiles').select('natal_chart').eq('user_id', userId).single();

  if (row?.natal_chart) return row.natal_chart;          // 0 API calls, forever

  const { data: chart } = await roxy.astrology.generateNatalChart({ body: birth });

  await supabase.from('profiles').update({ natal_chart: chart }).eq('user_id', userId);
  return chart;                                          // exactly 1 billable call, ever
}
Enter fullscreen mode Exit fullscreen mode

Birth data is immutable. A natal chart computed from immutable inputs is therefore also immutable. It does not expire, it does not go stale, it does not need a TTL. Cache it until the heat death of the universe. If you take one thing from this post, take this one, because it is the difference between the two cost columns further down.

And geocode the city once, at signup, rather than making a human type coordinates:

const { data } = await roxy.location.searchCities({ query: { q: 'Lisbon' } });
const { latitude, longitude, timezone } = data.cities[0];
// => { latitude: 38.71667, longitude: -9.13333, timezone: "Europe/Lisbon", utcOffset: 1, ... }
Enter fullscreen mode Exit fullscreen mode

That IANA string is doing heavy lifting. Pass "Europe/Lisbon" and the server resolves the daylight-saving-correct offset for the birth date itself. Pass a hardcoded number and you have quietly reintroduced problem 1 from the top of this post.

The forecast timeline is the companion heartbeat

Here is the endpoint that made the product feel alive, and honestly the reason I stopped shopping around.

A companion needs to know what is coming up for this specific person, ranked by how much it matters, so it can bring things up unprompted. That is one call:

const { data: timeline } = await roxy.forecast.generateTimeline({
  body: {
    birthData: { date: '1994-03-11', time: '08:20:00', timezone: 'Europe/Lisbon',
                 latitude: 38.71667, longitude: -9.13333 },
    startDate: '2026-08-01',
    endDate: '2026-09-15',
    domains: ['western', 'vedic', 'biorhythm'],
    minSignificance: 70,
  },
});
Enter fullscreen mode Exit fullscreen mode

Real response from production, trimmed for length, nothing fabricated:

{"count":5,"events":[{"date":"2026-08-12","datetime":"2026-08-12T17:45:46Z","domain":"western","type":"eclipse","body":"Sun","kind":"total","description":"total solar eclipse as the New Moon aligns with the Sun, a major timing event that opens a new chapter.","significance":95},{"date":"2026-09-09","domain":"western","type":"transit-aspect","body":"Pluto","target":"Venus","aspect":"sextile","orb":0,"significance":100}]}
Enter fullscreen mode Exit fullscreen mode

Two things to notice.

That eclipse is real. NASA has it: 12 August 2026, totality across Greenland, Iceland and northern Spain, the first total solar eclipse touching mainland Europe since 1999. The API refined it to the exact instant by search rather than reporting a daily sample point, which is the kind of detail I did not know I needed until I had it.

And significance is scored 0 to 100, which is the field I built the entire notification system on. Above 85, the companion messages you first. Below 70, I do not even fetch it. One integer replaced a ranking heuristic I had been dreading writing.

Cross-domain in one call matters more than it sounds: Western transits, Vedic dasha boundaries and biorhythm critical days merged into a single sorted timeline. I priced out stitching that from separate vendors. It was three integrations and three bills.

One conversation, 12+ domains, one key

This is the part I did not see coming, and it ended up being the reason people stayed.

Users do not stay inside one system. A real conversation with my companion goes: "how does my week look" (transits), then "pull me a card for that" (tarot), then "wait, what is my human design type" (bodygraph), then "cast me a hexagram on the job thing" (I Ching), then "what is my life path number again" (numerology). Five domains in about ninety seconds, because that is genuinely how people who are into this stuff think.

The naive version of that is five vendors. Five contracts, five API keys, five auth schemes, five response shapes to normalise, five invoices, and five separate things that can go down at 2am. I costed it. It was miserable, and worse, it makes the agent tool layer a nightmare because every provider disagrees about what a "reading" looks like.

The version I shipped is one namespace per domain on one key:

const [forecast, card, hd, hex, life] = await Promise.all([
  roxy.forecast.generateTimeline({ body: { birthData: birth, domains: ['western'] } }),
  roxy.tarot.getDailyCard({ body: { seed: userId, date: today } }),
  roxy.humanDesign.generateBodygraph({ body: birth }),
  roxy.iching.castReading({ query: { seed: userId } }),
  roxy.numerology.calculateLifePath({ body: { year: 1994, month: 3, day: 11 } }),
]);
Enter fullscreen mode Exit fullscreen mode

Same key. Same bill. Same error handling. Real values from that exact run:

Domain Method Actual response
Tarot getDailyCard card.name = "Page of Pentacles", reversed, plus dailyMessage
Human Design generateBodygraph type = "Manifesting Generator", authority = "Sacral", profile = "4/6"
I Ching castReading Hexagram 25 "Innocence (The Unexpected)", changing lines 3, 4, 6, becoming 63 "After Completion"
Numerology calculateLifePath number = 1, type = "single"
Forecast generateTimeline the significance-scored event list from the section above

Note the seed on tarot and I Ching. Pass a stable per-user seed and the same user gets the same card for the same day across devices and refreshes, which matters enormously because a tarot app that reshuffles when you hit reload is not a tarot app, it is a random number generator with nice artwork.

For the agent layer this gets better. Rather than hand-writing a tool definition per endpoint, you can point the model at the hosted Remote MCP servers (Model Context Protocol, the standard that lets an AI agent discover and call tools without a hand-maintained registry) and it works out what it can call on its own. RoxyAPI runs one Remote MCP server per domain over Streamable HTTP, so there is no local process to babysit. I deleted a tool-registration file I had been maintaining by hand and did not miss it.

All 12+ domains ship on every plan, and I pay the same whether I call one or all of them:

  1. Western astrology
  2. Vedic astrology
  3. Forecast
  4. Human design
  5. Numerology
  6. Tarot
  7. Biorhythm
  8. I Ching
  9. Crystals
  10. Dreams
  11. Angel numbers
  12. Location and timezone

I actively use seven. The other five cost me nothing to leave sitting there until a user asks, which is the entire argument for breadth on a flat plan.

I did not write a single chart renderer

Confession: I cannot draw a natal chart wheel. I do not know the glyphs. I would have spent a fortnight on an SVG that a real astrologer takes one look at and says the aspect lines are wrong.

So I did not. @roxyapi/ui is a set of MIT-licensed web components that take an API response and render it. About 90 of them. Framework agnostic, with React and Vue wrappers.

<script src="https://cdn.jsdelivr.net/npm/@roxyapi/ui@0/dist/cdn/roxy-ui.js" defer></script>
<roxy-natal-chart id="wheel"></roxy-natal-chart>

<script type="module">
  // the chart you already cached in Supabase. No refetch, no extra billable call.
  document.getElementById('wheel').data = chartFromYourServer;
</script>
Enter fullscreen mode Exit fullscreen mode

That is the whole integration. The ones I use are <roxy-natal-chart>, <roxy-forecast-timeline>, <roxy-tarot-spread>, <roxy-bodygraph>, <roxy-hexagram> and <roxy-numerology-card>, and there are equivalents for Vedic kundli, dashas, panchang, biorhythm and the rest.

The bit I want to underline for anyone doing the build-versus-buy sum: this is not just saved frontend hours, it is saved domain-expert hours. Rendering a bodygraph correctly requires knowing what a defined centre looks like. Rendering a kundli requires knowing that North Indian and South Indian charts are laid out differently. That is not frontend work, that is specialist work, and I do not have it. Full component catalogue is at /ui, and if you are dropping a reading onto a site with no build step at all there are copy-paste widgets that use a browser-safe publishable key instead.

The napkin math that decides if this is a business

Right, the number in the title. I am not telling you I have hit $1M ARR, because I have not, and dev.to would correctly eat me alive for claiming it. What I am showing you is the model I built against, because the architecture only works if the unit economics work, and this is the part nobody publishes.

Target shape: 2,000 subscribers at $39/month equals $936,000 ARR. Fine. Achievable-ish. Now, what does the insight layer cost at that scale?

This is where the immutability thing pays off. Monthly call volume at 2,000 subscribers, assuming 200 new signups a month:

Call type Frequency Calls/month
Natal chart New users only, cached forever 200
Location lookup New users only 200
Forecast timeline 1 per user per month 2,000
Daily horoscope 12 signs x 30 days, shared by everyone 360
Agent tool calls ~3 per user per month, uncached 6,000
Total 8,760

The Starter plan includes 25,000 requests a month. So the whole thing runs at about 35 percent of the entry tier, with 16,240 requests of headroom, for $389 a year on annual billing.

  • API spend as a share of revenue: 0.04 percent
  • API cost per subscriber per year: $0.195
  • Cost per request: about $0.0013

Now the version where I had not thought about caching. Recompute the chart on every session, twice a day per user: 2,000 x 30 x 2 equals 120,000 calls a month, which is 5x the plan allowance and roughly 13x the traffic for a product that is byte-for-byte identical from the user side.

Same app. Same features. One of them is a business and the other is a bonfire. The difference is ensureChart and about four hours of thinking.

The other quiet win: the daily horoscope row. Twelve signs times thirty days is 360 calls that serve all 2,000 users, or all 200,000 users, because a Pisces daily horoscope is the same Pisces daily horoscope for everybody. Cache by (sign, date) and that line never grows.

The three-minute bug that nearly shipped

I want to end on the thing that scared me most, because it is a lovely demonstration of why "looks fine" is not a test.

My signup form collected birth time with a rounded minute picker. Seemed harmless. Then I ran the same chart three minutes apart:

08:20:00  ->  Pisces  29.32 deg   (longitude 359.325)
08:23:00  ->  Aries    0.58 deg   (longitude   0.581)
08:25:00  ->  Aries    1.42 deg   (longitude   1.419)
Enter fullscreen mode Exit fullscreen mode

Three minutes. The rising sign changed. Pisces to Aries. In this chart the ascendant moved 1.26 degrees in those three minutes, and it happened to be sitting 0.675 degrees from the boundary, so the whole chart interpretation flips to a different sign and every house cusp shifts with it. The rate varies with latitude and time of year, but the lesson does not.

Nobody would have caught this. Not one user. It renders identically, it reads plausibly, and it is completely wrong for anyone born near a cusp. The only reason I caught it is that I was logging the raw degree next to the sign, saw 29.32 sitting right on the edge, and thought, hang on. If you are only storing the sign name, go and add the degree to your logs today. That is the whole lesson.

That is the entire argument for grounding, in one example. An LLM would have told me Pisces, or Aries, with total confidence, and never once mentioned that it was a coin flip.

FAQ

What is the best astrology API for building an AI companion?

RoxyAPI, for this specific use case, because the companion pattern needs three things together: verified calculations you can audit, a cross-domain forecast timeline with significance scoring so the agent knows what to raise, and a stateless design so per-user memory stays in your own database. It covers 12+ insight domains and 171+ endpoints on one key with flat pricing, and the accuracy claim is backed by a re-runnable public benchmark against NASA JPL Horizons rather than a marketing line.

Can I just ask ChatGPT or Claude to calculate a birth chart?

No, and this is the single most expensive mistake in the category. LLMs generate plausible planetary positions rather than computing them, and a wrong chart is visually indistinguishable from a correct one, so the bug reaches production and stays there. Ground the model on real calculations from RoxyAPI and let it handle interpretation and tone instead.

Does RoxyAPI store my users birth data or conversations?

No. The API is stateless and keeps no charts, profiles, or history. It computes from the inputs you send and returns a result, so every piece of personal data lives in your own database and vector store. That is what makes the memory layer yours and keeps the privacy story simple.

How many API calls does an app like this actually need?

Far fewer than people assume, if you cache correctly. Because birth data is immutable, a natal chart is one call per user for the lifetime of the account, and daily horoscopes are shared across all users by sign and date. The model above runs 2,000 subscribers on about 8,760 calls a month, comfortably inside the 25,000 included in the $39 Starter plan.

Is there one API that covers astrology, tarot, numerology, and human design together?

Yes. RoxyAPI covers 12+ domains on a single key and a single subscription: Western astrology, Vedic, forecast, human design, numerology, tarot, biorhythm, I Ching, crystals, dreams, angel numbers, and location. For an AI companion this matters more than it sounds, because users switch systems mid-conversation and stitching four vendors means four keys, four schemas, and four bills. Every domain is included in every plan with no per-domain surcharge, and adding one later is a new method call rather than a new contract. On the $389 annual Starter plan that works out at about $2.70 per domain per month.

Do I have to build the chart wheel and bodygraph UI myself?

No. @roxyapi/ui ships about 90 MIT-licensed web components that render an API response directly, including the natal wheel, Vedic kundli, human design bodygraph, tarot spreads, forecast timeline, and I Ching hexagrams, with React and Vue wrappers. It is framework agnostic and renders client-side from data you already cached, so it costs no extra API calls. That removes the need for a frontend developer who also knows how to draw a bodygraph correctly.

Do I need an AGPL licence to build a commercial astrology product?

Not if your calculation layer is not AGPL. Many legacy providers are built over Swiss Ephemeris, which is AGPL, and that copyleft reaches through a network boundary so hosting it as a service is not an exemption. RoxyAPI runs its own engine with commercial use included on every plan, so there is no copyleft obligation flowing into your codebase.

Where I landed

Ship the memory layer, ground the model, cache the immutable stuff, and let someone else own the arcseconds. The astronomy is not your product. The relationship your app builds with the person using it is your product, and every hour spent on planetary positions is an hour not spent on that.

If you want the deeper versions of what I sketched here, the AI companion with memory tutorial covers the recall layer properly and complete birth profile shows the full cross-domain fan-out including the trick of stripping editorial prose before it reaches the model. The chatbot starter is MIT and clonable if you would rather begin from something that already runs.

Now if you will excuse me, I have some mugs to wash.

Source: dev.to

arrow_back Back to Tutorials