<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Nick</title>
    <description>The latest articles on DEV Community by Nick (@prowler64).</description>
    <link>https://dev.to/prowler64</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3953358%2Ffcd39765-f70d-4e4d-a755-ccdd847299af.jpg</url>
      <title>DEV Community: Nick</title>
      <link>https://dev.to/prowler64</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/prowler64"/>
    <language>en</language>
    <item>
      <title>Best Horoscope API for Developers Shipping Feeds 2026</title>
      <dc:creator>Nick</dc:creator>
      <pubDate>Sun, 13 Sep 2026 03:07:43 +0000</pubDate>
      <link>https://dev.to/prowler64/best-horoscope-api-for-developers-shipping-feeds-2026-36pd</link>
      <guid>https://dev.to/prowler64/best-horoscope-api-for-developers-shipping-feeds-2026-36pd</guid>
      <description>&lt;p&gt;&lt;em&gt;Originally published on &lt;a href="https://roxyapi.com/blogs/best-horoscope-api-developers" rel="noopener noreferrer"&gt;the RoxyAPI blog&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;If you are wiring a horoscope feed into a CMS, a content pipeline, or a chat surface, the interesting question is not which sign gets which paragraph. It is whether the text you are about to schedule a month ahead can be checked before it runs, and whether calling the same endpoint twice returns the same bytes. This post is a field level look at one horoscope API built around that question: four periods, dated sky events under every sentence, and a license that lets you actually touch the words.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;TL;DR&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A horoscope feed is worth buying on whether it can prove itself, not on how many signs it covers: does every reading ship the dated sky events it is built from, can you re-run it and get the same text, and who owns the words once you edit them.&lt;/li&gt;
&lt;li&gt;Measured live against production on 2026-09-13: monthly love, career, health, and finance sections ran 116 to 199 words across twelve signs and two months, each backed by a dated events array checkable against NASA JPL Horizons or the US Naval Observatory.&lt;/li&gt;
&lt;li&gt;Every reading ships an events array beside the prose, dated to the second, so a publishing pipeline can wire in a fact-check step instead of trusting the text blind.&lt;/li&gt;
&lt;li&gt;Build this with the &lt;a href="https://roxyapi.com/products/astrology-api" rel="noopener noreferrer"&gt;Astrology API&lt;/a&gt; in about thirty minutes, reusable for daily, weekly, monthly, and yearly feeds.&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;

&lt;p&gt;A horoscope feed buyer, a psychic network, a content aggregator, a dating or wellness app, a white-label publisher, is not evaluating an astrology API the way a chart-app developer does. The question is not "can it compute a birth chart," it is "can I schedule a month of copy today and trust it will still be accurate when it runs." That means the reading needs a reason behind every sentence, the reason needs a date attached to it, and the whole thing needs to survive an editor touching the words before it goes out under a byline.&lt;/p&gt;

&lt;p&gt;Most of the category fails this test in one of two directions. Legacy astrology API providers sell one static paragraph keyed to the current sign of a planet, identical for everyone reading that sign for weeks at a time. AI horoscope generators go the other way: fluent, varied, and impossible to fact-check, because the model that wrote a line about a trine cannot hand back the exact date of that trine for an editor to verify. The four RoxyAPI horoscope routes, daily, weekly, monthly, and yearly, ship a column plus six section fields (&lt;code&gt;overview&lt;/code&gt;, &lt;code&gt;love&lt;/code&gt;, &lt;code&gt;career&lt;/code&gt;, &lt;code&gt;health&lt;/code&gt;, &lt;code&gt;finance&lt;/code&gt;, &lt;code&gt;advice&lt;/code&gt;), and every one of them names the sky events driving it, in an &lt;code&gt;events&lt;/code&gt; array with the exact instant, the bodies, and the house it falls in for that sign.&lt;/p&gt;

&lt;h2&gt;
  
  
  What does each horoscope period actually return?
&lt;/h2&gt;

&lt;p&gt;A daily, weekly, monthly, and yearly call from the same route family return the same seven pieces, a column plus six sections, but the depth and the extra arrays scale with the period, so a publisher picks the grain that matches how far ahead the calendar runs. Measured live against production, twelve signs per period, one date per grain (2026-10-05 for daily and weekly, October and November 2026 for monthly, all of 2026 for yearly):&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Period&lt;/th&gt;
&lt;th&gt;Column length (measured)&lt;/th&gt;
&lt;th&gt;Dated events measured&lt;/th&gt;
&lt;th&gt;What only this grain adds&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Daily&lt;/td&gt;
&lt;td&gt;125 to 152 words, median 142&lt;/td&gt;
&lt;td&gt;1 event (2026-10-05, all twelve signs)&lt;/td&gt;
&lt;td&gt;Active transits list, Moon sign and phase, an energy rating, lucky number and color&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Weekly&lt;/td&gt;
&lt;td&gt;260 to 302 words, median 282&lt;/td&gt;
&lt;td&gt;4 events (week of 2026-10-05, all twelve signs)&lt;/td&gt;
&lt;td&gt;Three lucky days, three lucky numbers&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Monthly&lt;/td&gt;
&lt;td&gt;503 to 659 words, median 592&lt;/td&gt;
&lt;td&gt;11 to 14 events, median 12 (Oct and Nov 2026)&lt;/td&gt;
&lt;td&gt;A week-by-week focus breakdown and a key-dates list of the real New Moon, Full Moon, and retrograde instants that month&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Yearly&lt;/td&gt;
&lt;td&gt;831 to 895 words, median 883&lt;/td&gt;
&lt;td&gt;15 to 17 events (2026, all twelve signs)&lt;/td&gt;
&lt;td&gt;Every eclipse and retrograde station of the year with its house, a slow-planet theme timeline, and the best month per life area&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;A scheduled importer typically pulls monthly for a rolling content calendar and yearly once, at the start of a subscriber year, for an annual feature page; daily and weekly cover a live feed or a chat surface where freshness matters more than lead time.&lt;/p&gt;

&lt;p&gt;Ready to see it on your own signs? The &lt;a href="https://roxyapi.com/products/astrology-api" rel="noopener noreferrer"&gt;Astrology API&lt;/a&gt; ships all four periods on one key, and &lt;a href="https://roxyapi.com/pricing" rel="noopener noreferrer"&gt;pricing&lt;/a&gt; stays flat whether you pull one sign a day or all twelve on every period.&lt;/p&gt;

&lt;h2&gt;
  
  
  How does a horoscope feed API compare across providers?
&lt;/h2&gt;

