Birth Time and Date Input Formats for Astrology APIs
How an astrology API should parse birth time and date input, from HH:MM to a trailing Z, and why 25:00:00 still has to fail with a 400.
TL;DR
- A birth chart API should accept
HH:MMandHH:MM:SS, single digit hours, a trailingZ, and fractional seconds, then normalize every one to canonicalHH:MM:SS. - Live test on one birth:
14:30,14:30:00Z, and14:30:00.5all return the same Scorpio ascendant at 1.97 degrees.25:00:00and2:30 PMreturn a 400. - Birth time is a local wall clock value. Keep it separate from the timezone field so the location and date can resolve the correct offset.
- Wire this up on the Astrology API in minutes.
Birth time is the single most fragile field in any natal chart form. A browser date picker returns a full ISO date, but the native time input returns 14:30 with no seconds. A hand rolled form might send 9:15 with a single digit hour. A datetime library might append a Z. Each of these is a correct representation of a birth moment, yet a strict astrology API rejects most of them with a 400 on the very first call. The user never sees a chart, and the integration is abandoned before it starts. This guide shows which birth time and date formats an astrology API should accept, why strict ISO validation rejects valid input, and how to send birth data so the first call succeeds.
Which birth time formats should an astrology API accept?
An astrology API should accept the birth time formats that real forms actually produce, then normalize all of them to one canonical value before it calculates anything. The formats worth accepting are HH:MM:SS, HH:MM with no seconds, a single digit hour like 9:15, a trailing Z, and fractional seconds. Every one of these describes a valid local time, so rejecting them punishes the developer for a cosmetic difference. The table below is real output from one birth, 1990-07-15 in London, varying only the time string.
| Input time | HTTP | Stored time | Ascendant |
|---|---|---|---|
14:30:00 | 200 | 14:30:00 | Scorpio 1.97 |
14:30 | 200 | 14:30:00 | Scorpio 1.97 |
14:30:00Z | 200 | 14:30:00 | Scorpio 1.97 |
14:30:00.5 | 200 | 14:30:00 | Scorpio 1.97 |
9:15 | 200 | 09:15:00 | Virgo 6.47 |
25:00:00 | 400 | validation_error | none |
2:30 PM | 400 | validation_error | none |
The Scorpio ascendant returned identically for four different birth time inputs, because all four normalize to 14:30:00. See the natal chart endpoint.
Ready to build this? The Astrology API normalizes birth time input for you and returns a full natal chart in one call. See pricing.
Why does 14:30 get rejected by strict ISO validation?
Strict ISO 8601 and RFC 3339 define a full time as a value that carries a timezone offset, so a bare 14:30:00 with no offset is not a valid RFC 3339 time, and 14:30 fails twice because it also omits seconds. A validator that enforces the specification to the letter rejects both, even though a birth certificate records exactly that: a local clock reading with no offset attached. This is the most common reason an astrology API returns a 400 on input the developer knows is correct.
A strict RFC 3339 time validator requires a zone, so it rejects 14:30:00 and 14:30 outright. The fix is to parse leniently first: pad a missing seconds field to :00, pad a single digit hour, strip a trailing Z or fractional seconds, then validate the real hour, minute, and second ranges. Normalization comes before validation, never after.
A forgiving parser runs a fixed pipeline before it validates:
- Trim whitespace and strip a single trailing
Zor offset marker. - Split on the colon and pad a single digit hour to two digits.
- Append
:00when the seconds field is missing. - Drop any fractional part after the whole second.
- Validate the final hour, minute, and second against 24 hour ranges, and reject anything out of range.
The order matters. Lenient parsing that skips validation would happily accept 25:00:00, which is why the normalize step still rejects impossible clock values.
Why does 25:00:00 still return a 400?
Forgiving input parsing is not the same as accepting anything, so an astrology API should still reject a time that cannot exist on a clock. In the live matrix above, 25:00:00, 14:30:99, and 2:30 PM all return a 400 with a validation_error code, because an hour of 25, a second of 99, and a twelve hour clock string with PM are not valid 24 hour times. On the date side the same rule holds: 1990-2-30 is rejected because February never has thirty days, while 1990-7-15 is accepted and normalized to 1990-07-15.
| Input date | HTTP | Stored date |
|---|---|---|
1990-07-15 | 200 | 1990-07-15 |
1990-7-15 | 200 | 1990-07-15 |
1990-02-30 | 400 | validation_error |
The distinction is precision that helps versus precision that hurts. Padding a single digit and dropping a redundant Z removes friction with no loss of meaning. Silently accepting an impossible date would produce a wrong chart the user could not detect, which is a far worse failure than a clear 400.
Why is birth time separate from the timezone field?
Birth time is a local wall clock reading, so a chart API takes it as a plain time and takes the timezone as a separate field, because the correct offset depends on the place and the date together. Daylight saving rules and historical zone changes mean the offset for a city is not fixed. London in July runs at plus one, not zero, and many regions shifted their zones across the twentieth century. If you fold an offset directly into the time string, you throw away the API ability to resolve the historically correct offset from the location and the birth date.
This is why the natal chart request takes date, time, latitude, longitude, and timezone as distinct fields. Pass the local clock time as the user wrote it, pass the place, and let the resolver handle the offset. For the deeper mechanics of offsets and daylight saving, see the timezone handling guide and the DST and historical timezone breakdown.
How to send birth time and date to the natal chart API
Send birth data in two steps: geocode the birth city to coordinates and a timezone, then post the chart with the local date and time. The first call resolves the place so you never ask a user to type latitude and longitude, and the second call accepts the birth time in either 14:30 or 14:30:00 form. Both examples below are verified against the live schema.
# 1. Geocode the birth city to latitude, longitude, and timezone
curl -s "https://roxyapi.com/api/v2/location/search?q=London" \
-H "X-API-Key: YOUR_KEY"
# 2. Post the natal chart. Birth time can be 14:30 or 14:30:00.
curl -s -X POST "https://roxyapi.com/api/v2/astrology/natal-chart" \
-H "X-API-Key: YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{"date":"1990-07-15","time":"14:30","latitude":51.50853,"longitude":-0.12574,"timezone":"Europe/London"}'
const key = process.env.ROXY_API_KEY;
const geo = await fetch(
"https://roxyapi.com/api/v2/location/search?q=London",
{ headers: { "X-API-Key": key } },
).then((r) => r.json());
const place = geo.cities[0];
const chart = await fetch(
"https://roxyapi.com/api/v2/astrology/natal-chart",
{
method: "POST",
headers: { "X-API-Key": key, "Content-Type": "application/json" },
body: JSON.stringify({
date: "1990-07-15",
time: "14:30",
latitude: place.latitude,
longitude: place.longitude,
timezone: place.timezone,
}),
},
).then((r) => r.json());
console.log(chart.ascendant.sign, chart.ascendant.degree);
import os, requests
key = os.environ["ROXY_API_KEY"]
h = {"X-API-Key": key}
geo = requests.get(
"https://roxyapi.com/api/v2/location/search",
params={"q": "London"}, headers=h,
).json()
place = geo["cities"][0]
chart = requests.post(
"https://roxyapi.com/api/v2/astrology/natal-chart",
headers={**h, "Content-Type": "application/json"},
json={
"date": "1990-07-15",
"time": "14:30",
"latitude": place["latitude"],
"longitude": place["longitude"],
"timezone": place["timezone"],
},
).json()
print(chart["ascendant"]["sign"], chart["ascendant"]["degree"])
The response echoes the normalized 14:30:00 in birthDetails and returns the full chart, so you can store the canonical time your database expects. The natal chart is one of 160 plus endpoints across 12 insight domains, including Western astrology, Vedic astrology, numerology, and tarot, all under one key.
FAQ
What time format does the RoxyAPI astrology API accept for birth time?
RoxyAPI accepts birth time as HH:MM:SS or HH:MM, with single digit hours, a trailing Z, and fractional seconds all tolerated. Every accepted value is normalized to canonical HH:MM:SS before the chart is calculated, and the normalized value is echoed back in the response so you can store it.
Why does my astrology API call return a 400 on the time field?
A 400 with a validation_error code means the time is not a valid 24 hour clock value. Common causes are an impossible time like 25:00:00, a twelve hour string like 2:30 PM, or an out of range second like 14:30:99. RoxyAPI accepts 14:30 and 14:30:00, so a missing seconds field is not the cause.
Do I need to include seconds in the birth time?
No. RoxyAPI accepts 14:30 and pads it to 14:30:00 automatically, which matters because the native browser time input returns HH:MM with no seconds. You can send either form and receive the same chart, since both normalize to the same canonical time.
Can I send birth time straight from an HTML time input?
Yes. The native HTML time input returns HH:MM with no seconds, and RoxyAPI accepts that value directly and pads it to HH:MM:SS. You can bind the input value to the time field with no reformatting, which removes one of the most common causes of a first call 400.
Should birth time include a timezone or UTC offset?
No. Send the local wall clock time as recorded on the birth certificate and pass the timezone as a separate field. RoxyAPI resolves the correct offset from the location and the birth date, which is how it handles daylight saving and historical zone changes that a fixed offset in the time string would break.
Does RoxyAPI reject invalid birth times and dates?
Yes. RoxyAPI normalizes forgiving formats but still rejects values that cannot exist, so 25:00:00 and 1990-02-30 both return a 400. This prevents a silently wrong chart, which is a worse failure than a clear validation error the developer can fix immediately.
Conclusion
Forgiving input parsing is the difference between a first call that returns a chart and a first call that returns a 400. Accept the birth time formats real forms produce, normalize them to one canonical value, and keep the local time separate from the timezone so the offset resolves correctly. Build it on the Astrology API and check the pricing to start.