Guides
Credits, errors, and going to production
This guide covers what changes between prototyping and running in production. You'll set up billing awareness, error handling, caching, and monitoring so your integration holds up under real traffic.
How billing works
There are two ways to pay for API usage. A subscription plan gives you a monthly allocation against a unified credit wallet. Pay-as-you-go lets you buy credits and spend them per call without a recurring plan. You can mix both.
Every new account starts with 150 free credits, which is enough to build and test an integration before you commit to a plan. Credit costs vary by endpoint, so check the pricing page for current numbers.
Keep API keys server-side
The JSON API at json.astrologyapi.com does not send CORS headers, so a browser-direct call fails. A key placed in client-side code is a key any visitor can read from the page source and use as their own.
Store your user ID and API key in environment variables or a secret manager. Never put either value in the bundle you ship to the browser.
Route every call through your own server. Use a Next.js API route, a backend service, or a serverless function. The browser talks to your server, and your server talks to the API.
Handle errors like you mean it
Treat any non-200 response as a failure. Read the status code before you decide what to do next.
Retry with exponential backoff only on 5xx responses and network errors such as timeouts and connection resets. Do not retry on 4xx.
A bad request stays bad no matter how many times you resend it. A 401 with a wrong key fails identically forever. So does a 400 with a malformed date. Retrying just wastes time and credits.
Set a request timeout so a hung connection cannot block your app indefinitely. The wrapper below uses AbortController for the timeout and stops after four attempts.
// wrapper.js — Node 18+ (global fetch available)
const BASE_URL = 'https://json.astrologyapi.com/v1'
const MAX_ATTEMPTS = 4
const TIMEOUT_MS = 10000
async function callAstrologyApi(endpoint, body, userId, apiKey) {
const auth = Buffer.from(`${userId}:${apiKey}`).toString('base64')
for (let attempt = 1; attempt <= MAX_ATTEMPTS; attempt++) {
const controller = new AbortController()
const timer = setTimeout(() => controller.abort(), TIMEOUT_MS)
try {
const res = await fetch(`${BASE_URL}/${endpoint}`, {
method: 'POST',
headers: {
Authorization: `Basic ${auth}`,
'Content-Type': 'application/json',
},
body: JSON.stringify(body),
signal: controller.signal,
})
if (res.ok) {
return res.json()
}
// 4xx is a client error. Retrying sends the same bad request, so stop now.
if (res.status >= 400 && res.status < 500) {
const text = await res.text()
throw new Error(`Request failed (${res.status}): ${text}`)
}
// 5xx falls through to the retry logic below.
} catch (err) {
// A thrown 4xx error must not be retried.
if (err.message && err.message.startsWith('Request failed (4')) {
throw err
}
// AbortError and network errors reach here and are retried.
} finally {
clearTimeout(timer)
}
if (attempt === MAX_ATTEMPTS) {
throw new Error(`Gave up after ${MAX_ATTEMPTS} attempts`)
}
// Exponential backoff: 1s, 2s, 4s.
const delay = 1000 * 2 ** (attempt - 1)
await new Promise((resolve) => setTimeout(resolve, delay))
}
}
// Usage:
// const data = await callAstrologyApi(
// 'planets',
// { day: 10, month: 5, year: 1990, hour: 19, min: 55,
// lat: 19.2056, lon: 25.2056, tzone: 5.5 },
// process.env.ASTROLOGY_USER_ID,
// process.env.ASTROLOGY_API_KEY,
// )
Cache to cut credit spend
Many responses do not change between requests. Caching them means you pay for one call instead of one call per visitor.
Daily horoscope content, such as a sun-sign daily prediction, is generated once per sign per day.
Cache that prediction server-side keyed by (sign, date). Serve the cached copy to every user who asks for the same sign that day. Do not call the API once per visitor.
Natal chart data from endpoints like planets never changes for a fixed birth date, time, and location. Cache that response indefinitely, keyed on the birth-detail tuple (day, month, year, hour, min, lat, lon, tzone).
The same input always produces the same planetary positions, so the cached copy never goes stale.
Monitor what you're spending
Watch usage and cost from the dashboard so a traffic spike does not drain your wallet unnoticed. The usage analytics page shows call volume and patterns over time.
The cost analysis page shows where your credits go. The breakdown tells you which endpoints cost the most.
Pre-launch checklist
Run through this list before you point production traffic at the API.
- Keys and user IDs live in environment variables or a secret manager, never in client-side code.
- Retry logic runs on 5xx and network errors only, with exponential backoff.
- A request timeout is set on every call so a hung connection cannot block your app.
- Non-200 responses are handled and logged, not swallowed.
- Caching is in place for stable data such as daily horoscopes and natal charts.
- Wallet balance is monitored on cost analysis.
- Usage patterns are reviewed on usage analytics.
- Retry and backoff behavior is tested against a real key, not just mocked.
- Error responses reach your own users as a friendly message, not raw API JSON.
- A fallback or alert path fires if the wallet runs low.
Related guides
If you haven't nailed down input accuracy yet, start with timezones, DST, and historical birth data. A wrong offset breaks the natal-chart cache described above just as much as it breaks the chart. A cache keyed on the wrong tzone serves the wrong result forever.
If you're feeding API output into an LLM, see ground an LLM on computed charts for a caching pattern scoped to a single conversation.
For a first end-to-end build, see generate your first PDF report. Browse the full set on the guides index.