&lt;p&gt;Run the same evaluation a buyer would run before signing: does the text change per sign, does it carry the dates it is built from, can an editor check it, can it be re-fetched identically, what does it cost, and who owns the words after you edit them.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;What a buyer checks&lt;/th&gt;
&lt;th&gt;RoxyAPI&lt;/th&gt;
&lt;th&gt;Legacy astrology API providers selling generic horoscope prose&lt;/th&gt;
&lt;th&gt;AI horoscope generators&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Per-sign uniqueness&lt;/td&gt;
&lt;td&gt;Every sign is read through its own whole-sign houses, so the same sky event produces a different sentence for each of the twelve signs&lt;/td&gt;
&lt;td&gt;One paragraph keyed to the sign a planet occupies, reused for every subscriber of that sign, often unchanged for weeks&lt;/td&gt;
&lt;td&gt;Genuinely different per call, but not guaranteed to stay different: two runs for the same sign can converge on similar phrasing with no control over it&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Transit basis with dates&lt;/td&gt;
&lt;td&gt;Every column and section is composed from aspects, sign ingresses, retrograde stations, lunations, and eclipses, each with the exact UTC instant to the second&lt;/td&gt;
&lt;td&gt;Prose is usually keyed to the current sign of a planet for the period, without the aspect or ingress dates that produced it&lt;/td&gt;
&lt;td&gt;A model can mention a transit if it is told about one, but the output alone gives no independently checkable date&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Fact-check evidence&lt;/td&gt;
&lt;td&gt;An &lt;code&gt;events&lt;/code&gt; array ships beside the prose: type, bodies, angle, house, exact instant, and the window it holds open&lt;/td&gt;
&lt;td&gt;No machine-readable event list ships with the text; checking it means re-deriving the astrology yourself&lt;/td&gt;
&lt;td&gt;Whatever grounded the answer is not returned in a checkable form unless the app wraps the call in its own retrieval step&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Reproducibility for scheduling&lt;/td&gt;
&lt;td&gt;Deterministic: the same sign and period returns byte-identical text on every call, so a piece approved a month ahead is the piece that runs&lt;/td&gt;
&lt;td&gt;Fixed for its stated period, but the period is usually the whole month rather than a specific day&lt;/td&gt;
&lt;td&gt;Not guaranteed byte-identical across calls unless the vendor pins a fixed seed and prompt version, which most consumer-facing generators do not expose&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Languages&lt;/td&gt;
&lt;td&gt;Eight languages, each composed natively in its own grammar, not machine-translated after the fact&lt;/td&gt;
&lt;td&gt;Coverage is vendor-specific and usually gated by plan tier rather than included as a base feature&lt;/td&gt;
&lt;td&gt;Whatever the underlying model supports, with no independent check on terminology consistency&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;License to edit and white-label&lt;/td&gt;
&lt;td&gt;Translate, edit, shorten, and rewrite the text for your product voice; white-label the computed output inside your own product; the one condition is not presenting a materially altered version as editorial content belonging to RoxyAPI&lt;/td&gt;
&lt;td&gt;Republishing and edit rights are usually a separate negotiated contract that varies by vendor&lt;/td&gt;
&lt;td&gt;Output ownership follows the usage terms of the model vendor itself, layered on top of whatever prompt produced it&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Agent access over Remote MCP&lt;/td&gt;
&lt;td&gt;One Remote MCP server per product over Streamable HTTP; an agent the buyer runs calls the same horoscope tools live, no local hosting&lt;/td&gt;
&lt;td&gt;MCP support, where it exists at all, is typically a local or stdio wrapper the buyer has to host and maintain&lt;/td&gt;
&lt;td&gt;The generator itself may be reachable as a chat endpoint, but grounding it in dated, checkable sky events still needs an outside source&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Pricing shape&lt;/td&gt;
&lt;td&gt;Flat subscription, every domain in every plan, one request equals one quota unit, from $39/month for 50,000 requests&lt;/td&gt;
&lt;td&gt;Typically credit-weighted or priced per call, often with horoscope content behind its own add-on or higher tier&lt;/td&gt;
&lt;td&gt;Priced per token or per generation call, so cost scales with prompt length as well as traffic&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  What do legacy feeds and AI generators actually ship instead?
&lt;/h2&gt;

&lt;p&gt;A legacy horoscope feed ships stability at the cost of specificity: the same paragraph runs for a sign until the editorial calendar of the vendor rotates it, which is honest about being generic but gives a publisher nothing to point to when a reader asks why the reading published today mentions a retrograde. An AI horoscope generator ships specificity at the cost of stability: ask it twice and the wording moves, which is fine for a one-off chat reply and a real problem for a piece an editor reviewed on Monday and a CMS publishes on Friday.&lt;/p&gt;

&lt;p&gt;Both failure modes trace back to the same missing piece: neither category ships the dated event list a reading rests on, so neither can be fact-checked without redoing the astrology from scratch. A buyer choosing between them is really choosing which kind of unreviewable content they would rather run.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A legacy feed is still the right call in one honest case:&lt;/strong&gt; a single-language, single-market product that publishes one evergreen paragraph a month and has no interest in dated events or a fact-check trail. &lt;strong&gt;An AI generator earns its place&lt;/strong&gt; where the copy must sound conversational and match a specific brand voice on every single reply, and byte-for-byte reproducibility across requests is not a requirement, a chatbot reply rather than a scheduled publication.&lt;/p&gt;

&lt;h2&gt;
  
  
  How do you fact-check a horoscope column before it runs?
&lt;/h2&gt;

&lt;p&gt;Every sentence traces to a row in the &lt;code&gt;events&lt;/code&gt; array, so an editor checks the astrology before publishing rather than trusting the prose. Pulled live from &lt;code&gt;GET /astrology/horoscope/aries/monthly?date=2026-10-01&lt;/code&gt; on 2026-09-13, the &lt;code&gt;love&lt;/code&gt; field opens: "Because Venus turns retrograde on the 3rd, your eighth house is the part that moves." The matching row in &lt;code&gt;events&lt;/code&gt; reads &lt;code&gt;{"type":"retrograde-station","at":"2026-10-03T07:09:17Z","bodies":["Venus"],"sign":"scorpio","house":8,"through":"2026-11-14T00:21:35Z"}&lt;/code&gt;, exact instant, the sign Venus turns retrograde in, and the house that placement falls in for Aries.&lt;/p&gt;

&lt;p&gt;That date is independently checkable two ways. First, against the physics: a planet does not actually reverse orbit, retrograde motion is an apparent effect of Earth passing a slower-moving planet, or being passed by a faster one, as explained by NASA Goddard Space Flight Center. Second, against a completely separate publisher: a long-running astrological almanac, published independently of RoxyAPI since 1792, lists the same 2026 Venus retrograde window, October 3 to November 14. A reading that is wrong about the date fails both checks at once; this one passes both.&lt;/p&gt;

&lt;p&gt;Why does a Venus retrograde station belong in a love section specifically, and not just career or finance? Venus governs relationships, attraction, and personal values in the tradition every general-audience horoscope draws on, so a professional astrologer writing for that same almanac frames the October 2026 window as a period for reviewing relationships and spending rather than starting new ones, not a career or health signal. RoxyAPI routes a Venus event to the &lt;code&gt;love&lt;/code&gt; section whichever house it falls in, following that same convention.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;116 to 199 words.&lt;/strong&gt; Monthly section length, love/career/health/finance, measured against production across twelve signs and two months (October and November 2026), each backed by its own dated events array. Median 178 to 187 words per field. See &lt;a href="https://roxyapi.com/methodology" rel="noopener noreferrer"&gt;methodology&lt;/a&gt; for how every dated claim is checked.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  How do you pull a publishing calendar of horoscope columns ahead of time?
&lt;/h2&gt;

&lt;p&gt;Because the same sign and period always returns the same text, an editorial team can pull a month, or a year, before it runs, review it once, and publish on schedule without the copy drifting between the review and the publish date. Every subscriber requesting the same sign and period gets the same column, since no language model sits in the request path, which is also what makes a license to edit meaningful: rewrite the piece in the voice of your product once, and a re-fetch of the same sign and date will not silently change the source text underneath that edit.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;curl&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="s2"&gt;"https://roxyapi.com/api/v2/astrology/horoscope/aries/monthly?date=2026-11-01"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"X-API-Key: YOUR_KEY"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;TypeScript&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;createRoxy&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@roxyapi/sdk&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;roxy&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;createRoxy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ROXY_API_KEY&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;roxy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;astrology&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getMonthlyHoroscope&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;sign&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;aries&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;query&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;date&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;2026-11-01&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;column&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;love&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;events&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Full field-by-field reference, including the daily, weekly, and yearly variants: &lt;a href="https://roxyapi.com/api-reference#tag/western-astrology/GET/astrology/horoscope/{sign}/monthly" rel="noopener noreferrer"&gt;monthly horoscope endpoint&lt;/a&gt;. For a no-code publishing surface on the same data, see the &lt;a href="https://roxyapi.com/docs/tutorials/horoscope-widget" rel="noopener noreferrer"&gt;daily horoscope widget&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  REST, Remote MCP, or the docs MCP: which one fits a horoscope import job?
&lt;/h2&gt;

&lt;p&gt;Use REST for a scheduled importer: a cron job or CMS plugin calling &lt;code&gt;?date=&lt;/code&gt; on a fixed schedule needs nothing more than an HTTP client and an API key, and every response is the same shape every time. Use the &lt;a href="https://roxyapi.com/docs/mcp" rel="noopener noreferrer"&gt;Remote MCP server&lt;/a&gt; when the caller is a runtime agent rather than a script: RoxyAPI mounts one Remote MCP server per product over Streamable HTTP, so a chatbot or content agent the buyer runs connects to the astrology server and calls the horoscope tools directly, live, with no local server to host. And when the thing writing the importer is a coding agent rather than a person, point it at the keyless docs MCP instead of the billable domain server: it reads the full endpoint and field reference and writes the request correctly on the first try, without spending a single quota unit on lookups.&lt;/p&gt;

