🌍 Astrocartography API and ✋ Palmistry API are now live. Ship them in your app today.Get Started
Blog/Use cases

How to automate daily horoscope content

A horoscope site needs fresh text for twelve signs every morning. The pipeline is one cron job and twelve requests. The hard parts are time zones, duplicate text, and getting anyone to read it.

May 8, 2026·10 min read·AstrologyAPI Team
In brief
  • The whole pipeline costs about 424 API requests a month. That covers twelve signs, fetched daily, weekly, and monthly.
  • Calling the API on every pageview makes about 1,667 times more requests at 20,000 views a day. The text is exactly the same.
  • Run the job on your readers’ local day. A job at UTC midnight leaves Auckland reading yesterday’s horoscope all morning.
  • The horoscope endpoints return English only. You translate the stored text yourself, as one stage in your pipeline.
  • A new site will not rank for "daily horoscope". Reach readers through channels you own: push, email, widgets, and bots.

A horoscope site needs fresh content every morning. It needs twelve signs, each with daily, weekly, and monthly text. If people write all of it, you are running a newsroom. If an API writes it, you need one scheduled job and twelve HTTP requests.

The fetch is the easy part. Three things cause trouble. Time zones break the meaning of “today”. The twelve pages you publish look like everyone else’s. And making content is not the same as getting people to read it. This page covers the setup, the cost math, and the fix for each problem.

Why horoscope content brings readers back

Most content ages. You write an article once. It ranks or it does not. A year later it needs an update that nobody has time for.

Horoscope content works the other way. The same twelve slots refill on a set schedule. Every refill gives readers a fresh reason to come back.

Count the output. Twelve signs with daily, weekly, and monthly text means thirty-six live documents at any moment. The daily row alone produces 4,380 documents a year. No writer touches any of them, so the cost stays low while the content keeps coming.

But there is a trap. Anyone who calls the same API can publish the same amount. The pipeline alone does not set you apart. We come back to this problem, and its fix, two sections from now.

What the horoscope endpoints return

Every horoscope endpoint is a POST to https://json.astrologyapi.com/v1. Auth is HTTP Basic. Your user ID is the username. Your API key is the password. The zodiac sign goes in the path, in lowercase. The body has one field, a timezone float. That is a UTC offset in hours. It defaults to 5.5 if you leave it out.

EndpointCadenceResponse fields
sun_sign_prediction/daily/:zodiacNameTodaypersonal_life, profession, health, emotions, travel, luck
sun_sign_prediction/daily/next/:zodiacNameTomorrowstatus, sun_sign, prediction_date, prediction (six fields)
sun_sign_prediction/daily/previous/:zodiacNameYesterdaystatus, sun_sign, prediction_date, prediction (six fields)
sun_sign_consolidated/daily/:zodiacNameTodaystatus, sun_sign, prediction_date, prediction (one paragraph)
horoscope_prediction/weekly/:zodiacNameWeekstatus, sun_sign, week_start_date, prediction (array)
horoscope_prediction/monthly/:zodiacNameMonthstatus, sun_sign, prediction_month, prediction (array)

Plan for two shapes. The daily endpoint returns six flat strings, one per life area, with no wrapper. Weekly and monthly return a wrapper object whose prediction field is an array of paragraphs. Write two parsers, one for each shape. One shared parser will break.

Watch the date formats. week_start_date comes back as 14-6-2026. That is day-month-year, with no zero padding. And prediction_month is a month name, like January. Neither one parses as an ISO 8601 date. Convert both when the data comes in. Store the converted value, and keep the raw response next to it.

Request and response shapeshttp
POST https://json.astrologyapi.com/v1/sun_sign_prediction/daily/leo
Authorization: Basic base64(USER_ID:API_KEY)
Content-Type: application/json

{ "timezone": 5.5 }

200 OK
{
  "personal_life": "...",
  "profession": "...",
  "health": "...",
  "emotions": "...",
  "travel": "...",
  "luck": "..."
}

POST https://json.astrologyapi.com/v1/horoscope_prediction/weekly/leo

