Official RoxyAPI SDKs for TypeScript, Python, PHP, C#, Go, and Rust give your app methods for natal charts, compatibility readings, forecasts and the rest of the catalog, all based on the published OpenAPI request contract. Use this guide to resolve a birthplace, call a chart endpoint and handle its response in your server code. The WordPress plugin adds Gutenberg blocks and shortcodes for sites built in a page editor.
C# / .NET:RoxyApi.Sdk on NuGet (.NET 8+ and netstandard2.0, so it also runs on .NET Framework, Unity, and MAUI)
Go:github.com/RoxyAPI/sdk-go on pkg.go.dev (Go 1.24+, one direct runtime dependency, standard library net/http)
Rust:roxyapi on crates.io (Rust 1.88+, async on Tokio)
WordPress:RoxyAPI plugin on the WordPress.org Plugin Directory
UI components:@roxyapi/ui, @roxyapi/ui-react, and @roxyapi/ui-vue on npm. Open-source web components for charts, tables, cards, and forms across every domain. Setup walkthrough at /docs/ui (Next.js, vanilla HTML, shadcn). Catalog and live customizer at /ui.
Install
bash
npm install @roxyapi/sdk
bash
bun add @roxyapi/sdk
bash
pnpm add @roxyapi/sdk
bash
yarn add @roxyapi/sdk
bash
pip install roxy-sdk
bash
uv add roxy-sdk
bash
poetry add roxy-sdk
bash
composer require roxyapi/sdk
bash
dotnet add package RoxyApi.Sdk
bash
go get github.com/RoxyAPI/sdk-go
bash
cargo add roxyapi
cargo add tokio --features macros,rt-multi-thread
The Tokio features above enable standalone async examples. An existing async service can use its current runtime. Run the snippets below inside an async function with use roxyapi::types; and the initialized roxy client from the quick start.
In WordPress admin: Plugins → Add New → search RoxyAPI → Install Now → Activate. The plugin adds its own top-level RoxyAPI menu; open RoxyAPI → Settings to paste your API key. Full integration guide at /docs/integrations/wordpress. Prefer the latest unreleased code? Grab roxyapi.zip from GitHub Releases and upload via Plugins → Add New → Upload Plugin.
Quick start
Initialize the client once and reuse it across calls. Each SDK configures the API base URL and request credentials.
from roxy_sdk import create_roxy
roxy = create_roxy("your-api-key")
# Daily horoscope
horoscope = roxy.astrology.get_daily_horoscope(sign="aries")
# Numerology life path
lp = roxy.numerology.calculate_life_path(year=1990, month=1, day=15)
# Tarot Celtic Cross spread
reading = roxy.tarot.cast_celtic_cross(question="What should I focus on this month?")
php
<?php
use function RoxyAPI\Sdk\createRoxy;
$roxy = createRoxy(getenv('ROXY_API_KEY'));
// Daily horoscope
$horoscope = $roxy->astrology->getDailyHoroscope(sign: 'aries');
// Numerology life path
$lp = $roxy->numerology->calculateLifePath(year: 1990, month: 1, day: 15);
// Tarot Celtic Cross spread
$reading = $roxy->tarot->castCelticCross(question: 'What should I focus on this month?');
csharp
using RoxyApi;
var roxy = new RoxyClient(Environment.GetEnvironmentVariable("ROXY_API_KEY")!);
// Daily horoscope
var horoscope = await roxy.Astrology.Horoscope["aries"].Daily.GetAsync();
// Numerology life path
var lp = await roxy.Numerology.LifePath.PostAsync(new() { Year = 1990, Month = 1, Day = 15 });
// Tarot Celtic Cross spread
var reading = await roxy.Tarot.Spreads.CelticCross.PostAsync(new() { Question = "What should I focus on this month?" });
Go exposes the same domains as PascalCase services on the value returned by NewRoxy (roxy.Astrology, roxy.VedicAstrology). Each method mirrors the operation id with context first and the optional query-params struct as a nilable pointer: roxy.Astrology.GetDailyHoroscope(ctx, "aries", nil). Read the parsed body from resp.JSON200; any 4xx/5xx comes back as a *roxyapi.RoxyError (switch on .Code). Build date fields with roxyapi.Date(...) and optional fields with roxyapi.Ptr(...).
rust
use roxyapi::{Roxy, types};
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let roxy = Roxy::new(std::env::var("ROXY_API_KEY")?)?;
let places = roxy
.location()
.search_cities(&types::SearchCitiesParams::new(
"London, United Kingdom".into(),
))
.await?;
let city = places.cities.first().ok_or("No matching city")?;
let birth = types::NatalChartRequest::new(
"1990-01-15".into(),
"14:30:00".into(),
city.latitude,
city.longitude,
types::NatalChartRequestTimezone::String(city.timezone.clone()),
);
let chart = roxy
.astrology()
.generate_natal_chart(&Default::default(), &birth)
.await?;
println!("Ascendant: {}", chart.ascendant.sign);
Ok(())
}
Never expose your secret API key in client-side code. Call RoxyAPI from your server, API routes, serverless functions, or background workers only.
Authentication
Pass your API key on initialization. Get your API key, delivered instantly after checkout.
typescript
import { createRoxy } from '@roxyapi/sdk';
const roxy = createRoxy(process.env.ROXY_API_KEY!);
python
import os
from roxy_sdk import create_roxy
roxy = create_roxy(os.environ["ROXY_API_KEY"])
php
<?php
use function RoxyAPI\Sdk\createRoxy;
$roxy = createRoxy(getenv('ROXY_API_KEY'));
csharp
using Microsoft.Kiota.Abstractions; // Date and Time
using RoxyApi;
var roxy = new RoxyClient(Environment.GetEnvironmentVariable("ROXY_API_KEY")!);
C# exposes the same domains as PascalCase request-builder properties on the client: roxy.Astrology, roxy.VedicAstrology, roxy.AngelNumbers, and so on. Path parameters are indexers (roxy.Astrology.Horoscope["aries"].Daily), POST bodies use a target-typed new() { ... }, and calls throw a typed RoxyError on failure. A status the endpoint does not declare, such as a gateway error, throws the base ApiException from Microsoft.Kiota.Abstractions instead, so catch that too.
rust
use roxyapi::Roxy;
let roxy = Roxy::new(std::env::var("ROXY_API_KEY")?)?;
Rust uses snake_case namespace methods and operation names, such as roxy.vedic_astrology().generate_birth_chart(...). Request and response types live in roxyapi::types. Pass a borrowed parameter struct, then a typed body for POST operations. The Rust API docs and bundled AGENTS.md list every signature.
Domains
Each domain is a namespace on the roxy instance.
Namespace (TS / PHP)
Namespace (Python)
What it covers
roxy.astrology / $roxy->astrology
roxy.astrology
Western astrology: natal charts, horoscopes, synastry, transits, moon phases
roxy.vedicAstrology / $roxy->vedicAstrology
roxy.vedic_astrology
Vedic / Jyotish: kundli, panchang, dashas, nakshatras, doshas, KP system
roxy.numerology / $roxy->numerology
roxy.numerology
Life path, expression, soul urge, personal year, karmic lessons
roxy.tarot / $roxy->tarot
roxy.tarot
Daily card, custom draws, three-card, Celtic Cross, yes / no, love spread
Crystal meanings, zodiac / chakra pairings, birthstone, search
roxy.dreams / $roxy->dreams
roxy.dreams
Dream symbol dictionary, interpretations
roxy.angelNumbers / $roxy->angelNumbers
roxy.angel_numbers
Number meanings, universal digit-root lookup, daily
roxy.location / $roxy->location
roxy.location
City search for birth chart coordinates
roxy.usage / $roxy->usage
roxy.usage
API usage stats, remaining quota, subscription info
Location first
Every chart, horoscope, panchang, dasha, dosha, navamsa, KP, synastry, compatibility, and natal endpoint needs latitude, longitude, and a timezone. Call the location API first, then feed the result into the chart method.
The location response is a pagination envelope. Each city has both an IANA timezone string and a decimal utcOffset. Pass the IANA string as timezone: chart endpoints resolve it to the offset in force on the birth date. utcOffset is the offset in effect today, so it is wrong for any birth on the other side of a daylight saving change.
result = roxy.location.search_cities(q="London, UK")
# Response: { "total", "limit", "offset", "cities": [...] }
city = result["cities"][0]
chart = roxy.vedic_astrology.generate_birth_chart(
date="1990-01-15", time="14:30:00",
latitude=city["latitude"], longitude=city["longitude"],
timezone=city["timezone"], # IANA string; server resolves DST for the chart date
)
php
$result = $roxy->location->searchCities(q: 'London, UK');
// Response carries total, limit, offset, cities
$city = $result['cities'][0];
$chart = $roxy->vedicAstrology->generateBirthChart(
date: '1990-01-15',
time: '14:30:00',
latitude: $city['latitude'],
longitude: $city['longitude'],
timezone: $city['timezone'], // IANA string; server resolves DST for the chart date
);
csharp
var result = await roxy.Location.Search.GetAsync(c => c.QueryParameters.Q = "London, UK");
// result.Cities holds the matches; the envelope also carries pagination fields
var city = result!.Cities![0];
var chart = await roxy.VedicAstrology.BirthChart.PostAsync(new()
{
Date = new Date(1990, 1, 15),
Time = new Time(14, 30, 0),
Latitude = city.Latitude,
Longitude = city.Longitude,
Timezone = new() { String = city.Timezone }, // IANA string; server resolves DST for the chart date
});
rust
let places = roxy.location().search_cities(&types::SearchCitiesParams::new(
"London, United Kingdom".into(),
)).await?;
let city = places.cities.first().ok_or("No matching city")?;
let birth = types::NatalChartRequest::new(
"1990-01-15".into(), "14:30:00".into(), city.latitude, city.longitude,
types::NatalChartRequestTimezone::String(city.timezone.clone()),
);
let chart = roxy.astrology().generate_natal_chart(&Default::default(), &birth).await?;
println!("Ascendant: {}", chart.ascendant.sign);
Chart endpoints accept timezone as either a decimal number (-5 for EST, 1 for CET, 9 for Tokyo) or an IANA identifier ("America/New_York", "Europe/London"). IANA strings are resolved server-side to the DST-correct offset for the request date, so a January 1990 New York birth gets EST (-5) even when you geocoded the city in July. The utcOffset field on the Location response is a convenience decimal if you prefer numbers. Both forms produce identical charts.
Examples
Daily horoscope
typescript
const { data, error } = await roxy.astrology.getDailyHoroscope({
path: { sign: 'scorpio' },
});
if (error) {
console.error(error);
} else {
console.log(data.overview); // General daily forecast
console.log(data.love); // Love and relationship forecast
console.log(data.career); // Career outlook
}
python
horoscope = roxy.astrology.get_daily_horoscope(sign="scorpio")
print(horoscope["overview"]) # General daily forecast
print(horoscope["love"]) # Love and relationship forecast
print(horoscope["career"]) # Career outlook
php
$horoscope = $roxy->astrology->getDailyHoroscope(sign: 'scorpio');
echo $horoscope['overview']; // General daily forecast
echo $horoscope['love']; // Love and relationship forecast
echo $horoscope['career']; // Career outlook
csharp
var horoscope = await roxy.Astrology.Horoscope["scorpio"].Daily.GetAsync();
Console.WriteLine(horoscope!.Overview); // General daily forecast
Console.WriteLine(horoscope.Love); // Love and relationship forecast
Console.WriteLine(horoscope.Career); // Career outlook
rust
let horoscope = roxy.astrology().get_daily_horoscope(
&types::GetDailyHoroscopeParams::new(types::GetDailyHoroscopeParamsSign::Scorpio),
).await?;
println!("{}\n{}\n{}", horoscope.overview, horoscope.love, horoscope.career);
var result = await roxy.Numerology.LifePath.PostAsync(new() { Year = 1990, Month = 1, Day = 15 });
Console.WriteLine(result!.Number); // e.g. 7
Console.WriteLine(result.Type); // "single" | "master"
rust
let result = roxy.numerology().calculate_life_path(
&Default::default(),
&types::CalculateLifePathRequest::new(1990, 1, 15),
).await?;
println!("{} ({})", result.number, result.r#type);
Vedic birth chart
The response groups planets by the rashi (sign) they occupy. Each rashi has a signs[] array (planets in that rashi, often empty) plus a top-level meta dict with every planet keyed by name.
typescript
const { data, error } = await roxy.vedicAstrology.generateBirthChart({
body: {
date: '1990-01-15',
time: '14:30:00',
latitude: 40.7128,
longitude: -74.006,
timezone: -5,
},
});
if (error) throw error;
// Iterate rashis (aries..pisces) for the 12-sign chart
for (const rashi of ['aries', 'taurus', 'gemini'] as const) {
console.log(rashi, data[rashi].signs); // planets in this rashi
}
// Or look up any planet directly
console.log(data.meta.Sun.rashi); // "Capricorn"
console.log(data.meta.Moon.nakshatra.name); // nakshatra name
python
chart = roxy.vedic_astrology.generate_birth_chart(
date="1990-01-15", time="14:30:00",
latitude=40.7128, longitude=-74.006, timezone="America/New_York",
)
# Iterate rashis for the 12-sign chart
for rashi in ["aries", "taurus", "gemini"]:
print(rashi, chart[rashi]["signs"])
# Or look up any planet directly
print(chart["meta"]["Sun"]["rashi"]) # "Capricorn"
print(chart["meta"]["Moon"]["nakshatra"]["name"]) # nakshatra name
php
$chart = $roxy->vedicAstrology->generateBirthChart(
date: '1990-01-15',
time: '14:30:00',
latitude: 40.7128,
longitude: -74.006,
timezone: -5,
);
// Iterate rashis for the 12-sign chart
foreach (['aries', 'taurus', 'gemini'] as $rashi) {
echo $rashi, count($chart[$rashi]['signs']); // planets in this rashi
}
// Or look up any planet directly
echo $chart['meta']['Sun']['rashi']; // "Capricorn"
echo $chart['meta']['Moon']['nakshatra']['name']; // nakshatra name
csharp
var chart = await roxy.VedicAstrology.BirthChart.PostAsync(new()
{
Date = new Date(1990, 1, 15),
Time = new Time(14, 30, 0),
Latitude = 40.7128,
Longitude = -74.006,
Timezone = new() { Double = -5 },
});
// Planets, rashis, and meta are on the typed response - explore via IntelliSense
rust
let birth = types::BirthChartRequest::new(
"1990-01-15".into(), "14:30:00".into(), 40.7128, -74.006,
).with_timezone(types::BirthChartRequestTimezone::String("America/New_York".into()));
let chart = roxy.vedic_astrology().generate_birth_chart(&Default::default(), &birth).await?;
let moon = chart.meta.additional_properties.get("Moon").ok_or("Missing Moon")?;
println!("{}", moon.nakshatra.name);
const { data, error } = await roxy.angelNumbers.getAngelNumber({
path: { number: '1111' },
});
if (error) throw error;
console.log(data.coreMessage); // Short divine message summary
console.log(data.meaning.spiritual); // Spiritual interpretation
console.log(data.meaning.love); // Love / relationship meaning
python
result = roxy.angel_numbers.get_angel_number(number="1111")
print(result["coreMessage"]) # Short divine message summary
print(result["meaning"]["spiritual"]) # Spiritual interpretation
print(result["meaning"]["love"]) # Love / relationship meaning
php
$result = $roxy->angelNumbers->getAngelNumber(number: '1111');
echo $result['coreMessage']; // Short divine message summary
echo $result['meaning']['spiritual']; // Spiritual interpretation
echo $result['meaning']['love']; // Love / relationship meaning
csharp
var angel = await roxy.AngelNumbers.Numbers["1111"].GetAsync();
Console.WriteLine(angel!.CoreMessage); // Short divine message summary
Console.WriteLine(angel.Meaning!.Spiritual); // Spiritual interpretation
Console.WriteLine(angel.Meaning.Love); // Love / relationship meaning
rust
let result = roxy.angel_numbers().get_angel_number(
&types::GetAngelNumberParams::new("1111".into()),
).await?;
println!("{}", result.core_message);
println!("{}\n{}", result.meaning.spiritual, result.meaning.love);
Interpretations are available in ten languages: English (en), Turkish (tr), German (de), Spanish (es), French (fr), Hindi (hi), Portuguese (pt), Russian (ru), Chinese Simplified (zh-Hans), Chinese Traditional (zh-Hant).
var card = await roxy.Tarot.Daily.PostAsync(
new() { Date = new Date(2026, 4, 22) },
c => c.QueryParameters.Lang = RoxyApi.Tarot.Daily.PostLangQueryParameterType.Es);
rust
let card = roxy.tarot().get_daily_card(
&types::GetDailyCardParams::new().with_lang(types::GetDailyCardParamsLang::Es),
&types::GetDailyCardRequest::new().with_date("2026-04-22".into()),
).await?;
println!("{}", card.daily_message);
Python ships an _async variant for every sync method, for asyncio, FastAPI, or any event loop.
php
// Each call is synchronous. For concurrent fan-out, use the Saloon pool API
// on the underlying connector (Saloon ships a battle-tested request pool).
$horoscope = $roxy->astrology->getDailyHoroscope(sign: 'aries');
$card = $roxy->tarot->getDailyCard();
// Already async: every method returns a Task
var horoscope = await roxy.Astrology.Horoscope["aries"].Daily.GetAsync();
rust
let card = roxy.tarot().get_daily_card(&Default::default(), &Default::default()).await?;
println!("{}", card.daily_message);
Rust is async on Tokio. Clone Roxy to share its connection pool across application state or workers, then await calls in your existing async handlers. See the Rust SDK for Axum and Actix Web usage.
Error handling
TypeScript returns { data, error, response } per call. Python raises RoxyAPIError. PHP throws RoxyApiException. C# throws RoxyError, or the base ApiException for a status the endpoint does not declare. Go returns (resp, error) where any 4xx/5xx is a *roxyapi.RoxyError. Rust returns Result<T, roxyapi::Error>; an HTTP failure is Error::Api with status, code, error and optional doc_url. Match the stable code when choosing what to do next: every SDK ships the codes as typed constants, so a typo fails to compile or lint instead of silently never matching.
All six SDKs already retry for you, so a catch block never needs a sleep and retry loop. By the time an error reaches your code the retries have run out, or the wait was one a retry should not sit through, such as an exhausted monthly allowance. Each error carries doc_url and the Retry-After wait, under the name each tab shows.
ErrorCodes holds every code as a typed constant, and retryAfterSeconds(response) returns the Retry-After wait in seconds, or undefined. Pass { maxRetries: 0 } as the second argument of createRoxy to turn retries off, or maxRetries, initialDelayMs and maxDelayMs to tune them.
python
from roxy_sdk import create_roxy, ErrorCode, RoxyAPIError
roxy = create_roxy("your-api-key")
try:
result = roxy.tarot.cast_yes_no(question="Should I take this opportunity?")
print(result["answer"]) # "Yes" | "No" | "Maybe"
except RoxyAPIError as e:
if e.code == ErrorCode.VALIDATION_ERROR:
print(f"Bad input: {e.error}")
elif e.code == ErrorCode.RATE_LIMIT_EXCEEDED:
print(f"Monthly quota reached, resets in {e.retry_after} seconds")
else:
print(f"API error: {e.code} {e.error} {e.doc_url}")
ErrorCode is a string enum of every code, and e.retry_after is the Retry-After wait in seconds, or None. Pass max_retries=0 to create_roxy to turn retries off, or max_retries, initial_delay and max_delay to tune them.
php
use RoxyAPI\Sdk\ErrorCode;
use RoxyAPI\Sdk\RoxyApiException;
try {
$result = $roxy->tarot->castYesNo(question: 'Should I take this opportunity?');
echo $result['answer']; // "Yes" | "No" | "Maybe"
} catch (RoxyApiException $e) {
match ($e->code()) {
ErrorCode::ValidationError => fwrite(STDERR, "Bad input: {$e->error}\n"),
ErrorCode::RateLimitExceeded => fwrite(STDERR, "Monthly quota reached, resets in {$e->retryAfter} seconds\n"),
default => fwrite(STDERR, "API error: {$e->errorCode} {$e->error} {$e->docUrl}\n"),
};
}
$e->code() returns the ErrorCode case, or null for a code newer than your installed SDK, and $e->retryAfter is the Retry-After wait in whole seconds, or null. Pass new RetryPolicy(maxRetries: 0) (from RoxyAPI\Sdk\Retry) as the second argument of createRoxy to turn retries off, or set maxRetries, initialDelayMs and maxDelayMs to tune them.
csharp
using Microsoft.Kiota.Abstractions;
using RoxyApi;
using RoxyApi.Models;
try
{
var answer = await roxy.Tarot.YesNo.PostAsync(new() { Question = "Should I take this opportunity?" });
Console.WriteLine(answer!.Answer); // "Yes" | "No" | "Maybe"
}
catch (RoxyError e)
{
switch (e.Code)
{
case ErrorCodes.ValidationError: Console.Error.WriteLine($"Bad input: {e.Message}"); break;
case ErrorCodes.RateLimitExceeded: Console.Error.WriteLine($"Monthly quota reached, resets in {e.RetryAfter}"); break;
default: Console.Error.WriteLine($"API error: {e.Code} {e.Message} {e.DocUrl}"); break;
}
}
catch (ApiException e)
{
Console.Error.WriteLine($"HTTP {e.ResponseStatusCode}"); // a status the endpoint does not declare
}
ErrorCodes holds a constant for every code, and e.RetryAfter is a nullable TimeSpan. Pass a RoxyRetryOptions to the RoxyClient constructor to tune retries, or set MaxRetries = 0 to turn them off.
go
resp, err := roxy.Tarot.CastYesNo(ctx, nil, roxyapi.CastYesNoJSONRequestBody{
Question: roxyapi.Ptr("Should I take this opportunity?"),
})
var rerr *roxyapi.RoxyError
if errors.As(err, &rerr) {
switch rerr.Code {
case roxyapi.ErrCodeValidationError:
fmt.Println("Bad input:", rerr.Message)
case roxyapi.ErrCodeRateLimitExceeded:
fmt.Println("Monthly quota reached, resets in", rerr.RetryAfter)
default:
fmt.Println("API error:", rerr.Code, rerr.Message, rerr.DocURL)
}
return
}
fmt.Println(resp.JSON200.Answer) // "Yes" | "No" | "Maybe"
The roxyapi.ErrCode... constants cover every code, and rerr.RetryAfter is a time.Duration, zero when the response sent no Retry-After. Pass roxyapi.WithMaxRetries(0) to NewRoxy to turn retries off, or roxyapi.WithRetry(roxyapi.RetryConfig{...}) to change the delays.
rust
use roxyapi::{Error, ErrorCode};
let result = roxy.tarot().cast_yes_no(
&Default::default(),
&types::CastYesNoRequest::new().with_question("Should I take this opportunity?".into()),
).await;
match result {
Ok(reading) => println!("{}", reading.answer),
Err(Error::Api(error)) => match error.error_code() {
ErrorCode::ValidationError => eprintln!("Bad input: {}", error.details["issues"]),
ErrorCode::RateLimitExceeded => eprintln!("Monthly quota reached, resets in {:?}", error.retry_after),
_ => eprintln!("API error: {} {} {:?}", error.code, error.error, error.doc_url),
},
Err(error) => return Err(error.into()),
}
ErrorCode is an enum of every code, with Other(String) for a code newer than your installed SDK. error.retry_after is an Option<Duration>, error.doc_url an Option<String>, and error.details holds every other body field, such as issues. Pass Roxy::builder(key).retry_policy(RetryPolicy::new(0)).build()? to turn retries off, or RetryPolicy::new(n).with_initial_delay(..).with_max_delay(..) to tune them.
Retries
TypeScript, Python, PHP, C#, Go and Rust retry twice by default, with jittered backoff, on 429, 500, 502, 503 and 504 and on a connection that never reached the API, and they honour Retry-After. A wait longer than 30 seconds, which is what an exhausted monthly allowance asks for, is not waited out: the error comes back at once with the wait attached. Setting the retry count to 0 turns retries off.
Retry 429 with code rate_limit_per_minute and 503 with code compute_saturated after the Retry-After seconds. Retry 500 with code internal_error with exponential backoff. A request that fails on our side with a 5xx does not count against your monthly allowance, so retrying it is free. A 429 with code rate_limit_exceeded means the monthly allowance is used up: retrying cannot help before the reset, and Retry-After gives the seconds to it. Fix the request for any other 4xx, because it will fail the same way until it changes. Every official SDK already retries these for you, twice and honouring Retry-After, so a call made through an SDK needs no second retry loop.
Error codes
Every code, the status it arrives with, and what to change for each one is on the error codes page. It is one entry per code, and every failed response carries a doc_url that links directly to its own entry, so an SDK error can be looked up without searching.
The code field is machine-readable and stable, safe to switch on. The error field is human-readable and may change wording.
Type safety
SDK method names and request shapes come from the published OpenAPI contract. TypeScript ships generated .d.ts definitions for editor completion. Each language uses its own response and error pattern, as shown in the examples above. Inspect complete field shapes in the API reference.
typescript
// TypeScript knows sign must be one of the 12 zodiac signs
const { data, error } = await roxy.astrology.getDailyHoroscope({
path: { sign: 'aries' }, // autocompletes all 12 options
});
if (error) throw error;
// after the error check, data is typed, no manual casting needed
console.log(data.date); // string
console.log(data.overview); // string
You can also import individual namespace classes or response types:
typescript
import { Astrology, type GetAstrologyHoroscopeBySignDailyResponse } from '@roxyapi/sdk';
PHP uses named arguments for parameters and returns decoded JSON. Follow the PHP examples above for reading responses and handling errors.
Gotchas
Parameters are structured
TypeScript always uses { path: {...} }, { body: {...} }, or { query: {...} }. Python uses keyword arguments only (sign="aries"), never positional. PHP uses named arguments (sign: 'aries'), never positional. C# path params are indexers (roxy.Astrology.Horoscope["aries"]), query params go through a config lambda (c => c.QueryParameters.Q = "London, UK"), and POST bodies use a target-typed new() { ... }. Do not mix the patterns.
Timezone accepts both IANA and decimal
Chart endpoints accept timezone as either a decimal (-5, 1, 0) or an IANA identifier ("America/New_York", "Europe/London"). IANA is preferred because it is DST-resolved against the request's date, so historical birth charts pick the correct offset even when you geocoded the city today. Both city.timezone and city.utcOffset from the Location response work. In C# the timezone is a union wrapper, new() { Double = 5.5 } for the decimal offset or new() { String = "Europe/London" } for the IANA identifier.
Location returns an envelope, not a bare array
data.cities[0] in TypeScript and result["cities"][0] in Python. The top-level object carries total, limit, offset for pagination. Same envelope pattern applies to tarot.listCards (data.cards), iching.listHexagrams (data.hexagrams), dreams.searchDreamSymbols (data.symbols), and crystals.searchCrystals / getCrystalsByZodiac / getCrystalsByChakra (data.crystals).
Date format is YYYY-MM-DD, time is HH:MM:SS
These are the JSON request formats. 14:30 is also accepted, with seconds defaulting to 00. Missing required date or time fields and out-of-range values return 400. Use the request types shown in the examples for your SDK.
Do not guess method names
Type roxy.domain. and let autocomplete show methods. Names come from operationId in the OpenAPI spec, not URL paths. Examples: getDailyHoroscope (not dailyHoroscope), calculateLifePath (not lifePath), castCelticCross (not celticCross).
Chart endpoints need coordinates
Use the location API to geocode cities before any birth chart, synastry, panchang, KP, or natal method. Never hardcode a user's latitude, longitude, or timezone from memory.
Do not expose API keys client-side
Call RoxyAPI with a secret key from server code, API routes, serverless functions, or background workers only. Any secret key shipped to the browser is a leaked key; browser code takes a publishable pk_ key.
Do not use raw fetch or requests
The SDK handles auth headers, base URL, SDK identification, and typed responses. Raw HTTP clients re-invent this and miss the type safety.
Catch and switch on e.code (Python, against ErrorCode) or $e->code() (PHP, an ErrorCode case), not the human-readable message text. .NET throws RoxyError; switch on e.Code against ErrorCodes. Go compares rerr.Code with the roxyapi.ErrCode... constants. The codes are stable between releases; the message text may change.
TypeScript data and error are mutually exclusive
If error is set, data is undefined. Always check error first.
Render the response in the browser
For projects that need to display the response visually (charts, tables, cards, forms) without writing your own SVG geometry or table layouts, install RoxyAPI UI alongside the SDK.
Components are stateless. Caller fetches via the SDK, passes the response as the data prop. Theming via CSS custom properties on :root or per element. Works in React, Next.js, Vue, Svelte, Angular, vanilla HTML, and WordPress.
add_action('wp_enqueue_scripts', function () {
wp_enqueue_script(
'roxy-ui',
'https://cdn.jsdelivr.net/npm/@roxyapi/ui@latest/dist/cdn/roxy-ui.js',
[], null, true
);
});
// In a template or shortcode handler:
echo '<roxy-natal-chart data="' . esc_attr(wp_json_encode($response)) . '"></roxy-natal-chart>';
Full setup walkthrough at /docs/ui, catalog and live customizer at /ui. Source on GitHub at RoxyAPI/ui, MIT licensed, published with SLSA provenance via OIDC.
AI coding agents
The TypeScript, Python, PHP, C#, Go, and Rust SDKs bundle a consumer AGENTS.md. Point your coding agent at the installed guide explicitly: node_modules/@roxyapi/sdk/AGENTS.md, the installed Python package, vendor/roxyapi/sdk/AGENTS.md, the restored NuGet package (~/.nuget/packages/roxyapi.sdk/<version>/AGENTS.md), the Go module cache, or the roxyapi-<version>/AGENTS.md file in the Cargo registry source cache after cargo fetch.
Quickstart, critical patterns, common tasks reference, gotchas
Field format table covering timezone decimals, date / time strings, nested person objects, enum values
MCP tool name mapping for every REST endpoint
Before the package is installed, an agent reads the same guide as raw markdown. Fetch it whole with curl -s, not a summarizing web tool:
For agents in projects WITHOUT any SDK installed (Ruby, raw curl, Bash), there is also a site-level AGENTS.md playbook at https://roxyapi.com/AGENTS.md covering the same Rule 0, common tasks, body shapes, error contract, and field gotchas, but language-agnostic.
Also available: a remote MCP server per domain at https://roxyapi.com/mcp/{domain} (Streamable HTTP, no stdio / no self-hosting) for agents that speak the Model Context Protocol.
Links
API Reference: browse all endpoints and try live calls
Hi, I am Rosie, your guide to the RoxyAPI insight tools.
Ask me to read your birth chart, pull a card, or map your numbers, and I will interpret what it means for you. Unlike most AIs, I never make the numbers up: every reading runs on real, verified calculations. Curious about the API, pricing, or docs instead? Ask me that too.