Errors & limits
Every failed request returns a structured error. The hosted surfaces use
exactly three envelope shapes — tool errors, MCP protocol errors, and
account-surface errors — and this page lists every code, when it fires,
whether retrying helps, and how to fix it. Tool calls run at
https://mcp.agentastrology.com — REST at POST /v1/tools/{tool}, MCP at
/mcp. Key management runs at https://api.agentastrology.com.
1. The three envelopes
Tool errors — REST bodies and MCP tool results share this shape:
{
"error": {
"code": "invalid_input",
"message": "Invalid input: expected object, received undefined at birthData"
}
}
code is stable and switchable; message is human-readable — include it in
bug reports. Some codes add fields (method, requiredTier, resetAt) —
shown per code below. On MCP, this object sits under
structuredContent.error of a result with isError: true; on REST it is
the response body and the HTTP status carries the class.
MCP protocol errors — JSON-RPC error objects
({"jsonrpc":"2.0","id":…,"error":{"code":-32602,"message":"…"}}) for
transport-level failures: malformed JSON, wrong headers, unknown tool name,
malformed call params. Covered in §5.
Account-surface errors — a flat shape
{"error":"unauthorized","message":"Sign in required"} (note: error is
the code string itself, not an object). Covered in §7.
2. Master table — every tool-surface code
| Code | HTTP (REST) | Fires when | Retryable? | What to do |
|---|---|---|---|---|
invalid_input | 400 | A field is missing, mistyped, or out of range; the method value isn't one of the tool's methods; the request body isn't a JSON object | No | Fix the payload — the message names the first bad field and its path |
invalid_birth_data | 400 | Compute rejects the birth data past schema validation (latitude/longitude/unknown-time constraint messages) | No | Fix the named birth-data field |
unknown_tool | 404 | The REST slug doesn't exist | No | Check the slug against GET /v1/tools |
missing_api_key | 401 | A REST tool call carries no credentials | No | Send Authorization: Bearer <key> or x-api-key: <key> |
invalid_api_key | 401 | The key is unknown or revoked | No | Check the key on your account page; create a new one if revoked |
tier_denied | 403 | The method is paid-tier and the key's tier doesn't include it | No | Call a free method, or upgrade the key when paid tiers launch |
rate_limited | 429 | The key's monthly call quota is exhausted | Yes — after resetAt | Wait for the window reset (§6); retrying earlier returns the same 429 |
method_not_yet_implemented | 501 | The method is announced but not shipped (none on the current surface) | No | Wait for the release; the message names the method |
sweph_failure | 500 | The ephemeris cannot cover the request — most commonly a chart date before ~675 AD, which is outside Chiron's range (§8) | No | Move the date into the supported range |
geocode_failure | 500 | A place name cannot be resolved to coordinates | Once | Retry once; if it persists, pass a more specific place or explicit coordinates |
internal_error | 500 | Anything the service couldn't classify, including dates several millennia out (§8) | Once | Retry once; if it persists, report the full message |
auth_unavailable | 503 | The auth backend is temporarily not accepting checks | Yes | Retry with backoff |
auth_upstream_error | 502 | The auth backend was unreachable | Yes | Retry with backoff; if persistent, check status page |
invalid_input and invalid_birth_data are almost always caller bugs — a
string timezone instead of a number, birthData nested into a flat tool like
astro_moment, or a method that belongs to a different tool. Fix the
payload, not the service.
3. Worked failure payloads
Missing required field
astro_chart called with {"method": "natal"} and no birthData:
{
"error": {
"code": "invalid_input",
"message": "Invalid input: expected object, received undefined at birthData"
}
}
The message is built from the first schema issue — its text plus the path
(birthData). The underlying issue the validator raised for this call, shown
so you know what the parts mean:
{
"expected": "object",
"code": "invalid_type",
"path": ["birthData"],
"message": "Invalid input: expected object, received undefined"
}
Value out of range
birthData.year: 9999 — years run −4000..4000:
{
"error": {
"code": "invalid_input",
"message": "Too big: expected number to be <=4000 at birthData.year"
}
}
Method that isn't on the tool
Two shapes depending on where it is caught. With a key (HTTP surfaces), the gate answers and names the tool:
{
"error": {
"code": "invalid_input",
"message": "astro_chart has no method 'nope'",
"method": "nope",
"requiredTier": null
}
}
Without the gate (local stdio), schema validation answers instead:
{
"error": {
"code": "invalid_input",
"message": "Invalid input at method"
}
}
Missing key
curl -sS -X POST https://mcp.agentastrology.com/v1/tools/astro_chart \
-H "Content-Type: application/json" \
-d '{"method":"natal"}'
{
"error": {
"code": "missing_api_key",
"message": "Provide Authorization: Bearer <key> or x-api-key"
}
}
Unknown or revoked key (HTTP 401)
{
"error": {
"code": "invalid_api_key",
"message": "Unknown or revoked API key"
}
}
Tier-gated method on a free key (HTTP 403)
{
"error": {
"code": "tier_denied",
"message": "astro_aspects.between-charts requires the paid tier",
"method": "between-charts",
"requiredTier": "paid"
}
}
Quota exhausted (HTTP 429)
{
"error": {
"code": "rate_limited",
"message": "Monthly quota exhausted",
"resetAt": "2026-09-01T00:00:00.000Z"
}
}
resetAt is ISO 8601 — the start of the next UTC calendar month. There is no
rate-limit response header; read the reset time from the body.
Not-yet-implemented method (HTTP 501)
No method on the current surface returns it — if you ever see it, the method is announced but not shipped:
{
"error": {
"code": "method_not_yet_implemented",
"message": "Method astro_returns.saturn is not yet implemented (Paid tier, coming soon).",
"method": "astro_returns.saturn"
}
}
Ephemeris limit — Chiron range (HTTP 500)
Natal chart for year 600:
{
"error": {
"code": "sweph_failure",
"message": "calc failed for planet 15: Chiron's ephemeris is restricted to JD 1967601.5 - JD 3419437.5"
}
}
Ephemeris limit — date too remote (HTTP 500)
For year −4000:
{
"error": {
"code": "internal_error",
"message": "calc failed for planet 0: SwissEph file 'seplm42.se1' not found in PATH <ephemeris dir>"
}
}
Non-object REST body (HTTP 400)
Body [] — returned before authentication, so you can see it without a key:
{
"error": {
"code": "invalid_input",
"message": "request body must be a JSON object"
}
}
4. MCP results and protocol errors
Tool-level failures — failure is a normal result object with
isError: true and the envelope under structuredContent.error. Check
isError before parsing anything else — it is the only reliable
success/failure signal. Prefer structuredContent over content[0].text
when your client surfaces it; the text block is the same JSON as a string.
Example:
{
"isError": true,
"structuredContent": {
"error": {
"code": "invalid_input",
"message": "Invalid input: expected object, received undefined at birthData"
}
},
"content": [
{
"type": "text",
"text": "{\"error\":{\"code\":\"invalid_input\",\"message\":\"Invalid input: expected object, received undefined at birthData\"}}"
}
]
}
Protocol errors — JSON-RPC error objects for transport-level failures:
| JSON-RPC code | HTTP | Fires when | What to do |
|---|---|---|---|
-32700 | 400 | The POST body isn't valid JSON | Fix the JSON |
-32000 | 406 | Missing Accept pair — the endpoint requires both application/json and text/event-stream | Send Accept: application/json, text/event-stream |
-32601 | 200 | Unknown JSON-RPC method | Use the MCP methods your client library sends |
-32602 | 200 | tools/call names a tool that doesn't exist | Check the name against tools/list |
-32603 | 200 | tools/call params are malformed (e.g. name missing) | Fix the call params; the message quotes the failed fields |
Tool not found (note: in-protocol errors ride HTTP 200 — always check the body, not just the status):
{
"jsonrpc": "2.0",
"id": 1,
"error": {
"code": -32602,
"message": "MCP error -32602: Tool astro_nope not found"
}
}
Malformed JSON body:
{
"jsonrpc": "2.0",
"error": { "code": -32700, "message": "Parse error: Invalid JSON" },
"id": null
}
Two facts to keep in mind. First, invalid tool ARGUMENTS are never protocol
errors — they come back as isError: true results with invalid_input so
your agent can read and fix them. Second, auth on /mcp is checked per HTTP
request before the protocol runs, so auth failures arrive as plain JSON HTTP
errors (§3 codes), not JSON-RPC errors.
5. Rate limits
- Free tier: 500 calls per month. Paid tier: 50,000 calls per month. Enforced
per key over the UTC calendar month; the window resets at 00:00 UTC on the
first of the next month (that instant is the 429 body's
resetAt). - All keys are currently issued at paid tier with full access during the beta, so 50,000 is the number to plan against today.
- Every authenticated call draws one unit: each REST tool call, and each HTTP
POST to
/mcp— includinginitializeandtools/list. GET /v1/tools,GET /v1/openapi.json, and the health endpoint are unauthenticated and never count.- Retrying while limited returns the same 429 and does not push the reset later.
- There is no rate-limit header on responses; the reset time lives only in
the 429 body (
error.resetAt, ISO 8601).
6. Account-surface errors (api.agentastrology.com)
Key management (/keys, /usage) is session-authenticated — sign in on the
website; the dashboard drives these. Its envelope is flat — error IS the
code string:
{
"error": "unauthorized",
"message": "Sign in required"
}
| Code | HTTP | Fires when | What to do |
|---|---|---|---|
unauthorized | 401 | No valid session | Sign in at the website and retry from the account page |
invalid_input | 400 | Body isn't valid JSON, or name isn't a string of 1..64 chars | Fix the request body |
not_found | 404 | Deleting a key that doesn't exist (or isn't yours) | List keys first; it may already be revoked |
endpoint_moved | 410 | Calling the retired /rpc/* or /chart/* paths | Use the current REST/MCP endpoints on mcp.agentastrology.com |
7. Compute limits and silent fallbacks
- Chart dates accept years −4000..4000; outside that, schema validation
answers with
invalid_inputnamingyear(§3, "Value out of range"). - Precision ephemeris data covers roughly 1200–2999 AD. Outside it, main
bodies fall back to a built-in lower-precision model, but Chiron has a hard
range of JD 1967601.5–3419437.5 (about 675 AD to 4650 AD) — and default
charts include Chiron, so charts before ~675 AD fail with the
sweph_failureshown under "Ephemeris limit — Chiron range". Dates several millennia out exceed the fallback model too and fail with theinternal_errornaming a missing ephemeris file. - Polar latitudes are NOT an error: inside the polar circles, where
Placidus/Koch-family cusps are undefined, the service computes Porphyry
cusps instead and returns success — the same behavior as the major
reference software. The chart's
summary.houseSystemlabel still names the system you asked for, so check latitudes above ~66° if cusps look evenly trisected.
Related
public-connect.md— get a key and connect Claude Code, Claude Desktop, or plain REST.public-quickstart.md— first successful calls.