{ "timezone": 5.5 }

200 OK
{
  "status": true,
  "sun_sign": "leo",
  "week_start_date": "14-6-2026",
  "prediction": ["...", "...", "..."]
}

The field-by-field details for every endpoint are in the Horoscope API docs.

One honest limitation
These endpoints return English only. Some of our other suites return many languages. The horoscope set does not. Translation is a stage in your own pipeline. There is no request parameter for it. Plan for that cost before you promise a reader a horoscope in Tamil.

Fetch once a day and store the result

This system has four parts, and the plain version of each part wins. A scheduler fires at a fixed local time. A worker fetches twelve signs. It writes each response to a store, keyed by sign and date. The read path serves whatever the store holds.

CRON05:00 AUDIENCE-LOCAL12 SIGNS1 FETCH EACHSTOREKEY (SIGN, DATE)STATIC PAGESN READERS, 0 CALLS
One scheduled job makes twelve requests and stores one document per sign per day. Readers get pages from the store, and their traffic never reaches the API.

Reader traffic never touches the API. This one rule makes the whole system cheap. It rests on one fact. The API writes a daily prediction once per sign per day. A second call for the same sign on the same day returns the same text. So a fresh fetch for each visitor gives you nothing new.

scripts/build-horoscopes.mjsjavascript
// scripts/build-horoscopes.mjs
// One scheduled run writes one document per sign per day.
import { mkdir, writeFile } from 'node:fs/promises'

const SIGNS = [
  'aries', 'taurus', 'gemini', 'cancer', 'leo', 'virgo',
  'libra', 'scorpio', 'sagittarius', 'capricorn', 'aquarius', 'pisces',
]

const BASE = 'https://json.astrologyapi.com/v1'
const AUTH =
  'Basic ' +
  Buffer.from(
    `${process.env.ASTROLOGYAPI_USER_ID}:${process.env.ASTROLOGYAPI_API_KEY}`,
  ).toString('base64')

// The UTC offset of the audience you publish for, not of the server.
const AUDIENCE_TZ = 5.5

async function fetchSign(sign) {
  const response = await fetch(`${BASE}/sun_sign_prediction/daily/${sign}`, {
    method: 'POST',
    headers: { Authorization: AUTH, 'Content-Type': 'application/json' },
    body: JSON.stringify({ timezone: AUDIENCE_TZ }),
  })
  if (!response.ok) {
    throw new Error(`${sign}: ${response.status} ${await response.text()}`)
  }
  return response.json()
}

async function main() {
  const day = new Date(Date.now() + AUDIENCE_TZ * 3600 * 1000)
    .toISOString()
    .slice(0, 10)
  const dir = `content/horoscope/${day}`
  await mkdir(dir, { recursive: true })

  const results = await Promise.allSettled(SIGNS.map(fetchSign))
  const failed = []

  for (const [index, result] of results.entries()) {
    const sign = SIGNS[index]
    if (result.status === 'rejected') {
      failed.push(`${sign}: ${result.reason.message}`)
      continue
    }
    await writeFile(
      `${dir}/${sign}.json`,
      JSON.stringify({ sign, day, prediction: result.value }, null, 2),
    )
  }

  // A missing file means that sign falls back to yesterday's copy.
  // A silent failure means a blank page, so fail loudly instead.
  if (failed.length) {
    failed.forEach((line) => console.error(line))
    process.exit(1)
  }
}

main()

Two details in that script matter. First, Promise.allSettled keeps one failed sign from blocking the other eleven. Second, the non-zero exit code gives your scheduler something to alert on. Retry logic belongs here too. Retry on 5xx errors and network timeouts, with a pause between tries. Never retry on 4xx errors. A bad request fails the same way every time you send it. The production checklist has the wrapper we recommend.

The read path is boring, and that is the goal. On a static export, the scheduled run commits the files and starts a rebuild. On a server, swap the file read for a cache lookup and keep the rest.

pages/horoscope/[sign].jsxjavascript
// pages/horoscope/[sign].jsx
// The read path opens a file. It never calls the API.
import { readFile } from 'node:fs/promises'

