:::note
**TL;DR**
- WordPress runs 42.2% of the public web (W3Techs, May 2026), so an astrology widget for WordPress is the highest-leverage place to ship a horoscope, natal chart, or kundli feature.
- The single canonical pattern: store the API key in `wp-config.php`, register a shortcode with `add_shortcode`, call RoxyAPI through `wp_remote_get` or `wp_remote_post`, cache responses with `set_transient`.
- One key, ten domains. The same five lines that ship daily horoscope ship Vedic kundli, dream symbol lookup, and Life Path numerology.
- Build this with the [Astrology API](/products/astrology-api "production-ready Astrology API with daily horoscopes and natal charts") in under 20 minutes.
:::

WordPress dominates the public web. The May 2026 W3Techs reading puts it at 42.2% of all websites, with WooCommerce alone covering roughly 20% of every WordPress install. That makes WordPress the single biggest distribution surface for any horoscope plugin, daily-card widget, kundli generator, or numerology calculator. The bar to ship one is not framework knowledge. The bar is one shortcode, one API call, and one transient cache. This guide walks the canonical pattern end to end with the daily horoscope, then scales it to natal charts, Vedic kundli, dreams, and Life Path numerology, all on the same key.

## How do I add an astrology widget to WordPress in under 20 minutes?

Register a shortcode that calls the Astrology API and returns HTML. That is the entire pattern. WordPress shortcodes are PHP callbacks bound to a tag like `[roxy_horoscope sign="leo"]`; once registered, the tag works in every post, page, widget area, and Gutenberg Shortcode block. The four moving parts: an API key in `wp-config.php`, a callback that runs `wp_remote_get`, a `set_transient` cache so identical requests do not double-bill against rate limits, and HTML output sanitized with `esc_html`. The full daily horoscope shortcode is 30 lines, ships as a single Code Snippets entry, and survives theme switches.

Total time for a developer who has SFTP access or the Code Snippets plugin: under 20 minutes from blank site to rendered horoscope card. If you want the canonical multi-domain reference with every snippet inline, the [WordPress integration guide](/docs/integrations/wordpress "complete RoxyAPI WordPress integration guide with shortcodes, REST routes, and caching") is the long-form companion to this post.

## Where should I store the RoxyAPI key on WordPress?

In `wp-config.php` as a PHP constant. That places the key outside the database, outside any theme file, and outside the request body. WordPress reads `wp-config.php` on every request, so a `define` call for `ROXY_API_KEY` becomes available globally with zero startup cost. The Code Snippets plugin path is the second-best option for sites without SFTP access; it stores the snippet in the `wp_snippets` table, so any database export contains the key, but the boundary between the key and frontend code is preserved.

:::warning
Never put the API key in a theme file that loads on the frontend, a `wp_options` row, a custom field, post meta, a JavaScript bundle, or a REST response. Any of those either ships the key to the browser or surfaces it in a routine database dump.
:::

Ready to build this? [Astrology API](/products/astrology-api "production-ready Astrology API with daily horoscopes and natal charts") gives you all 10 spiritual domains behind one key with 130+ endpoints, MCP-first agent support, and accuracy verified against NASA JPL Horizons. [See the full API reference](/api-reference "RoxyAPI Scalar reference with live test key").

The four practical places to put the PHP code, ranked by survivability:

| Location | Persists across | Best for | Caveats |
|---|---|---|---|
| Code Snippets plugin | Theme switches, plugin updates | Non-developers, hosted WordPress, fastest path | Snippet stored in the database, included in every export |
| Child theme `functions.php` | Parent theme updates | Sites with one fixed theme | Lost when the active theme changes |
| `mu-plugins/` directory | Theme switches, plugin pages, core updates | Sites where the widget is permanent infrastructure | Requires SFTP or file-manager access |
| Custom plugin (in `plugins/`) | Theme switches, version control | Sites where you want to ship the widget separately or to clients | Requires plugin file scaffolding |

Avoid editing the parent theme `functions.php` directly. Theme updates wipe it.