&lt;h2&gt;
  
  
  What does a horoscope feed cost per sign, and what can you do with the text?
&lt;/h2&gt;

&lt;p&gt;Pricing is flat rather than metered by content type: the Starter plan is $39 a month for 50,000 requests, and every one of the eighteen domains, including all four horoscope periods, is included at that price, not sold as a separate add-on. Spread the annual Starter price across every domain and the floor works out to about $1.80 a domain a month, whether a subscriber calls the horoscope routes once a day per sign or runs the full yearly forecast for all twelve.&lt;/p&gt;

&lt;p&gt;The license answers the question a publisher actually asks: can this text run under my own byline? You may translate, edit, shorten, and rewrite the interpretation content to match the voice of your product, including into a language RoxyAPI does not ship natively, and you may white-label the computed output inside your own product. The one condition is attribution: if you materially alter the text, do not present the altered version as editorial content belonging to RoxyAPI. Results retrieved during a paid term are yours to keep and keep using after cancelling, so a published archive of past months does not need to be re-licensed later. Full terms: &lt;a href="https://roxyapi.com/policy/license" rel="noopener noreferrer"&gt;license&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  FAQ
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;What is the best horoscope API for developers publishing feeds in 2026?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;RoxyAPI is built for exactly this buyer: four periods (daily, weekly, monthly, yearly) with an editorial column plus six section fields, every reading backed by a dated, fact-checkable events array, in eight natively composed languages on one flat-priced key.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Does the horoscope API include the transit dates a reading is based on?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Yes. Every response carries an &lt;code&gt;events&lt;/code&gt; array alongside the prose, with the event type, the bodies involved, the angle where there is one, the exact UTC instant, and the whole-sign house it falls in for the requested sign, so any sentence can be checked before it publishes.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Can I edit or rewrite the horoscope text before I publish it?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Yes. The license permits translating, editing, shortening, and rewriting the interpretation content to match the voice of your own product, and white-labeling the computed output inside your product. The only condition is not presenting a materially altered version as editorial content belonging to RoxyAPI.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Is the horoscope text the same every time I request the same sign and date?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Yes. Readings are deterministic: the same sign and period always return the same column and section text, because no language model sits in the request path. That is what makes scheduling a publishing calendar weeks ahead safe.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Does the horoscope API support multiple languages?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Yes, eight languages, each composed natively in its own grammar rather than translated after the fact, included on the same key at no extra cost.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Can an AI agent call the horoscope API directly without custom integration code?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Yes. The RoxyAPI astrology Remote MCP server runs over Streamable HTTP with no local setup, so an agent built on Claude, ChatGPT, or a framework like LangChain can call the same horoscope tools a REST integration would use, live, during a conversation.&lt;/p&gt;

&lt;h2&gt;
  
  
  Building a feed worth publishing
&lt;/h2&gt;

&lt;p&gt;A horoscope feed is only as good as the ability of a reader, or an editor, to check it, and that is the one thing most of the category does not ship. The four RoxyAPI horoscope routes name the dated sky events behind every column and section, return the same text on every re-fetch, and license the words for you to edit and run under your own name.&lt;/p&gt;

&lt;p&gt;Start with the &lt;a href="https://roxyapi.com/products/astrology-api" rel="noopener noreferrer"&gt;Astrology API&lt;/a&gt; and pull your first month of copy on the &lt;a href="https://roxyapi.com/pricing" rel="noopener noreferrer"&gt;flat, all-inclusive pricing&lt;/a&gt; today.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>I Built an AI Astrology Companion With Memory. Here Is the $1M ARR Napkin Math</title>
      <dc:creator>Nick</dc:creator>
      <pubDate>Sat, 01 Aug 2026 11:15:11 +0000</pubDate>
      <link>https://dev.to/prowler64/i-built-an-ai-astrology-companion-with-memory-here-is-the-1m-arr-napkin-math-26g9</link>
      <guid>https://dev.to/prowler64/i-built-an-ai-astrology-companion-with-memory-here-is-the-1m-arr-napkin-math-26g9</guid>
      <description>&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;That was my "astrology app" in month two. Not the app. Just the part where I try to work out where Saturn was.&lt;/p&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;It is like shooting blind in a dark room and everyone in the room politely telling you that you are hitting the target.&lt;/p&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;TL;DR&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Do not compute ephemeris yourself. Ground the LLM on a verified insight API and let the model do voice, not math.&lt;/li&gt;
&lt;li&gt;Stack: Next.js App Router, Supabase (Postgres, auth, RLS, pgvector), Vercel AI SDK, and the typed &lt;code&gt;@roxyapi/sdk&lt;/code&gt; for the calculations.&lt;/li&gt;
&lt;li&gt;Memory is yours, calculation is stateless. Store the natal chart once. It is immutable. This one decision is the entire cost model.&lt;/li&gt;
&lt;li&gt;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.&lt;/li&gt;
&lt;li&gt;I wrote zero chart renderers. &lt;code&gt;@roxyapi/ui&lt;/code&gt; is about 90 MIT web components that take the API response and draw it.&lt;/li&gt;
&lt;li&gt;At 2,000 subscribers a cache-correct build uses about &lt;strong&gt;8,760 API calls a month&lt;/strong&gt;. The naive build uses &lt;strong&gt;120,000&lt;/strong&gt;, which is 5x the plan. Same product, 13x the load.&lt;/li&gt;
&lt;li&gt;Napkin math: 2,000 x $39/mo = $936K ARR, with the insight layer costing $389 a year. That is &lt;strong&gt;0.04 percent of revenue&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;

&lt;p&gt;Before the story, the facts, so you can bail early if this is not your problem.&lt;/p&gt;

&lt;p&gt;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.&lt;/p&gt;

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

&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;And the five steps, if you want the shape before the detail:&lt;/p&gt;

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

&lt;h2&gt;
  
  
  Why I stopped trying to compute the charts myself
&lt;/h2&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;Here is what actually ate me alive:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Timezones, historically.&lt;/strong&gt; 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.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;House systems.&lt;/strong&gt; 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.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Verification.&lt;/strong&gt; 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.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Licensing.&lt;/strong&gt; 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 &lt;code&gt;npm install&lt;/code&gt; 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.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;h2&gt;
  
  
  How I actually picked the insight API
&lt;/h2&gt;

&lt;p&gt;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.&lt;/p&gt;

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

&lt;p&gt;The two that genuinely moved me were the &lt;strong&gt;re-runnable benchmark&lt;/strong&gt; and the &lt;strong&gt;flat pricing&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;On pricing, let me show you the actual arithmetic, because this is where I nearly made an expensive mistake.&lt;/p&gt;

&lt;p&gt;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 &lt;strong&gt;is&lt;/strong&gt; 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.&lt;/p&gt;

&lt;p&gt;(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.)&lt;/p&gt;

&lt;p&gt;Run my actual load through that. Same 25,000 requests:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Pricing model&lt;/th&gt;
&lt;th&gt;Cost for 25,000 calls&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Wallet, cheapest published rate ($0.008)&lt;/td&gt;
&lt;td&gt;$200&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Wallet, mid published rate ($0.03)&lt;/td&gt;
&lt;td&gt;$750&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Wallet, top published rate ($0.05)&lt;/td&gt;
&lt;td&gt;$1,250&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Flat plan, 1 request equals 1 unit&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;$39&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Ready to try this?&lt;/strong&gt; The &lt;a href="https://roxyapi.com/api-reference" rel="noopener noreferrer"&gt;live API playground&lt;/a&gt; returns real production responses with no signup and no key, so you can check the exact JSON shape before you commit to anything. &lt;a href="https://roxyapi.com/pricing" rel="noopener noreferrer"&gt;Pricing is here&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  The stack, and the one rule that holds it together
&lt;/h2&gt;

&lt;p&gt;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 &lt;code&gt;@roxyapi/sdk&lt;/code&gt; for every calculation. Boring, deliberately. I wanted zero novel infrastructure so all my remaining brain could go into the product.&lt;/p&gt;

&lt;p&gt;The rule that holds it together, and I will say this louder for the people at the back:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;The LLM does voice. The API does maths. They never swap jobs.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;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.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;User message
     |
     v