export async function getStaticPaths() {
  return { paths: SIGNS.map((sign) => ({ params: { sign } })), fallback: false }
}

export async function getStaticProps({ params }) {
  const day = process.env.HOROSCOPE_DAY // set by the same scheduled run
  const doc = JSON.parse(
    await readFile(`content/horoscope/${day}/${params.sign}.json`, 'utf8'),
  )
  return { props: { doc } }
}

Keep more than one day in the store. Old documents cost almost nothing to keep, and they give you two things. You get an archive page for each sign. You also get a fallback when a run fails. The page can then show an older prediction with a clear date, instead of a blank slot. Label it honestly in the UI. Do not present yesterday’s text as today’s.

There is an even safer option. sun_sign_prediction/daily/next/:zodiacName returns tomorrow’s prediction. So a run at 05:00 can write today’s document and tomorrow’s at the same time. If the next run fails, tomorrow’s page is already on disk. The matching daily/previous endpoint fills in an archive you started late. Both double the daily request count, from twelve to twenty-four. That is still a tiny number.

The math

Twelve signs fetched once a day is twelve requests. Add a weekly pull, which is twelve requests a week. Add a monthly pull, which is twelve a month. The full pipeline then runs at about 424 requests in a thirty-day month. That is 5,148 a year: 4,380 daily, 624 weekly, 144 monthly.

Now price the version with no cache. Say a site serves 20,000 horoscope pageviews a day and calls the API on each view. That makes 20,000 requests a day and 600,000 a month. Every Leo who loads the page that day gets the exact same text. Same output, about 1,667 times the request volume.

12
Fetches per day
424
Requests per month
1,667Ă—
Fewer calls at 20k views/day

Credit cost per endpoint varies. The current rates are in the pricing catalog. Check there, and do not trust a number in a blog post. The pattern holds at any rate. With a cache, your spend stays flat as traffic grows. With no cache, spend grows with every pageview. If you would rather pay a fixed monthly price, the Horoscope suite starts at $89 (₹3,500) a month. The wallet model fits traffic you can’t predict yet. Either way, the 150 free credits on signup cover building and testing the whole pipeline.

Start building with real ephemeris data
150 free credits. No card required.

A complete example: the Telegram bot

The horoscope Telegram bot guide shows this pattern as running code. It is a complete build in Python. The bot answers /horoscope <sign> on demand and saves subscribers to disk. Every morning, a scheduled job sends each subscriber the text for their sign.

It follows the same caching rule as this article. The bot caches by (sign, date). So the daily job calls the API once per sign, no matter how many people subscribe. Ten Leo subscribers cost one request. On a website, the subscribers become pages. Nothing else about the shape changes.

The time zone problem

This is the most common bug in this kind of system. Your job runs at 00:05 UTC, writes twelve files, and marks them as today. But in Auckland it is already 12:05 in the afternoon. Readers there spent the whole morning on yesterday’s horoscope. In Los Angeles, the same run publishes tomorrow’s page at 17:05 today.

One job at UTC midnight is late for Oceania and early for the Americas at the same time. The fix is to stop treating “today” as a server-side idea. Use your readers’ local day instead. Key each file to that day. Run the job at a fixed local hour in each market you serve. Pass that market’s UTC offset in the request body.

The extra cost is small. Three regions means 36 requests a day instead of 12, about 1,080 a month. That is still under a five-hundredth of the per-pageview version. Store the region in the cache key, next to sign and date. The read path stays a simple lookup.

Weekly content has its own boundary, and the response shows you where it is. Each weekly payload includes a week_start_date. Refetch when that value changes. Do not guess which weekday starts a new week. The monthly payload names its month in prediction_month for the same reason. Let the data tell you when the cache is stale.

One more warning about the offset. The timezone field is a plain number. It does not accept a zone name. And half the world shifts that number twice a year for daylight saving. New York is -4.0 in July and -5.0 in December. So work out the offset from the place and the date. Never hardcode it. You would do the same for a birth chart. The guide to birth times and timezones covers this logic in full.