## The daily horoscope shortcode end to end

The daily horoscope is the single highest-volume astrology query on the web, so it is the right place to start. The endpoint is `GET /api/v2/astrology/horoscope/{sign}/daily` and the response carries `sign`, `overview`, `love`, `career`, `health`, `finance`, `advice`, `luckyNumber`, `luckyColor`, `compatibleSigns`, `activeTransits`, `moonSign`, `moonPhase`, and `energyRating`. Field names are camelCase. Match them byte-for-byte; a `lucky_number` reference returns blank because the field is `luckyNumber`.

:::tip
Wrap every external call in `get_transient` and `set_transient`. Daily content does not change within the day, so an hour-long transient saves at least 24 calls per sign per day, keeps your rate-limit headroom for the chart endpoints that actually cost compute, and makes the widget render in microseconds on every cache hit.
:::

:::tabs

### Shortcode definition

```php
<?php
add_shortcode( 'roxy_horoscope', 'roxy_horoscope_shortcode' );

function roxy_horoscope_shortcode( $atts ) {
    $atts  = shortcode_atts( array( 'sign' => 'aries' ), $atts, 'roxy_horoscope' );
    $sign  = sanitize_key( $atts['sign'] );
    $valid = array( 'aries','taurus','gemini','cancer','leo','virgo','libra','scorpio','sagittarius','capricorn','aquarius','pisces' );
    if ( ! in_array( $sign, $valid, true ) || ! defined( 'ROXY_API_KEY' ) ) {
        return '<p>Horoscope unavailable.</p>';
    }
    $cache_key = 'roxy_horoscope_' . $sign;
    $cached    = get_transient( $cache_key );
    if ( false !== $cached ) {
        return $cached;
    }
    $response = wp_remote_get(
        'https://roxyapi.com/api/v2/astrology/horoscope/' . $sign . '/daily',
        array( 'headers' => array( 'X-API-Key' => ROXY_API_KEY ), 'timeout' => 10 )
    );
    if ( is_wp_error( $response ) || 200 !== wp_remote_retrieve_response_code( $response ) ) {
        return '<p>Could not load today horoscope.</p>';
    }
    $data = json_decode( wp_remote_retrieve_body( $response ), true );
    $html  = '<div class="roxy-horoscope">';
    $html .= '<h3>' . esc_html( $data['sign'] ) . ' daily horoscope</h3>';
    $html .= '<p>' . esc_html( $data['overview'] ) . '</p>';
    $html .= '<p><strong>Love:</strong> ' . esc_html( $data['love'] ) . '</p>';
    $html .= '<p><strong>Career:</strong> ' . esc_html( $data['career'] ) . '</p>';
    $html .= '<p>Lucky number ' . intval( $data['luckyNumber'] ) . ', lucky color ' . esc_html( $data['luckyColor'] ) . '.</p>';
    $html .= '</div>';
    set_transient( $cache_key, $html, HOUR_IN_SECONDS );
    return $html;
}
```

### curl preview

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

Sample response shape (trimmed):

```json
{
  "sign": "Leo",
  "date": "2026-05-04",
  "overview": "...",
  "love": "...",
  "career": "...",
  "luckyNumber": 7,
  "luckyColor": "Gold",
  "compatibleSigns": ["Aries", "Sagittarius", "Gemini"],
  "moonPhase": "Waxing Gibbous Moon",
  "energyRating": 7
}
```

:::

Drop `[roxy_horoscope sign="leo"]` into a Shortcode block in any post or page. The card renders the sign heading, daily overview, love line, career line, and lucky number plus color, all sanitized.

### Custom REST proxy for client-side widgets

If you want a JavaScript widget that fetches a fresh sign on click, register a custom REST route that proxies the call. The browser only sees `/wp-json/roxy/v1/horoscope/leo`; the API key never leaves the server.

