1. Docs
  2. What To Build
  3. Daily Horoscope Widget

Build a daily horoscope widget

Ship a 12-sign horoscope page in pure HTML with no framework and no backend. Time to ship: 20 minutes.

This is the fastest end-to-end build in the Roxy catalog. No framework, no bundler, no backend. Double-click the file, it opens in the browser, clicking a sign renders a card. Perfect for wellness embeds, lifestyle apps, and landing-page widgets.

What you can build with this

  • Standalone daily horoscope pages
  • Wellness and meditation app daily-check-in cards
  • Landing-page widgets that refresh automatically
  • Weekly and monthly variants with the same render code
  • Personalized forecasts by layering on POST /astrology/transits with a saved natal chart

What you need, 30 seconds

  1. A Roxy API key. Get one on the pricing page.
  2. A text editor and a browser. That is it.

Step 1, call your first endpoint

One GET, one sign, everything you need to render a card. Pick a language.

curl "https://roxyapi.com/api/v2/astrology/horoscope/aries/daily" \
  -H "X-API-Key: $ROXY_API_KEY"

Response fields to use: sign, date, overview, love, career, health, finance, advice, luckyNumber, luckyColor, compatibleSigns[], moonPhase, energyRating (1-10).

Step 2, create the HTML file

Create horoscope.html and paste this whole block. It ships 12 buttons and renders the selected sign.

<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8" />
  <title>Daily Horoscope</title>
  <style>
    * { box-sizing: border-box; margin: 0; padding: 0; }
    body { font-family: system-ui, sans-serif; max-width: 600px; margin: 0 auto; padding: 20px; background: #fafafa; }
    h1 { text-align: center; margin-bottom: 8px; }
    .subtitle { text-align: center; color: #666; margin-bottom: 24px; }
    .signs { display: grid; grid-template-columns: repeat(4, 1fr); gap: 8px; margin-bottom: 24px; }
    .signs button { padding: 12px 4px; border: 2px solid #e0e0e0; border-radius: 12px; background: white; cursor: pointer; font-size: 14px; }
    .signs button:hover { border-color: #7c3aed; background: #f5f0ff; }
    .signs button.active { border-color: #7c3aed; background: #7c3aed; color: white; }
    .signs .emoji { font-size: 24px; display: block; margin-bottom: 4px; }
    .result { background: white; border-radius: 16px; padding: 24px; display: none; }
    .result h2 { margin-bottom: 4px; }
    .result .date { color: #666; font-size: 14px; margin-bottom: 16px; }
    .overview { font-size: 16px; line-height: 1.6; margin-bottom: 20px; }
    .cats { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; margin-bottom: 16px; }
    .cat { background: #f9fafb; padding: 12px; border-radius: 8px; }
    .cat .label { font-size: 12px; font-weight: 600; text-transform: uppercase; color: #666; }
    .cat .text { font-size: 14px; line-height: 1.5; }
    .lucky { text-align: center; padding: 12px; background: #f5f0ff; border-radius: 8px; font-size: 14px; }
    .loading { text-align: center; padding: 40px; color: #666; }
  </style>
</head>
<body>
  <h1>Daily Horoscope</h1>
  <p class="subtitle">Pick your sign to see the forecast for today</p>

  <div class="signs" id="signs"></div>
  <div id="result" class="result"></div>

  <script>
    const API_KEY = 'YOUR_API_KEY';
    const SIGNS = [
      ['aries', 'Aries', '♈'], ['taurus', 'Taurus', '♉'],
      ['gemini', 'Gemini', '♊'], ['cancer', 'Cancer', '♋'],
      ['leo', 'Leo', '♌'], ['virgo', 'Virgo', '♍'],
      ['libra', 'Libra', '♎'], ['scorpio', 'Scorpio', '♏'],
      ['sagittarius', 'Sagittarius', '♐'], ['capricorn', 'Capricorn', '♑'],
      ['aquarius', 'Aquarius', '♒'], ['pisces', 'Pisces', '♓'],
    ];

    const grid = document.getElementById('signs');
    for (const [slug, label, glyph] of SIGNS) {
      const b = document.createElement('button');
      b.innerHTML = '<span class="emoji">' + glyph + '</span>' + label;
      b.onclick = () => load(b, slug);
      grid.appendChild(b);
    }

    async function load(btn, sign) {
      document.querySelectorAll('.signs button').forEach(b => b.classList.remove('active'));
      btn.classList.add('active');
      const el = document.getElementById('result');
      el.style.display = 'block';
      el.innerHTML = '<div class="loading">Reading the stars...</div>';

      const res = await fetch('https://roxyapi.com/api/v2/astrology/horoscope/' + sign + '/daily', {
        headers: { 'X-API-Key': API_KEY }
      });
      const data = await res.json();

      el.innerHTML =
        '<h2>' + data.sign + ' horoscope</h2>' +
        '<div class="date">' + data.date + '</div>' +
        '<div class="overview">' + data.overview + '</div>' +
        '<div class="cats">' +
          '<div class="cat"><div class="label">Love</div><div class="text">' + data.love + '</div></div>' +
          '<div class="cat"><div class="label">Career</div><div class="text">' + data.career + '</div></div>' +
          '<div class="cat"><div class="label">Health</div><div class="text">' + data.health + '</div></div>' +
          '<div class="cat"><div class="label">Finance</div><div class="text">' + data.finance + '</div></div>' +
        '</div>' +
        '<div class="lucky">Lucky number <strong>' + data.luckyNumber + '</strong> ' +
        '&middot; Lucky color <strong>' + data.luckyColor + '</strong> ' +
        '&middot; Compatible <strong>' + data.compatibleSigns.join(', ') + '</strong></div>';
    }
  </script>
</body>
</html>

Replace YOUR_API_KEY. Save. Double-click to open.

The key sits in browser code here because this is a zero-infrastructure demo. Do not ship it this way to users. For anything public, proxy the call through a backend route (the Next.js guide shows the pattern). Anyone can view page source.

Step 3, deploy

Two options, both free.

Cloudflare Pages. Drag the file into a new project at pages.cloudflare.com. Global CDN, HTTPS, deploys in under a minute.

Vercel. Put the file in an empty folder, run npx vercel. Same result.

For a real app, move the API key to an environment variable and proxy through a serverless function. One file becomes two files, the client calls your function, the function calls Roxy.

Gotchas

  • Sign slugs are lowercase. aries not Aries. The path is case-insensitive server-side but lowercase is the convention.
  • Weekly and monthly have extra fields. Switch the URL to /weekly or /monthly and data.week, data.luckyDays[], data.weekByWeek[], data.keyDates[] become available.
  • Do not ship the key in the browser for production. Proxy through a backend. The demo HTML above is for local prototyping only.
  • compatibleSigns is capitalized, but the path param is lowercase. Easy to trip over when templating.

What to build next

  • Upgrade to weekly and monthly variants. Same shape plus extra fields. See the astrology guide.
  • Auto-detect the sign from a user birthday to skip the button grid.
  • Extend the UI with transit data. POST /astrology/transits with a saved natal chart returns personalized aspects for the day.
  • The dating app tutorial shows two-person compatibility on top of the same astrology surface.