How to avoid publishing duplicate text

Now back to the trap. Say your twelve sign pages hold the raw API response and nothing else. Then any other customer of any horoscope API can publish the same twelve pages. A search engine judges the page it sees. It cannot see the pipeline behind it. Nothing on that page is yours.

Two fixes work, and they combine well. The first fix is editorial. Wrap the API text in something a human chose. That could be a weekly column across all twelve signs, a local angle, or a note that links today’s text to yesterday’s. The second fix is grounded rewriting. A language model rewrites the prediction in your own tone. The API response stays the only source of fact.

Grounding needs strict rules, written into the system prompt. The model may rephrase what is in the payload. It may not add a planet position, a date, or any claim the payload does not hold. We use the same tool-use pattern for chart readings, and the LLM grounding guide shows how to build it. Run the rewrite once per document in the pipeline, never once per request. Per-request rewrites bring back the per-pageview cost, with a more costly vendor.

Translation follows the same rule. Fetch the English document once. Translate it into each language you serve. Store each translation next to the source. The translation bill then grows with documents and languages. It stays flat as readers grow.

Why you will not rank for “daily horoscope”

This section is the bad news. Read it before you build the rest.

Search for “daily horoscope” and look at page one. You will find big newspapers, magazine brands, and astrology publishers. Many of them printed this content before the web existed. Brand demand and domain history decide those results. A new site publishing twelve fresh pages a day moves neither one. Your pages will be correct and well made. And still, nobody will find them.

You will not rank for “daily horoscope” by publishing more pages. You reach readers through channels you own.

The projects that make money from this content reach readers directly:

  • A push notification from an app that already has users.
  • A morning email with twelve segments and one send.
  • A widget that partner sites can embed, with your brand under the content.
  • A Telegram or WhatsApp bot that people choose to join.

In each case, the reader already chose you. The daily refresh is what keeps them coming back.

Search still pays in a few narrow spots. It works for specific queries that mix sign, topic, and date range. It works in languages and regions where the big brands publish thin translations or nothing at all. And it works for queries about your own product. Search is a good way to serve people who already know you. It is a weak way to find new readers.

If the whole plan is to rank for “daily horoscope”, the pipeline is not what stops you. Building it faster will not help.

What to build first

  1. Start with one sign and one endpoint, called from a script you run by hand. Confirm the response shape and the auth before you schedule anything.
  2. Next, add the scheduler and the store. Fetch twelve signs. Write one document per sign and date. Keep yesterday’s copy as the fallback, and exit non-zero when a fetch fails.
  3. Then build the render layer: editorial framing, tone, and translation, each applied once per document.
  4. Last, work on reaching readers, before you add weekly and monthly text. Twelve daily pages nobody reads is a smaller problem than 424 monthly requests nobody reads.

Steps one and two take an afternoon. Step four takes the rest of the year. That split is normal, and it is why the pipeline was never the hard part.

Frequently asked questions

Is there an API for daily horoscopes?

Yes. The Horoscope suite has POST endpoints for daily, weekly, and monthly predictions per zodiac sign, including sun_sign_prediction/daily/:zodiacName and horoscope_prediction/weekly/:zodiacName. Auth is HTTP Basic: your user ID and API key.

How often does horoscope data update?

Once per calendar day per sign. A second call for the same sign on the same day returns the same text. Weekly responses include a week_start_date. Monthly responses include a prediction_month. Cache on those keys and refetch when the value changes.

Can I translate horoscope content into other languages?

Not through the API. The horoscope endpoints return English only, so translation is a stage in your own pipeline. Translate each stored document once per language, right after the daily fetch. Cost then grows with documents and languages. It does not grow with readers.

Do I need astrology knowledge to run a horoscope site?

No, not to run the pipeline. The predictions are computed and written upstream. You do need editorial judgment: how to frame the text, what tone to use, and how to label it. Never present generated text as personal advice.

Further reading
Start building with real ephemeris data
150 free credits. No card required.
Related