Supabase  --&amp;gt;  stored natal chart (computed once, immutable)
     |         reading history (what we already told them)
     |         pgvector recall (themes, not rows)
     v
RoxyAPI   --&amp;gt;  live time-dependent facts only (today, this window)
     |
     v
LLM       --&amp;gt;  voice, warmth, continuity. Zero arithmetic.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Install is unremarkable, which is the point:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt; @roxyapi/sdk @supabase/supabase-js ai
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Why I bought the maths and built the AI myself
&lt;/h2&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;The AI layer is the part you can genuinely build in an afternoon. This is the entire chat loop:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;streamText&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ai&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;anthropic&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@ai-sdk/anthropic&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;streamText&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;model&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;anthropic&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;claude-sonnet-5&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="na"&gt;system&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;myVoice&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;                 &lt;span class="c1"&gt;// MY product personality, not a vendor default&lt;/span&gt;
  &lt;span class="nx"&gt;messages&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;                        &lt;span class="c1"&gt;// MY conversation history from Supabase&lt;/span&gt;
  &lt;span class="na"&gt;tools&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;roxyTools&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;                &lt;span class="c1"&gt;// grounded facts, computed not guessed&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That is it. That is the thing being sold as the premium tier. Four reasons I would not rent it:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Voice is the product.&lt;/strong&gt; 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.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;I pay the model markup twice.&lt;/strong&gt; Per-query AI pricing is a margin on tokens I can buy directly from Anthropic or OpenAI at cost.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;I cannot swap models.&lt;/strong&gt; 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.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;It does not know my user.&lt;/strong&gt; 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.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;Here is the asymmetry that settles it. &lt;strong&gt;The LLM layer is an afternoon. The ephemeris layer is not.&lt;/strong&gt; 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.&lt;/p&gt;

&lt;h2&gt;
  
  
  Per-user memory: stateless calculation, stateful you
&lt;/h2&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Layer&lt;/th&gt;
&lt;th&gt;Who owns it&lt;/th&gt;
&lt;th&gt;What it holds&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Calculation&lt;/td&gt;
&lt;td&gt;RoxyAPI (stateless)&lt;/td&gt;
&lt;td&gt;charts, transits, forecast, numerology&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Identity and birth data&lt;/td&gt;
&lt;td&gt;My Supabase&lt;/td&gt;
&lt;td&gt;user, birth date/time, resolved location&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Reading history&lt;/td&gt;
&lt;td&gt;My Supabase&lt;/td&gt;
&lt;td&gt;every reading I have shown, timestamped&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Semantic recall&lt;/td&gt;
&lt;td&gt;Supabase pgvector&lt;/td&gt;
&lt;td&gt;embeddings of past readings and chat&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Two Supabase tables carry almost all of it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;create&lt;/span&gt; &lt;span class="k"&gt;table&lt;/span&gt; &lt;span class="n"&gt;profiles&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="n"&gt;user_id&lt;/span&gt;     &lt;span class="n"&gt;uuid&lt;/span&gt; &lt;span class="k"&gt;primary&lt;/span&gt; &lt;span class="k"&gt;key&lt;/span&gt; &lt;span class="k"&gt;references&lt;/span&gt; &lt;span class="n"&gt;auth&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;users&lt;/span&gt; &lt;span class="k"&gt;on&lt;/span&gt; &lt;span class="k"&gt;delete&lt;/span&gt; &lt;span class="k"&gt;cascade&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;birth_date&lt;/span&gt;  &lt;span class="nb"&gt;date&lt;/span&gt;        &lt;span class="k"&gt;not&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;birth_time&lt;/span&gt;  &lt;span class="nb"&gt;time&lt;/span&gt;        &lt;span class="k"&gt;not&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;latitude&lt;/span&gt;    &lt;span class="nb"&gt;double&lt;/span&gt; &lt;span class="nb"&gt;precision&lt;/span&gt; &lt;span class="k"&gt;not&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;longitude&lt;/span&gt;   &lt;span class="nb"&gt;double&lt;/span&gt; &lt;span class="nb"&gt;precision&lt;/span&gt; &lt;span class="k"&gt;not&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;timezone&lt;/span&gt;    &lt;span class="nb"&gt;text&lt;/span&gt;        &lt;span class="k"&gt;not&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;     &lt;span class="c1"&gt;-- IANA string, e.g. "Europe/Lisbon"&lt;/span&gt;
  &lt;span class="n"&gt;natal_chart&lt;/span&gt; &lt;span class="n"&gt;jsonb&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;                    &lt;span class="c1"&gt;-- computed ONCE, never again&lt;/span&gt;
  &lt;span class="n"&gt;created_at&lt;/span&gt;  &lt;span class="n"&gt;timestamptz&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="n"&gt;now&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;create&lt;/span&gt; &lt;span class="k"&gt;table&lt;/span&gt; &lt;span class="n"&gt;readings&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="n"&gt;id&lt;/span&gt;         &lt;span class="n"&gt;bigserial&lt;/span&gt; &lt;span class="k"&gt;primary&lt;/span&gt; &lt;span class="k"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;user_id&lt;/span&gt;    &lt;span class="n"&gt;uuid&lt;/span&gt; &lt;span class="k"&gt;references&lt;/span&gt; &lt;span class="n"&gt;auth&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;users&lt;/span&gt; &lt;span class="k"&gt;on&lt;/span&gt; &lt;span class="k"&gt;delete&lt;/span&gt; &lt;span class="k"&gt;cascade&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;kind&lt;/span&gt;       &lt;span class="nb"&gt;text&lt;/span&gt; &lt;span class="k"&gt;not&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;             &lt;span class="c1"&gt;-- 'daily' | 'timeline' | 'chat'&lt;/span&gt;
  &lt;span class="n"&gt;payload&lt;/span&gt;    &lt;span class="n"&gt;jsonb&lt;/span&gt; &lt;span class="k"&gt;not&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;            &lt;span class="c1"&gt;-- the structured API response&lt;/span&gt;
  &lt;span class="n"&gt;shown&lt;/span&gt;      &lt;span class="nb"&gt;text&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;                      &lt;span class="c1"&gt;-- what the user actually read&lt;/span&gt;
  &lt;span class="n"&gt;embedding&lt;/span&gt;  &lt;span class="n"&gt;vector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1536&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="n"&gt;created_at&lt;/span&gt; &lt;span class="n"&gt;timestamptz&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="n"&gt;now&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;alter&lt;/span&gt; &lt;span class="k"&gt;table&lt;/span&gt; &lt;span class="n"&gt;profiles&lt;/span&gt; &lt;span class="n"&gt;enable&lt;/span&gt; &lt;span class="k"&gt;row&lt;/span&gt; &lt;span class="k"&gt;level&lt;/span&gt; &lt;span class="k"&gt;security&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;alter&lt;/span&gt; &lt;span class="k"&gt;table&lt;/span&gt; &lt;span class="n"&gt;readings&lt;/span&gt; &lt;span class="n"&gt;enable&lt;/span&gt; &lt;span class="k"&gt;row&lt;/span&gt; &lt;span class="k"&gt;level&lt;/span&gt; &lt;span class="k"&gt;security&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;Now the single most important function in the codebase, and it is twelve lines:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;createRoxy&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@roxyapi/sdk&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;roxy&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;createRoxy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ROXY_API_KEY&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;ensureChart&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;birth&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;BirthData&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;row&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;supabase&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;from&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;profiles&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;select&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;natal_chart&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;eq&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;user_id&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;single&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;row&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;natal_chart&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;row&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;natal_chart&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;          &lt;span class="c1"&gt;// 0 API calls, forever&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;chart&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;roxy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;astrology&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;generateNatalChart&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;birth&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;supabase&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;from&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;profiles&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;update&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;natal_chart&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;chart&lt;/span&gt; &lt;span class="p"&gt;}).&lt;/span&gt;&lt;span class="nf"&gt;eq&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;user_id&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;chart&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;                                          &lt;span class="c1"&gt;// exactly 1 billable call, ever&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;And geocode the city once, at signup, rather than making a human type coordinates:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;roxy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;location&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;searchCities&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;query&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;q&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Lisbon&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;latitude&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;longitude&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;timezone&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cities&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;span class="c1"&gt;// =&amp;gt; { latitude: 38.71667, longitude: -9.13333, timezone: "Europe/Lisbon", utcOffset: 1, ... }&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That IANA string is doing heavy lifting. Pass &lt;code&gt;"Europe/Lisbon"&lt;/code&gt; 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.&lt;/p&gt;

