API Status and Limits

The /v2 API is free, needs no account and no key, and runs on shared serverless functions with no dedicated capacity behind it. This page states the real limits, what actually counts against them, what a throttled or failed request looks like, and how to reach us when something is wrong.

On this page

Rate limits

WindowLimit
Per minute300 requests
Per hour2,000 requests
Per day5,000 requests

Every request today is served at the single free Basic tier; the paid Pro, Ultra and Mega tiers are published and not yet on sale (see API Keys). Whichever of the three windows is closest to exhausted governs a given request.

Cache hits are free

The limiter only counts a request that reaches the origin function. A response served straight from Netlify's CDN cache costs no computation and consumes no quota. Because the answer for a given instant and place never changes on a deterministic endpoint, a caller who caches aggressively, or who lets a shared CDN cache do it for them, can serve far more than these numbers in total requests and never touch a limit. That is the intended design, not a loophole: ask for the same thing twice inside its cache lifetime and only the first request counts.

Reading the rate limit headers

Sent on every response, success or failure, so you see the budget before you ever hit it:

HeaderMeaning
RateLimit-PolicyEvery policy and its quota: q= is the quota, w= is the window in seconds.
RateLimitThe single policy closest to exhausted right now: r= remaining, t= seconds until that window resets.
X-RateLimit-LimitLegacy alias: the quota of the binding policy.
X-RateLimit-RemainingLegacy alias: requests left in the binding policy's window.
X-RateLimit-ResetLegacy alias: an absolute Unix timestamp, not a countdown.

RateLimit-Policy and RateLimit follow the IETF rate limit header fields draft, which is a draft, not a published RFC. The X-RateLimit-* headers are kept alongside as the older, still-common convention.

Because the response itself is cached at the CDN, a cache hit returns the header values that were computed for whoever caused the origin miss, so a cached response's numbers can be stale. That is not misleading: a cache hit consumed no quota. Check Cache-Status to tell which happened: fwd=miss means the origin ran and the numbers are current, hit means it did not and nothing was spent.

Getting throttled: 429

HTTP/1.1 429 Too Many Requests
Retry-After: 37

Exceeding a window returns 429 with code RATE_LIMITED and an integer Retry-After in seconds. Honor it: wait the stated number of seconds before retrying. Retrying immediately just earns another 429. Two ways to stay clear of the limit entirely: cache the response, since a given instant and place always return the same answer, or ask for a range in one request with start, end and step instead of one request per instant.

Request and range caps

Independent of the rate limit, every endpoint bounds how much a single request can ask for. A request over its cap is refused outright with 400 RANGE_TOO_LARGE; it is never silently truncated to a shorter 200. The published cap table, by shape and anonymous tier:

ShapeAnonymous capApplies to
Daily rows, one body366 rows/v2/sun, /v2/moon, /v2/rise-set, /v2/twilight, /v2/dark-window, /v2/equation-of-time, /v2/libration
Daily rows, all bodies93 rows/v2/planet-board, /v2/cycles
Sub-daily grid31 days/v2/positions, /v2/time, /v2/sidereal-time
Event search100 events/v2/seasons, /v2/apsides, /v2/moon-nodes, /v2/eclipses, /v2/retrogrades, /v2/conjunctions, /v2/separation, /v2/phases, /v2/jupiter-moons

No shape may exceed 10,000 rows in total, whatever the request. An event-search endpoint is also bounded on the span it is allowed to search, independently of how many rows it returns; the exact per-endpoint window is published at /v2/conventions. Two further ceilings apply everywhere: at most 20 bodies in one request, and a response is refused with 413 RESPONSE_TOO_LARGE if it would serialize past 2 MB, well inside Netlify Functions' 6 MB hard limit.

Service errors: 503

A small number of failures return 503 rather than a caller mistake: an unread dataset, an unavailable time zone database, or an endpoint taken deliberately offline. Each of these carries an integer Retry-After, the same way 429 does; wait that long and retry. 429 and 503 are the only two statuses that always carry Retry-After.

Reporting an outage or a wrong number

If a request returns 500 or 503, or a number looks wrong, email info@cyclecalcs.com. Include the exact request URL, and the request_id from the response body if there is one. Every 429, 500 and 503 carries a fresh request_id; a 400 validation failure never does, because that response is cacheable and a shared identifier would be wrong for everyone who gets the cached copy. A wrong number is treated as seriously as an outage: this is a positional astronomy API, and a plausible-looking wrong answer is worse than a visible failure.

Uptime

This is a free API on shared, serverless infrastructure with no dedicated capacity and no service level agreement. We do not publish or promise a specific uptime percentage on the anonymous tier. If an answer matters for something consequential, cache it, verify it independently, and do not make this API your only source.