```php
add_action( 'rest_api_init', function () {
    register_rest_route( 'roxy/v1', '/horoscope/(?P<sign>[a-z]+)', array(
        'methods'             => 'GET',
        'callback'            => 'roxy_rest_horoscope',
        'permission_callback' => '__return_true',
    ) );
} );

function roxy_rest_horoscope( $request ) {
    $sign = strtolower( $request['sign'] );
    if ( ! defined( 'ROXY_API_KEY' ) ) {
        return new WP_Error( 'no_key', 'Key missing', array( 'status' => 500 ) );
    }
    $cache_key = 'roxy_rest_horoscope_' . $sign;
    $cached    = get_transient( $cache_key );
    if ( false !== $cached ) {
        return rest_ensure_response( $cached );
    }
    $response = wp_remote_get(
        'https://roxyapi.com/api/v2/astrology/horoscope/' . $sign . '/daily',
        array( 'headers' => array( 'X-API-Key' => ROXY_API_KEY ), 'timeout' => 10 )
    );
    $data = json_decode( wp_remote_retrieve_body( $response ), true );
    set_transient( $cache_key, $data, HOUR_IN_SECONDS );
    return rest_ensure_response( $data );
}
```

After registering, visit **Settings, Permalinks** in `wp-admin` and click **Save**. WordPress regenerates the rewrite rules; without that, the new route returns a 404.

## How to scale from horoscope to natal charts, kundli, and Life Path

The shortcode pattern generalizes to every endpoint on the platform. Swap the URL, swap the request method, swap the field names, keep the cache. The four most common widgets a WordPress site ships beyond daily horoscope:

1. **Natal chart.** `POST /api/v2/astrology/natal-chart` with body `{ date, time, latitude, longitude, timezone, houseSystem }`. The `time` field is `HH:MM:SS`, not `HH:MM`. The response carries `planets[]`, `houses[]`, `aspects[]`, `ascendant`, `midheaven`, `summary`. Use `wp_remote_post` and `wp_json_encode` for the body, plus the `Content-Type: application/json` header. Cache for `DAY_IN_SECONDS` because birth charts are immutable.
2. **Vedic kundli.** `POST /api/v2/vedic-astrology/birth-chart` with the same `date, time, latitude, longitude, timezone` body. Response is keyed by sign (`aries`, `taurus`, ..., `pisces`); each sign carries `signs[]` with planet placements including `nakshatra.name` and `nakshatra.pada`. The `timezone` field accepts decimal hours (`5.5`) or IANA strings (`Asia/Kolkata`).
3. **Life Path number.** `POST /api/v2/numerology/life-path` with body `{ year, month, day }` as integers (not a `birthDate` string). Response carries `number`, `calculation`, `type`, `hasKarmicDebt`, and `meaning` with `title`, `keywords`, `description`, `strengths`, `challenges`, `career`, `relationships`, `spirituality`. Cache forever (`WEEK_IN_SECONDS`).
4. **Dream symbol lookup.** `GET /api/v2/dreams/symbols?q=snake` for search or `/api/v2/dreams/symbols/{id}` for a specific symbol by kebab-case identifier. Response shape includes `id`, `name`, `letter`, `meaning`. Static dictionary content, so cache for `WEEK_IN_SECONDS`.

For coordinate-dependent endpoints (natal chart, kundli), call `GET /api/v2/location/search?q={city}` first to resolve the user input into `latitude`, `longitude`, and an IANA timezone, then feed those into the chart request. Prefer IANA strings over decimal offsets; decimals do not handle daylight saving and produce ascendants that drift by up to four arcminutes during DST windows. The companion [add daily horoscopes in 10 lines](/blogs/add-daily-horoscopes-any-app-10-lines "minimal daily horoscope integration walkthrough") post covers the minimal version of this pattern in any framework.

## Common gotchas (cURL SSL, permalink refresh, object cache, IANA timezones)

Six failure modes account for almost every broken first install. The first three are platform-level; the last three are content-level.