&lt;h2&gt;
  
  
  The forecast timeline is the companion heartbeat
&lt;/h2&gt;

&lt;p&gt;Here is the endpoint that made the product feel alive, and honestly the reason I stopped shopping around.&lt;/p&gt;

&lt;p&gt;A companion needs to know what is coming up for &lt;strong&gt;this specific person&lt;/strong&gt;, ranked by how much it matters, so it can bring things up unprompted. That is one call:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;timeline&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;roxy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;forecast&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;generateTimeline&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;birthData&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;date&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;1994-03-11&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;time&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;08:20:00&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;timezone&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Europe/Lisbon&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                 &lt;span class="na"&gt;latitude&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;38.71667&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;longitude&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mf"&gt;9.13333&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="na"&gt;startDate&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;2026-08-01&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;endDate&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;2026-09-15&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;domains&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;western&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;vedic&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;biorhythm&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="na"&gt;minSignificance&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;70&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Real response from production, trimmed for length, nothing fabricated:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"count"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"events"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"date"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2026-08-12"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"datetime"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2026-08-12T17:45:46Z"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"domain"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"western"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"eclipse"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"body"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Sun"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"kind"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"total"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"description"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"total solar eclipse as the New Moon aligns with the Sun, a major timing event that opens a new chapter."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"significance"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;95&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"date"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2026-09-09"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"domain"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"western"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"transit-aspect"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"body"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Pluto"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"target"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Venus"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"aspect"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"sextile"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"orb"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"significance"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Two things to notice.&lt;/p&gt;

&lt;p&gt;That eclipse is real. &lt;a href="https://science.nasa.gov/eclipses/future-eclipses/total-solar-eclipse-on-august-12-2026/" rel="noopener noreferrer"&gt;NASA has it&lt;/a&gt;: 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.&lt;/p&gt;

&lt;p&gt;And &lt;code&gt;significance&lt;/code&gt; 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.&lt;/p&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;h2&gt;
  
  
  One conversation, 12+ domains, one key
&lt;/h2&gt;

&lt;p&gt;This is the part I did not see coming, and it ended up being the reason people stayed.&lt;/p&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;The version I shipped is one namespace per domain on one key:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;forecast&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;card&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;hd&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;hex&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;life&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;all&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
  &lt;span class="nx"&gt;roxy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;forecast&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;generateTimeline&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;birthData&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;birth&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;domains&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;western&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;}),&lt;/span&gt;
  &lt;span class="nx"&gt;roxy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;tarot&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getDailyCard&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;seed&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;date&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;today&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;}),&lt;/span&gt;
  &lt;span class="nx"&gt;roxy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;humanDesign&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;generateBodygraph&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;birth&lt;/span&gt; &lt;span class="p"&gt;}),&lt;/span&gt;
  &lt;span class="nx"&gt;roxy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;iching&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;castReading&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;query&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;seed&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;userId&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;}),&lt;/span&gt;
  &lt;span class="nx"&gt;roxy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;numerology&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;calculateLifePath&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;year&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1994&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;month&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;day&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;11&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;}),&lt;/span&gt;
&lt;span class="p"&gt;]);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Same key. Same bill. Same error handling. Real values from that exact run:&lt;/p&gt;

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

&lt;p&gt;Note the &lt;code&gt;seed&lt;/code&gt; 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.&lt;/p&gt;

&lt;p&gt;For the agent layer this gets better. Rather than hand-writing a tool definition per endpoint, you can point the model at the hosted &lt;a href="https://roxyapi.com/docs/mcp" rel="noopener noreferrer"&gt;Remote MCP servers&lt;/a&gt; (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.&lt;/p&gt;

&lt;p&gt;All 12+ domains ship on every plan, and I pay the same whether I call one or all of them:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Western astrology&lt;/li&gt;
&lt;li&gt;Vedic astrology&lt;/li&gt;
&lt;li&gt;Forecast&lt;/li&gt;
&lt;li&gt;Human design&lt;/li&gt;
&lt;li&gt;Numerology&lt;/li&gt;
&lt;li&gt;Tarot&lt;/li&gt;
&lt;li&gt;Biorhythm&lt;/li&gt;
&lt;li&gt;I Ching&lt;/li&gt;
&lt;li&gt;Crystals&lt;/li&gt;
&lt;li&gt;Dreams&lt;/li&gt;
&lt;li&gt;Angel numbers&lt;/li&gt;
&lt;li&gt;Location and timezone&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;h2&gt;
  
  
  I did not write a single chart renderer
&lt;/h2&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;So I did not. &lt;code&gt;@roxyapi/ui&lt;/code&gt; 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.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"https://cdn.jsdelivr.net/npm/@roxyapi/ui@0/dist/cdn/roxy-ui.js"&lt;/span&gt; &lt;span class="na"&gt;defer&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;roxy-natal-chart&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"wheel"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/roxy-natal-chart&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"module"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="c1"&gt;// the chart you already cached in Supabase. No refetch, no extra billable call.&lt;/span&gt;
  &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;wheel&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;chartFromYourServer&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That is the whole integration. The ones I use are &lt;code&gt;&amp;lt;roxy-natal-chart&amp;gt;&lt;/code&gt;, &lt;code&gt;&amp;lt;roxy-forecast-timeline&amp;gt;&lt;/code&gt;, &lt;code&gt;&amp;lt;roxy-tarot-spread&amp;gt;&lt;/code&gt;, &lt;code&gt;&amp;lt;roxy-bodygraph&amp;gt;&lt;/code&gt;, &lt;code&gt;&amp;lt;roxy-hexagram&amp;gt;&lt;/code&gt; and &lt;code&gt;&amp;lt;roxy-numerology-card&amp;gt;&lt;/code&gt;, and there are equivalents for Vedic kundli, dashas, panchang, biorhythm and the rest.&lt;/p&gt;

&lt;p&gt;The bit I want to underline for anyone doing the build-versus-buy sum: this is not just saved frontend hours, it is saved &lt;strong&gt;domain-expert&lt;/strong&gt; 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 &lt;a href="https://roxyapi.com/ui" rel="noopener noreferrer"&gt;/ui&lt;/a&gt;, and if you are dropping a reading onto a site with no build step at all there are copy-paste &lt;a href="https://roxyapi.com/widgets" rel="noopener noreferrer"&gt;widgets&lt;/a&gt; that use a browser-safe publishable key instead.&lt;/p&gt;

&lt;h2&gt;
  
  
  The napkin math that decides if this is a business
&lt;/h2&gt;

&lt;p&gt;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.&lt;/p&gt;

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

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

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Call type&lt;/th&gt;
&lt;th&gt;Frequency&lt;/th&gt;
&lt;th&gt;Calls/month&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Natal chart&lt;/td&gt;
&lt;td&gt;New users only, cached forever&lt;/td&gt;
&lt;td&gt;200&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Location lookup&lt;/td&gt;
&lt;td&gt;New users only&lt;/td&gt;
&lt;td&gt;200&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Forecast timeline&lt;/td&gt;
&lt;td&gt;1 per user per month&lt;/td&gt;
&lt;td&gt;2,000&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Daily horoscope&lt;/td&gt;
&lt;td&gt;12 signs x 30 days, &lt;strong&gt;shared by everyone&lt;/strong&gt;
&lt;/td&gt;
&lt;td&gt;360&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Agent tool calls&lt;/td&gt;
&lt;td&gt;~3 per user per month, uncached&lt;/td&gt;
&lt;td&gt;6,000&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Total&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;8,760&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;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 &lt;strong&gt;$389 a year&lt;/strong&gt; on annual billing.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;API spend as a share of revenue: &lt;strong&gt;0.04 percent&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;API cost per subscriber per year: &lt;strong&gt;$0.195&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Cost per request: about &lt;strong&gt;$0.0013&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;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 &lt;strong&gt;120,000 calls a month&lt;/strong&gt;, which is 5x the plan allowance and roughly 13x the traffic for a product that is byte-for-byte identical from the user side.&lt;/p&gt;

&lt;p&gt;Same app. Same features. One of them is a business and the other is a bonfire. The difference is &lt;code&gt;ensureChart&lt;/code&gt; and about four hours of thinking.&lt;/p&gt;

&lt;p&gt;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 &lt;code&gt;(sign, date)&lt;/code&gt; and that line never grows.&lt;/p&gt;

&lt;h2&gt;
  
  
  The three-minute bug that nearly shipped
&lt;/h2&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;My signup form collected birth time with a rounded minute picker. Seemed harmless. Then I ran the same chart three minutes apart:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;08:20:00  -&amp;gt;  Pisces  29.32 deg   (longitude 359.325)
08:23:00  -&amp;gt;  Aries    0.58 deg   (longitude   0.581)
08:25:00  -&amp;gt;  Aries    1.42 deg   (longitude   1.419)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;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 &lt;code&gt;degree&lt;/code&gt; 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.&lt;/p&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;h2&gt;
  
  
  FAQ
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;What is the best astrology API for building an AI companion?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Can I just ask ChatGPT or Claude to calculate a birth chart?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Does RoxyAPI store my users birth data or conversations?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How many API calls does an app like this actually need?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Is there one API that covers astrology, tarot, numerology, and human design together?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Do I have to build the chart wheel and bodygraph UI myself?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;No. &lt;code&gt;@roxyapi/ui&lt;/code&gt; 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.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Do I need an AGPL licence to build a commercial astrology product?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where I landed
&lt;/h2&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;If you want the deeper versions of what I sketched here, the &lt;a href="https://roxyapi.com/docs/tutorials/ai-companion-with-memory" rel="noopener noreferrer"&gt;AI companion with memory tutorial&lt;/a&gt; covers the recall layer properly and &lt;a href="https://roxyapi.com/docs/tutorials/complete-birth-profile" rel="noopener noreferrer"&gt;complete birth profile&lt;/a&gt; shows the full cross-domain fan-out including the trick of stripping editorial prose before it reaches the model. The &lt;a href="https://github.com/RoxyAPI/astrology-ai-chatbot" rel="noopener noreferrer"&gt;chatbot starter&lt;/a&gt; is MIT and clonable if you would rather begin from something that already runs.&lt;/p&gt;

&lt;p&gt;Now if you will excuse me, I have some mugs to wash.&lt;/p&gt;

</description>
      <category>nextjs</category>
      <category>typescript</category>
      <category>ai</category>
      <category>supabase</category>
    </item>
    <item>
      <title>How to choose an astrology API: build vs buy, MCP, and the licensing trap nobody mentions</title>
      <dc:creator>Nick</dc:creator>
      <pubDate>Wed, 17 Jun 2026 03:31:14 +0000</pubDate>
      <link>https://dev.to/prowler64/how-to-choose-an-astrology-api-build-vs-buy-mcp-and-the-licensing-trap-nobody-mentions-3f2b</link>
      <guid>https://dev.to/prowler64/how-to-choose-an-astrology-api-build-vs-buy-mcp-and-the-licensing-trap-nobody-mentions-3f2b</guid>
      <description>&lt;p&gt;I spent the back half of last year shipping an AI app that wires five systems into one chat UI: natal charts, vedic kundli, numerology, human design, and tarot. I am a developer, not an astrologer. So the part I actually had to get right was not the readings, it was the build-versus-buy call on the calculation layer underneath them.&lt;/p&gt;

&lt;p&gt;I went in assuming this was a quick decision. Pick the cheapest provider, move on. It was not. The threads I found were all "just use Swiss Ephemeris" or "here are the top 10 horoscope APIs," and none of them finished the sentence on the things that bit me later. This is the checklist I wish I had read first, in the order that actually matters.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;TL;DR&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The calculation engine and its license matter more than the feature list. A lot of providers wrap a 1990s desktop library that carries an AGPL obligation into your closed-source app.&lt;/li&gt;
&lt;li&gt;Endpoint counts are mostly marketing. Ask for a live OpenAPI spec you can open and count, and count real systems, not output formats.&lt;/li&gt;
&lt;li&gt;For an AI app, hosted (remote) MCP versus a local server you babysit is a real time difference, not a nice-to-have.&lt;/li&gt;
&lt;li&gt;Get structured JSON back, not a prose blob, so you own the interpretation layer.&lt;/li&gt;
&lt;li&gt;Flat pricing where one request equals one unit keeps your bill boring. Credit wallets do not.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Start with the calculation engine, not the feature list
&lt;/h2&gt;

&lt;p&gt;Look at what computes the math before you look at anything else, because that is the one decision you cannot cheaply reverse. Here is the thing almost nobody says out loud: most established astrology APIs are wrappers around the same library, Swiss Ephemeris, a C library from the late 1990s originally written for single-user desktop astrology software. When a provider advertises "Swiss Ephemeris precision," that is the actual claim. They wrapped a 25-year-old desktop library and put a REST endpoint in front of it.&lt;/p&gt;

&lt;p&gt;Two problems with betting an AI product on that in 2026.&lt;/p&gt;

&lt;p&gt;The license is the big one. Swiss Ephemeris is AGPL-3.0 unless you buy a commercial license. The AGPL network-use clause means a closed-source SaaS built on it can be obligated to open-source the whole app. Most people repeating "just use Swiss Ephemeris" have never opened the LICENSE file, and this is the kind of thing that surfaces during due diligence or an acquisition, not at a convenient time. Either pay for the commercial license, open-source your product, or use an API whose engine is not AGPL. Decide before you write code.&lt;/p&gt;

&lt;p&gt;The second problem is fit. Desktop, single user, one chart at a time. It was never designed for APIs, serverless, or agents calling tools in parallel.&lt;/p&gt;

&lt;p&gt;That combination is why I stopped trying to self-host an engine. RoxyAPI runs its own, called Roxy Ephemeris, built for APIs and agents, with no AGPL strings attached. The part that earned my trust was that they publish the proof instead of asking for faith. There is an open, MIT-licensed benchmark at &lt;a href="https://github.com/RoxyAPI/astrology-api-benchmark" rel="noopener noreferrer"&gt;github.com/RoxyAPI/astrology-api-benchmark&lt;/a&gt; comparing 210 planet positions across 21 charts against NASA JPL Horizons, which is the physics ground truth rather than another library copying another library. The published median difference is about 16 arc-seconds, max around 32, and the Moon comes in tightest at roughly 3. I re-ran a couple of my own birth dates before I trusted any of it. Most providers say "NASA-grade" and hope you never ask for the numbers.&lt;/p&gt;

&lt;p&gt;If you want to poke at real responses before reading further, &lt;a href="https://roxyapi.com/api-reference" rel="noopener noreferrer"&gt;roxyapi.com/api-reference&lt;/a&gt; is a live playground with a pre-filled test key. It returns real production data, not a fake sandbox, so you can audit before you pay.&lt;/p&gt;

&lt;h2&gt;
  
  
  Count real domains, not formats
&lt;/h2&gt;

&lt;p&gt;When a provider leads with "300+ endpoints across 8 domains," slow down and check how that number is built, because two tricks show up over and over.&lt;/p&gt;

&lt;p&gt;First, the big endpoint number is often backed by a Postman collection rather than a live spec, so the truly callable count is a fraction of the headline.&lt;/p&gt;

&lt;p&gt;Second, and this is the one that fooled me at first, the domain count gets padded by slicing one system into many. Horoscopes, natal charts, compatibility, and synastry are all astrology. Counted separately they become four "domains," and a love calculator or a PDF report gets the same treatment. RoxyAPI keeps all of that under a single &lt;code&gt;astrology&lt;/code&gt; domain, the way a developer would actually group it. So a dozen honest domains can look smaller on a marketing page than a padded "eight." That is the whole trick: a provider sitting on one real system can out-breadth a genuine multi-domain platform on paper, and a non-technical buyer cannot tell the difference. Open the spec and the padding disappears.&lt;/p&gt;

&lt;p&gt;The check that worked for me: ask for a live OpenAPI spec, an actual JSON URL you can open and count, and count genuinely distinct systems instead of formats.&lt;/p&gt;

&lt;p&gt;One more thing to watch in the spec itself. A few providers hand-roll their OpenAPI file by hand, and the request schema in the doc does not match what the endpoint actually accepts. You copy the documented body, send it, and get a 400 back, with no clue why. Not cool, and a real trust killer the first time it happens. A spec that is generated from the running service cannot drift like that. RoxyAPI publishes one per domain, generated from production, so what you read is what the endpoint takes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# count callable endpoints and tag groups, no auth needed&lt;/span&gt;
curl &lt;span class="nt"&gt;-s&lt;/span&gt; https://roxyapi.com/api/v2/openapi.json | jq &lt;span class="s1"&gt;'{endpoints: (.paths|length), tags: (.tags|length)}'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When I ran that it returned around 150 endpoints across roughly a dozen real domains, and the per-domain specs at &lt;code&gt;/api/v2/{slug}/openapi.json&lt;/code&gt; match what production actually serves. That is the number I could plan against, not a Postman export that drifts from the live API. One call returns a complete natal chart instead of fragmenting it across ten separately billed endpoints, which also keeps the bill honest.&lt;/p&gt;

&lt;h2&gt;
  
  
  Check how the API talks to AI agents
&lt;/h2&gt;

&lt;p&gt;If you are building anything agentic, look for remote (hosted) MCP, not a local stdio server you run yourself, because that gap is an afternoon of work per machine. RoxyAPI exposes a hosted Model Context Protocol server per domain over Streamable HTTP. In Claude Code I registered one with a single command and it was callable in seconds:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;claude mcp add-json &lt;span class="nt"&gt;--scope&lt;/span&gt; user roxy-astrology &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="s1"&gt;'{"type":"http","url":"https://roxyapi.com/mcp/astrology","headers":{"X-API-Key":"YOUR_KEY"}}'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After that, every endpoint in that domain shows up as a tool. I can ask the agent "build me a React birth chart page" and it picks the right endpoint, reads the response shape, and wires it in. The alternative most places offer is a local server you clone, Docker, and keep alive yourself, which is fine on your laptop and miserable across a team or a CI box.&lt;/p&gt;

&lt;p&gt;The reason I care beyond convenience: a hosted MCP server lets my own agent ground its answers in verified calculations and reply in my product's voice, instead of me renting someone else's locked AI wrapper with a hidden system prompt. I bring my own model. The API brings the facts. That split is the whole point.&lt;/p&gt;

&lt;p&gt;There is a second piece that made this the fastest integration I have done. Roxy ships an &lt;code&gt;AGENTS.md&lt;/code&gt; playbook, bundled inside each SDK and also served at the site root, plus a machine-readable &lt;code&gt;llms.txt&lt;/code&gt;. So I barely hand-held the agent at all. I pointed Claude Code at the &lt;code&gt;AGENTS.md&lt;/code&gt;, it read the rules, connected the MCP server, and had working calls in a few minutes. For getting an insight platform running inside an AI agent, that is about as close to zero setup as it gets right now.&lt;/p&gt;

&lt;p&gt;It also shows up in the design, and this was a real differentiator once I noticed it. A lot of providers in this space are older astrology services that bolted MCP and "AI support" onto a REST API from years ago, after the hype landed. You can feel it: the AI layer sits next to the product instead of inside it. Roxy was built agent-first, so the MCP servers, the structured responses, and the agent docs are one surface, not three afterthoughts stapled on later.&lt;/p&gt;

&lt;h2&gt;
  
  
  Look at the response shape before you commit
&lt;/h2&gt;

&lt;p&gt;Pull one real response and check whether you get discrete fields or a paragraph, because that single difference decides how much of your product you actually own. A weak API hands back prose you parse with regex and pray the format never changes. A good one hands back typed fields you read directly. Here is an actual daily horoscope response shape:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"sign"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Leo"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"date"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2026-05-22"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"overview"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"The Moon activates your first house of identity..."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"love"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"The Moon stirs Leo with deep emotional awareness..."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"career"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Mars powers up your tenth house of career..."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"energyRating"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"luckyNumber"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;41&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"luckyColor"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Gold"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"moonPhase"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Waxing Crescent Moon"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"compatibleSigns"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"Aries"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Sagittarius"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Gemini"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;My app reads &lt;code&gt;energyRating&lt;/code&gt; or &lt;code&gt;compatibleSigns&lt;/code&gt; straight off the object. I can ship the built-in interpretation text when I want speed, or feed only the primitives into my own model when I want my own voice, so it stops hallucinating charts. There is also localized text on translated endpoints. Adding &lt;code&gt;?lang=es&lt;/code&gt; returns the same reading in Spanish without me touching anything:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="s2"&gt;"https://roxyapi.com/api/v2/astrology/horoscope/leo/daily?lang=es"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"X-API-Key: &lt;/span&gt;&lt;span class="nv"&gt;$ROXY_API_KEY&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;What you want to avoid is a provider that returns one fixed prose report with nothing structured underneath. You cannot recompose it, localize it, or run your own model over it, so you are stuck with their wording forever.&lt;/p&gt;

&lt;h2&gt;
  
  
  Pricing: flat versus credit wallets
&lt;/h2&gt;

&lt;p&gt;Check whether one request costs one unit or whether different endpoints burn different amounts of credits, because the second model gets expensive in ways you cannot forecast. My app needs five systems. A lot of providers bill per product, so that is five separate subscriptions, or they bill per-call credits where a PDF or a "premium" endpoint quietly costs 50x a normal call. RoxyAPI is one flat subscription that includes every domain, the remote MCP servers, the SDKs, and the i18n, with no per-product fee and no credit weighting.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Model&lt;/th&gt;
&lt;th&gt;What you pay&lt;/th&gt;
&lt;th&gt;What happens as you grow&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Per-product subscriptions&lt;/td&gt;
&lt;td&gt;One sub per domain&lt;/td&gt;
&lt;td&gt;Five systems means five bills to reconcile&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Credit wallet&lt;/td&gt;
&lt;td&gt;Variable credits per endpoint&lt;/td&gt;
&lt;td&gt;"Premium" calls drain the wallet unpredictably&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Flat, one request equals one unit&lt;/td&gt;
&lt;td&gt;One sub, all domains&lt;/td&gt;
&lt;td&gt;The bill is a function of call count, full stop&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Entry pricing was around 39 dollars a month for the whole bundle when I signed up, which for a dozen-ish domains is roughly 3 dollars per domain. Stitching the same coverage from single-domain vendors quoted me well past 100 a month. And because these calculations are deterministic, identical input gives identical output forever, so I cache them. The bill flattens as users grow instead of scaling linearly.&lt;/p&gt;

&lt;p&gt;One thing I did not expect to matter, and then did: the platform keeps shipping new domains, and my flat plan picks them up automatically. Human design and forecast both landed after I signed up, and I did not buy anything or change tiers to use them. New endpoints even show up in the SDKs on their own, because the SDKs are generated from the same spec. With the per-product vendors, every new system is another subscription to evaluate and approve. Here the breadth grows under the same key, so the "dozen domains" number is a floor, not a ceiling.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I could not find anywhere else
&lt;/h2&gt;

&lt;p&gt;After evaluating a stack of providers, a few things were genuinely unique to one of them, and they are the reason I stopped shopping. I am listing the ones I verified myself, not the ones on a marketing page.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;What it is&lt;/th&gt;
&lt;th&gt;Why it changed my decision&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;One key across a dozen domains, including I Ching, dream symbols, crystals, and angel numbers&lt;/td&gt;
&lt;td&gt;No other single API I tried covered the long-tail domains. Everyone else was astrology-only or astrology plus one extra.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Hosted MCP for every domain, no local server&lt;/td&gt;
&lt;td&gt;Other providers either had no MCP or shipped a local stdio wrapper you run yourself.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;A public, runnable accuracy benchmark against NASA JPL Horizons&lt;/td&gt;
&lt;td&gt;I could re-run it. Nobody else published numbers I could reproduce.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;An engine with no AGPL or copyleft obligation&lt;/td&gt;
&lt;td&gt;The providers that name their stack name AGPL or GPL libraries, which is a license footgun for a closed-source product.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;A live OpenAPI spec per domain that matches production&lt;/td&gt;
&lt;td&gt;Several competitors only had a Postman export or a hand-rolled spec, which drifts from the real API.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;New domains ship into the same flat plan&lt;/td&gt;
&lt;td&gt;Existing subscribers get new systems automatically. The per-product vendors charge again for each one.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Agent-first design (MCP, AGENTS.md, llms.txt as one surface)&lt;/td&gt;
&lt;td&gt;Most providers retrofitted AI tooling onto an older REST product, and it shows.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The honest version: a couple of these exist in isolation somewhere. The combination, in one key, did not exist anywhere else I looked.&lt;/p&gt;

&lt;h2&gt;
  
  
  What real calls look like
&lt;/h2&gt;

&lt;p&gt;For any chart endpoint, geocode the city first. Every coordinate-dependent call needs latitude, longitude, and a timezone, and the location search hands you all three:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="s2"&gt;"https://roxyapi.com/api/v2/location/search?q=Berlin&amp;amp;limit=1"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"X-API-Key: &lt;/span&gt;&lt;span class="nv"&gt;$ROXY_API_KEY&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"total"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"limit"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"offset"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"cities"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"city"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Berlin"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"province"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Berlin"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"country"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Germany"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"latitude"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;52.52&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"longitude"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;13.405&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"timezone"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Europe/Berlin"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"utcOffset"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now feed those into the natal chart endpoint. Prefer the IANA timezone string over a decimal offset, since it handles historical DST correctly:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-X&lt;/span&gt; POST https://roxyapi.com/api/v2/astrology/natal-chart &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"X-API-Key: &lt;/span&gt;&lt;span class="nv"&gt;$ROXY_API_KEY&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Content-Type: application/json"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'{
    "date": "1990-07-15",
    "time": "14:30:00",
    "latitude": 52.52,
    "longitude": 13.405,
    "timezone": "Europe/Berlin",
    "houseSystem": "placidus"
  }'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The response comes back with &lt;code&gt;ascendant&lt;/code&gt;, &lt;code&gt;planets&lt;/code&gt;, &lt;code&gt;houses&lt;/code&gt;, &lt;code&gt;aspects&lt;/code&gt;, &lt;code&gt;midheaven&lt;/code&gt;, and a &lt;code&gt;summary&lt;/code&gt;, each as structured data. If you would rather use the typed SDK, the method names are generated from the spec, so the same call in TypeScript reads cleanly and returns a &lt;code&gt;{ data, error }&lt;/code&gt; pair:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;createRoxy&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@roxyapi/sdk&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;roxy&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;createRoxy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ROXY_API_KEY&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;roxy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;astrology&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;generateNatalChart&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;date&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;1990-07-15&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;time&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;14:30:00&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;latitude&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;52.52&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;longitude&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;13.405&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;timezone&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Europe/Berlin&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;ascendant&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;planets&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Numerology is the same pattern with a smaller body. The Life Path endpoint takes three integers and returns the number plus a step-by-step &lt;code&gt;calculation&lt;/code&gt; string, which I appreciated because I could show users how the number was derived:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-X&lt;/span&gt; POST https://roxyapi.com/api/v2/numerology/life-path &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"X-API-Key: &lt;/span&gt;&lt;span class="nv"&gt;$ROXY_API_KEY&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Content-Type: application/json"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'{ "year": 1990, "month": 7, "day": 15 }'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You are not stuck with curl either. There are typed SDKs for TypeScript (&lt;code&gt;@roxyapi/sdk&lt;/code&gt;), Python (&lt;code&gt;roxy-sdk&lt;/code&gt;), and PHP (&lt;code&gt;roxyapi/sdk&lt;/code&gt;), and their method names are generated from the spec, so autocomplete just works (&lt;code&gt;generateNatalChart&lt;/code&gt;, &lt;code&gt;calculateLifePath&lt;/code&gt;, &lt;code&gt;getDailyHoroscope&lt;/code&gt;). If you live in WordPress, there is an official plugin on &lt;a href="https://wordpress.org/plugins/roxyapi/" rel="noopener noreferrer"&gt;wordpress.org&lt;/a&gt; with shortcodes and Gutenberg blocks, so a non-developer can drop a horoscope on a page without writing code. Every domain also has its own guide with copy-paste examples, plus integration docs for the no-code tools, which meant I rarely had to guess at a field name. Methodology and verification details live at &lt;a href="https://roxyapi.com/methodology" rel="noopener noreferrer"&gt;roxyapi.com/methodology&lt;/a&gt; if you want the accuracy story before wiring anything.&lt;/p&gt;