| Gotcha | Symptom | Fix |
|---|---|---|
| Stale CA bundle | `wp_remote_get` returns `cURL error 60: SSL certificate problem` | Ask the host to update the CA bundle. Do not disable SSL verification; that breaks the security boundary |
| New REST route returns 404 | Custom `register_rest_route` URL gives 404 from the browser | Visit Settings, Permalinks, click Save once. WordPress regenerates rewrite rules |
| Object cache plugin ate the transient | Transient disappears after every cache flush | Switch to `update_option` for durability, or accept the per-flush re-fetch and tune `HOUR_IN_SECONDS` accordingly |
| Decimal timezone offset | Natal chart ascendant drifts by minutes around DST boundaries | Pass IANA strings like `Europe/London` instead of `0` or `1` |
| Wrong field casing | `null` rendered for lucky number on the horoscope card | Use `luckyNumber` not `lucky_number`. All response fields are camelCase, verified per route |
| `time` set to `HH:MM` | Natal chart returns 400 validation error | Use `HH:MM:SS` format. The Zod schema rejects two-segment time |

Two more not in the table but worth flagging. WordPress.com Free does not allow plugins or custom code; you need the Business plan or higher for `wp_remote_get` to even exist in scope. And every accuracy or methodology claim on a horoscope card or kundli page should link to [the methodology page](/methodology "RoxyAPI calculation methodology and accuracy verification") rather than asserting figures inline; the methodology surface keeps the citation up to date as the test corpus grows.

## FAQ

**How do I add astrology to WordPress without writing PHP?**

Install the free Code Snippets plugin from the WordPress.org plugin directory and paste the shortcode PHP into a new snippet. The plugin gives you a UI to manage PHP without touching theme files, and the snippet survives theme switches. The first snippet defines `ROXY_API_KEY`, the second registers `[roxy_horoscope]`, and you drop the shortcode into any post or page using the Shortcode block.

**Is RoxyAPI compatible with WordPress.com?**

Yes on the Business plan and higher. WordPress.com Free and Personal plans block plugins and custom PHP, so neither path works. On the Business, Commerce, or Enterprise plan you can install the Code Snippets plugin and use the same shortcode pattern as a self-hosted install. Self-hosted WordPress (the wordpress.org variant) supports it on every host.

**Do I need a dedicated plugin for this?**

No. A single shortcode registered through Code Snippets, a child theme `functions.php`, or a one-file mu-plugin is enough to ship a horoscope card, natal chart, or numerology calculator. A dedicated plugin is the right move only when you plan to ship the widget to multiple client sites and want versioned releases.

**Can I cache RoxyAPI responses on WordPress?**

Yes, and you should. WordPress ships `set_transient` and `get_transient` for short-lived caching with no extra setup. Use `HOUR_IN_SECONDS` for daily horoscope, `DAY_IN_SECONDS` for natal charts, `WEEK_IN_SECONDS` for static dictionary content like dream symbols and angel numbers. If your host runs a Redis or Memcached object cache plugin, transients persist there automatically.

**Does this work with WooCommerce?**

Yes. The pattern is identical: register the shortcode, hook a callback to `woocommerce_thankyou` or `woocommerce_order_status_completed`, call the RoxyAPI endpoint, store the result on the order as order meta, and email it via `wc_get_template`. A common build is a Life Path numerology report that runs on checkout completion and ships in the order confirmation email.

**Where does the API key live in production?**

In `wp-config.php` as a `define` call for the `ROXY_API_KEY` constant, above the line that reads `That is all, stop editing`. The constant is available in every PHP file, never reaches the database, and never ships to the browser. For sites without SFTP access, the Code Snippets path stores the constant in the `wp_snippets` table, which is the next-best boundary.

## Conclusion

WordPress is where the largest astrology, horoscope, and numerology audiences live, and the shortcode pattern in this post is the same five lines whether the widget shipping is a daily horoscope card, a Vedic kundli generator, or a Life Path calculator. The canonical multi-domain reference at [the WordPress integration guide](/docs/integrations/wordpress "complete RoxyAPI WordPress integration guide") covers every snippet inline, and the [Astrology API product page](/products/astrology-api "production-ready Astrology API behind one key") lists every endpoint that drops into the same callback. One key, ten domains, every shortcode the same pattern.