&lt;h2&gt;
  
  
  When you should build it yourself instead
&lt;/h2&gt;

&lt;p&gt;Buying is not always right, and pretending otherwise would make this whole post less useful. If you need exactly one domain, you have in-house astro or astronomy expertise, and you are comfortable either buying the Swiss Ephemeris commercial license or open-sourcing your app, then self-hosting a single-purpose engine is a perfectly good call. A dedicated KP-only or matrimonial-only build can also out-depth any general bundle on its one niche, because depth in a single tradition is its own moat.&lt;/p&gt;

&lt;p&gt;For a multi-system app where I needed verified math across five domains, an engine that is not AGPL, and agents that call tools over MCP, the tradeoff was not close. The 70 percent you can rough out in a weekend is the easy part. The hard 30 percent, which is cross-domain schema consistency, gold-standard verification, clean licensing, remote MCP, SDKs, and localization, is months of work each, and it is the part a serious user notices first when it is wrong.&lt;/p&gt;

&lt;h2&gt;
  
  
  FAQ
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;How do I choose an astrology API for an AI app?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Evaluate the calculation engine and its license first, then the response shape, then how it exposes tools to agents, then pricing. For an AI app specifically, look for structured JSON you can ground your own model on, a hosted MCP server so you are not running a local process, and an engine with no AGPL obligation. RoxyAPI covers all four with a dozen domains under one key, a remote MCP server per domain, and a public accuracy benchmark.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Do most astrology APIs use Swiss Ephemeris, and does it matter?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Many established providers wrap Swiss Ephemeris, a 1990s desktop C library that is AGPL-3.0 unless you buy a commercial license. For a closed-source SaaS the AGPL network-use clause can obligate you to open-source your whole app. It matters a lot. RoxyAPI runs its own engine, Roxy Ephemeris, verified against NASA JPL Horizons, with no AGPL strings, so you avoid the licensing question entirely.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Is it cheaper to use one multi-domain API or several single-domain ones?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;For more than one or two domains, one flat multi-domain subscription is almost always cheaper and simpler than stitching single-domain vendors together. RoxyAPI bundles roughly a dozen domains for about 39 dollars a month at entry, near 3 dollars per domain, against well over 100 a month to assemble the same coverage separately. Deterministic results are also cacheable, so the bill flattens as usage grows.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How can I verify an astrology API is actually accurate?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Ask for published numbers you can reproduce, not a "NASA-grade" slogan. RoxyAPI publishes an open, MIT-licensed benchmark of 210 planet positions across 21 charts against NASA JPL Horizons, with a median difference around 16 arc-seconds. You can clone it and re-run it. If a provider cannot point you to reproducible numbers, treat the accuracy claim as marketing.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Do I pay more when RoxyAPI adds new domains or features?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;No. One flat subscription covers every current domain and every one added later, with no separate purchase. Human design and forecast both shipped as included domains after launch. New endpoints also appear in the typed SDKs automatically, because the SDKs are generated from the same OpenAPI spec, so your client picks them up on the next update.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What does MCP support mean for an astrology API?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;MCP, the Model Context Protocol, lets an AI agent call the API as a set of tools. The useful version is a remote (hosted) server you point your agent at with one command, rather than a local server you run and maintain. RoxyAPI ships a hosted MCP server per domain over Streamable HTTP, so Claude Code, Cursor, or your own agent can ground answers in verified calculations while answering in your own voice.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;The decision that mattered was not which provider had the longest feature list. It was the engine, its license, the response shape, and whether the thing was built for agents or retrofitted for them. RoxyAPI was the one that checked every box without an AGPL asterisk, and it let me spend my real time on the product instead of the math. Start at &lt;a href="https://roxyapi.com/api-reference" rel="noopener noreferrer"&gt;roxyapi.com/api-reference&lt;/a&gt; with the pre-filled test key and run a couple of calls before you decide anything.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>api</category>
      <category>ai</category>
      <category>programming</category>
    </item>
  </channel>
</rss>
