astroapi-io-client 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (24) hide show
  1. astroapi_io_client-0.1.0/LICENSE.txt +15 -0
  2. astroapi_io_client-0.1.0/PKG-INFO +274 -0
  3. astroapi_io_client-0.1.0/README.md +262 -0
  4. astroapi_io_client-0.1.0/pyproject.toml +23 -0
  5. astroapi_io_client-0.1.0/setup.cfg +4 -0
  6. astroapi_io_client-0.1.0/src/astroapi_client/__init__.py +56 -0
  7. astroapi_io_client-0.1.0/src/astroapi_client/_validation.py +626 -0
  8. astroapi_io_client-0.1.0/src/astroapi_client/client.py +480 -0
  9. astroapi_io_client-0.1.0/src/astroapi_client/errors.py +38 -0
  10. astroapi_io_client-0.1.0/src/astroapi_client/py.typed +0 -0
  11. astroapi_io_client-0.1.0/src/astroapi_client/types.py +1355 -0
  12. astroapi_io_client-0.1.0/src/astroapi_io_client.egg-info/PKG-INFO +274 -0
  13. astroapi_io_client-0.1.0/src/astroapi_io_client.egg-info/SOURCES.txt +22 -0
  14. astroapi_io_client-0.1.0/src/astroapi_io_client.egg-info/dependency_links.txt +1 -0
  15. astroapi_io_client-0.1.0/src/astroapi_io_client.egg-info/top_level.txt +1 -0
  16. astroapi_io_client-0.1.0/tests/test_aspect_windows_client.py +70 -0
  17. astroapi_io_client-0.1.0/tests/test_calendar_export.py +22 -0
  18. astroapi_io_client-0.1.0/tests/test_client.py +681 -0
  19. astroapi_io_client-0.1.0/tests/test_davison_input.py +30 -0
  20. astroapi_io_client-0.1.0/tests/test_house_ingresses_client.py +59 -0
  21. astroapi_io_client-0.1.0/tests/test_next_expansions_client.py +543 -0
  22. astroapi_io_client-0.1.0/tests/test_onboarding_context_client.py +39 -0
  23. astroapi_io_client-0.1.0/tests/test_planetary_events_client.py +74 -0
  24. astroapi_io_client-0.1.0/tests/test_varga_input.py +99 -0
@@ -0,0 +1,15 @@
1
+ ISC License
2
+
3
+ Copyright (c) 2026 AstroAPI
4
+
5
+ Permission to use, copy, modify, and/or distribute this software for any
6
+ purpose with or without fee is hereby granted, provided that the above
7
+ copyright notice and this permission notice appear in all copies.
8
+
9
+ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10
+ WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11
+ MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12
+ ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13
+ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14
+ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
15
+ IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
@@ -0,0 +1,274 @@
1
+ Metadata-Version: 2.4
2
+ Name: astroapi-io-client
3
+ Version: 0.1.0
4
+ Summary: Synchronous Python client for the AstroAPI.io calculation gateway
5
+ License-Expression: ISC
6
+ Classifier: Programming Language :: Python :: 3
7
+ Classifier: Programming Language :: Python :: 3 :: Only
8
+ Requires-Python: >=3.10
9
+ Description-Content-Type: text/markdown
10
+ License-File: LICENSE.txt
11
+ Dynamic: license-file
12
+
13
+ # AstroAPI.io Python client
14
+
15
+ The source client covers all forty source-candidate calculation routes: thirty-nine standard operations plus the separate Pro Nakshatra Timeline. Production R192 exposes thirty-six standard routes plus the timeline, for thirty-seven total. Minor-body calculations are not part of the contract. This package is not registry-published.
16
+
17
+ An unpublished, synchronous client for Python 3.10+, with no runtime dependencies.
18
+ The package import is `astroapi_client`; the distribution name is
19
+ `astroapi-io-client`. This source release does not imply publication to PyPI or
20
+ new deployed API capabilities.
21
+
22
+ Install from a local checkout using `python -m pip install ./clients/python`.
23
+ Building needs setuptools and wheel; the installed client uses only the standard
24
+ library. Keep API credentials in trusted server or command-line environments.
25
+
26
+ ```python
27
+ from astroapi_client import AstroAPIClient, HTTPError, RequestTimeoutError
28
+
29
+ # Reads ASTROAPI_KEY; alternatively pass api_key=your_key explicitly.
30
+ client = AstroAPIClient()
31
+ birth = {
32
+ "date": "1990-01-01",
33
+ "time": "12:00",
34
+ "timezone": "America/New_York",
35
+ "lat": 40.7128,
36
+ "lon": -74.006,
37
+ }
38
+
39
+ try:
40
+ result = client.birth_chart(birth)
41
+ sun = result["chart"]["planets"]["Sun"]
42
+ except HTTPError as error:
43
+ print(error.status, error.retry_after) # Sanitized; does not trigger a retry.
44
+ except RequestTimeoutError:
45
+ print("Deadline exceeded; this request may already have consumed quota.")
46
+ ```
47
+
48
+ An explicit key takes precedence over `ASTROAPI_KEY`. Keys must contain exactly
49
+ 64 hexadecimal characters. The SDK never reads another application's settings,
50
+ environment files, browser sessions, or stored keys. Do not place keys in source
51
+ control, browser code, URLs, or logs.
52
+
53
+ ## Methods and current response shapes
54
+
55
+ Each method takes one dictionary using the gateway's field names. It returns
56
+ the **complete parsed JSON object without changing its envelope**. Exported
57
+ `TypedDict` input types and `py.typed` support editors. Methods without a detailed response type return
58
+ `dict[str, Any]` (`JSONResponse`). `lunar_nodes`, `returns`, `solar_arcs` and
59
+ `vimshottari` provide detailed response type hints; Vimshottari is a union of
60
+ `VimshottariResponseDepth2`, `VimshottariResponseDepth3`, and
61
+ `VimshottariResponseDepth4`. The return's existing
62
+ nested chart remains `JSONResponse`.
63
+ Type hints describe the server contract and do not perform response validation.
64
+
65
+ | Method | OpenAPI operation ID | Input | Important response path |
66
+ |---|---|---|---|
67
+ | `birth_chart` | `calculateBirthChart` | Birth fields; optional zodiac/ayanamsa | `result["chart"]` |
68
+ | `place_search` **beta** | `searchPlaces` | Query; optional uppercase country codes and limit | Ordered versioned candidates in `result["results"]` |
69
+ | `timezone_resolve` **beta** | `resolvePlaceTimezone` | Versioned place ID plus local date/time | Explicit civil-time status/candidates and unique `resolved_input` |
70
+ | `natal_context` **beta** | `calculateNatalContext` | Birth; optional zodiac/ayanamsa/houses/formats | Stable facts, calculation hash, and selected text artifacts |
71
+ | `aspects` | `calculateAspects` | `{"birth": birth}`; optional zodiac/ayanamsa | `result["aspects"]["aspects"]` is the array |
72
+ | `houses` | `calculateHouses` | `{"birth": birth, "system": "whole_sign"}`; optional zodiac/ayanamsa | Full chart at top level; `result["houses"]["systems"]` |
73
+ | `synastry` | `calculateSynastry` | `{"personA": birth, "personB": other}`; optional zodiac/ayanamsa | `result["charts"]`, `result["aspects"]` |
74
+ | `transits` | `calculateTransits` | Birth plus required `transitDate`; optional `transitTime`, timezone, zodiac/ayanamsa | `result["transits"]["natal_chart"]`, `transit_chart`, `aspects` |
75
+ | `horoscope` **beta** | `calculateHoroscope` | Birth plus optional transit fields, timezone, zodiac/ayanamsa | `result["interpretation"]` |
76
+ | `nakshatra_timeline` | `calculateNakshatraTimeline` | Planet, `start_utc`, `end_utc`, ayanamsa | `result["segments"]` |
77
+ | `natal_svg` **beta** | `calculateNatalSvg` | Birth; optional zodiac, ayanamsa, houses/theme/show_aspects, named aspect profile or custom aspects, `include_png` | `result["svg"]`, optional `result["png"]` |
78
+ | `vargas` **beta** | `calculateVargas` | Birth; optional ayanamsa and one of twenty-four divisions through D150 | `result["bodies"]["moon"]["sign"]`, `result["ascendant"]` |
79
+ | `vimshottari` **beta** | `calculateVimshottari` | Birth; optional ayanamsa and depth 2/3/4 | `result["birth_balance"]`, `result["periods"]` |
80
+ | `ashtakoota` **beta** | `calculateAshtakoota` | Required groom/bride births; optional ayanamsa | `result["kootas"]`, `result["total"]`; no verdict/advice |
81
+ | `ashtakavarga` **beta** | `calculateAshtakavarga` | Required birth; optional ayanamsa and `include_reductions` | BAV, Prastara, SAV, and opt-in reductions/pindas; no interpretation |
82
+ | `numerology` **beta** | `calculateNumerology` | Gregorian `birth_date`; optional ASCII `name`, boolean `master_numbers`/`y_vowel`, integer `year` | `numbers` contains symbolic reduction traces; no interpretations or predictions |
83
+ | `composite` **beta** | `calculateComposite` | personA/personB; optional zodiac/ayanamsa/house_system | `result["planets"]`, synthetic `result["houses"]` |
84
+ | `panchang` **beta** | `calculatePanchang` | at civil snapshot; optional ayanamsa | `result["vara"]`, `result["solar_events"]` |
85
+ | `secondary_progressions` **beta** | `calculateSecondaryProgressions` | birth and target civil time; optional zodiac/ayanamsa/house_system | `result["progressed_planets"]`, separate natal_context |
86
+ | `varga_svg` **beta** | `calculateVargaSvg` | birth; optional ayanamsa/division/layout/theme/`include_png` | `result["svg"]`, `result["chart"]`, optional `result["png"]` |
87
+ | `lunar_nodes` **beta** | `calculateLunarNodes` | at civil instant without coordinates; optional zodiac/ayanamsa | `result["mean"]["north"]`, `result["true"]["north"]` |
88
+ | `returns` **beta** | `calculateReturns` | birth, body sun/moon, after civil time; optional zodiac/ayanamsa/house_system/location | `result["return_utc"]`, `result["chart"]`, `result["search"]` |
89
+ | `solar_arcs` **beta** | `calculateSolarArcs` | birth and target civil time; optional zodiac/ayanamsa/house_system | `result["arc"]`, `result["directed_planets"]`, `result["directed_angles"]`, `result["directed_houses"]` |
90
+ | `moon_phases` **beta** | `calculateMoonPhases` | instant `at` or calendar civil `start`/`end`; optional display timezone and `include_ical` | `result["result"]["phase_sector"]` or `result["result"]["events"]`; optional `result["result"]["ical"]` |
91
+ | `planetary_events` **beta** | `calculatePlanetaryEvents` | whole-second UTC interval, event types/bodies; optional aspects/zodiac/ayanamsa | `result["result"]["events"]` |
92
+ | `house_ingresses` **beta** | `calculateHouseIngresses` | reference birth, whole-second UTC interval, bodies; optional Equal/Whole Sign and zodiac/ayanamsa | `result["result"]["events"]`, fixed cusps |
93
+ | `aspect_windows` **beta** | `calculateAspectWindows` | whole-second UTC interval, bodies; optional aspects/orb/zodiac/ayanamsa/iCalendar | `result["result"]["windows"]` |
94
+ | `event_calendar` **beta** | `calculateEventCalendar` | whole-second UTC interval and selected event families | `result["result"]["events"]`; optional iCalendar, strict opt-in fixed-column CSV, and opt-in deduplicated pass groups |
95
+ | `synastry_svg` **beta** | `calculateSynastrySvg` | two births; optional zodiac/houses/theme/aspects/`include_png` | `result["svg"]`, optional `result["png"]` |
96
+ | `transit_svg` **beta** | `calculateTransitSvg` | birth and transit date; optional display controls/`include_png` | `result["svg"]`, optional `result["png"]` |
97
+ | `natal_report` **beta** | `calculateNatalReport` | birth; optional zodiac/houses/theme/SVG/download | `result["html"]`, source-linked `result["facts"]`, optional `result["download"]` |
98
+ | `birth_time_sensitivity` **beta** | `calculateBirthTimeSensitivity` | civil interval up to six hours, or explicit unknown/approximate time, plus coordinates | `result["result"]["samples"]`, optional workflow classification |
99
+ | `fixed_stars` **beta** | `calculateFixedStars` | civil instant and 1–6 documented stars | `result["objects"]`; no minor bodies |
100
+ | `relocation_chart` **beta** | `calculateRelocationChart` | birth and destination coordinates; optional zodiac/ayanamsa/houses | `result["natal_reference"]`, `result["relocated_chart"]` |
101
+ | `astrocartography` **beta** | `calculateAstrocartography` | civil instant, 1–10 unique Sun-through-Pluto bodies, optional `include_geojson`, `include_geojson_seq`, `include_ndjson`, `include_topojson`, `include_kml`, `include_gpx`, `include_csv`, `include_wkt`, `include_wkb`, `include_gml`, `basemap`, `include_svg`, `include_png`, and `theme` | bounded geometry plus optional deterministic RFC 7946 GeoJSON, RFC 8142 GeoJSON Text Sequences, newline-delimited GeoJSON Features, unquantized TopoJSON, OGC KML 2.2, GPX 1.1, RFC 4180 CSV, WKT, WKB, GML 3.2 artifacts, and 1200×800 SVG/PNG coordinate-grid or bundled Natural Earth coastline map |
102
+ | `eclipse_geometry` **beta** | `calculateEclipseGeometry` | whole-second UTC interval up to 366 days, selected solar/lunar types, optional WGS84 observer | global events and optional sea-level/no-refraction snapshot in each event |
103
+ | `electional_search` **beta** | `calculateElectionalSearch` | location, one or more AND-only filters, and UTC interval up to seven days | hourly matches and sampled windows |
104
+ | `natal_analysis` **beta** | `calculateNatalAnalysis` | birth, aspect controls, and strict sidereal graha-drishti or partial-Shadbala opt-ins | aspects/patterns plus optional `graha_drishti` or `shadbala_foundation` |
105
+ | `planetary_hours` **beta** | `calculatePlanetaryHours` | civil date, IANA timezone, and coordinates | twenty-four unequal day/night temporal hours and rulers |
106
+ | `davison` **beta** | `calculateDavison` | personA/personB; optional zodiac/ayanamsa/house_system | `result["chart"]`, `result["midpoint"]`, `result["metadata"]` |
107
+
108
+ The never-live extended-sky methods were retired rather than depend on licensed
109
+ minor-body source data. They are not SDK methods or API contracts.
110
+
111
+ Graha drishti is a separate opt-in on `natal_analysis`. Exact `True` requires
112
+ explicit `zodiac: "sidereal"`; omission/false preserves the existing response.
113
+ Ruleset `parashari_whole_sign_full_aspects_v1` includes Sun, Moon, Mars,
114
+ Mercury, Jupiter, Venus, and Saturn only. It returns full whole-sign targets and
115
+ occupying classical grahas, with no node aspects, partial strengths, or
116
+ interpretation. The request remains one standard quota unit.
117
+
118
+ Synastry cross-aspects retain `transit` for person B and `natal` for person A.
119
+ The horoscope route is a beta, deterministic structured interpretation; it is
120
+ not a stable prose-report or LLM contract. Birth-chart/aspects compute both
121
+ legacy house systems; the houses method filters to the requested system.
122
+ Its `placidus`, `porphyry`, `meridian`, `campanus`, `regiomontanus`,
123
+ `alcabitius`, `koch`, `morinus`, `topocentric`, `sripati`, `vehlow`, `horizon`, `krusinski`, `sunshine`, `sunshine_alt`, `savard`, `pullen_sd`, `pullen_sr`, `carter`, `apc`, `equal_mc`, and `natural` options are beta, with explicit rejection and no fallback. Gauquelin is Houses-only because its response contains 36 sectors; natal
124
+ SVG accepts the preceding twelve-house systems, while other chart/report routes do not accept
125
+ Porphyry, Meridian, Campanus, Regiomontanus, Alcabitius, Koch, Morinus, Topocentric, Sripati, Vehlow, Horizon/Azimuth, Krusinski-Pisa-Goelzer, Sunshine, Sunshine alternative, Savard-A, Pullen SD, Pullen SR, Carter, APC, Equal MC, or Natural. Meridian cusp 1 is
126
+ the Equatorial Ascendant; Campanus, Regiomontanus, Alcabitius, and Koch cusp 1
127
+ are the physical Ascendant. Morinus cusps are latitude-independent; its cusps 1 and 10 are not the physical Ascendant and Midheaven. Topocentric uses Polich/Page house geometry with the physical Ascendant and Midheaven as cusps 1 and 10; it does not alter planet positions. Sripati shifts each cusp to the midpoint of the preceding Porphyry sector; its cusps 1 and 10 are not the physical angles. Vehlow uses twelve equal 30-degree ecliptic houses with the physical Ascendant at the center of house 1; its cusps 1 and 10 are not the physical angles. Horizon/Azimuth projects twelve equal local-horizon divisions through vertical circles to the ecliptic; cusp 1 is not the physical Ascendant and cusp 10 is the Midheaven. Krusinski-Pisa-Goelzer projects twelve equal Ascendant-zenith great-circle divisions through celestial meridian circles; cusps 1 and 10 are the physical Ascendant and Midheaven. Sunshine uses the Treindl construction from trisected solar diurnal and nocturnal semi-arcs; cusps 1 and 10 are the physical Ascendant and Midheaven. Sunshine alternative uses the separate Makransky prime-vertical projection for the same solar house points. Savard-A projects one-third and two-thirds geographic-latitude circles through the prime vertical; cusps 1 and 10 are the physical Ascendant and Midheaven and opposite cusps are antipodal. Pullen SD redistributes each ecliptic quadrant's deviation from 90 degrees with quarter/half/quarter weighting; cusp 10 is the physical Midheaven and cusp 1 uses the orientation-adjusted Ascendant. Pullen SR proportions complementary quadrant house widths as rx, x, rx and r³x, r⁴x, r³x; cusp 10 is the physical Midheaven and cusp 1 uses the orientation-adjusted Ascendant. Carter divides right ascension into twelve equal arcs from the orientation-adjusted Ascendant and projects them to the ecliptic; cusp 10 is not generally the physical Midheaven. APC divides the Ascendant parallel into six sectors below and six above the horizon; cusps 1 and 10 are the orientation-adjusted angles and intermediate opposite cusps are not generally antipodal. Equal MC fixes the physical Midheaven at cusp 10 and places twelve equal 30-degree ecliptic houses; cusp 1 is generally not the physical Ascendant. Natural fixes cusp 1 at zero degrees Aries in the selected zodiac and places all cusps on sign boundaries; physical angles remain separate. Gauquelin is Houses-only and returns 36 clockwise semiarc sector boundaries under the existing houses map; sector 1 is Ascendant and sector 10 is Midheaven. Regiomontanus, Alcabitius, Koch, Morinus, Topocentric, Sripati, Vehlow, Horizon/Azimuth, Krusinski-Pisa-Goelzer, Sunshine, Sunshine alternative, Savard-A, Pullen SD, Pullen SR, Carter, APC, Equal MC, Natural, and Gauquelin are not admitted by saved
128
+ profiles. Equal/Whole Sign remain the existing chart defaults.
129
+
130
+ ```python
131
+ timeline = client.nakshatra_timeline({
132
+ "planet": "moon",
133
+ "start_utc": "2026-01-01T00:00:00Z",
134
+ "end_utc": "2026-01-03T00:00:00Z",
135
+ "ayanamsa": "lahiri",
136
+ })
137
+ ```
138
+
139
+ The timeline is Pro-only, always sidereal, supports the nine documented bodies,
140
+ and allows a positive range up to 180 days. It uses a separate quota. It does not
141
+ accept a sampling-step override. Dates across routes remain limited to
142
+ 1900–2050. Legacy chart nodes and timeline Rahu/Ketu remain mean-node calculations;
143
+ the separate `lunar_nodes` beta supplies a distinct geometric osculating model.
144
+ Installing the SDK does not change any existing calculation convention or accuracy.
145
+
146
+ The SVG route accepts birth data, not an uploaded chart, SVG, URL, font or image.
147
+ Defaults are Equal houses, light theme, major aspects shown, tropical zodiac
148
+ and Lahiri when sidereal. The result contains an SVG string inside JSON; it is
149
+ not a browser credential or hosted-widget API.
150
+
151
+ Natal, synastry, transit and varga SVG plus natal report accept exact `light`,
152
+ `dark`, or `monochrome`. Defaults are unchanged. Monochrome is a fixed
153
+ grayscale palette, not an accessibility certification; optional PNG pixels are
154
+ grayscale while retaining RGB encoding.
155
+
156
+ Natal report HTML accepts optional `report_branding` with a required 1–64 character bounded ASCII `display_name` and optional exact `amber`, `blue`, or `green` accent. It is escaped, text-only, visibly attributed to AstroAPI, and invalid with non-HTML formats; omission preserves exact predecessor bytes. No logo, URL, markup, template, remote resource, localization, or fact mutation is accepted.
157
+
158
+ Natal report downloads accept exact `include_download: True`. Omitted
159
+ `download_format` retains HTML; exact `pdf`, `docx`, `xlsx`, `ods`, `epub`, `odt`, `rtf`, `json`, `csv`, `xml`, `ndjson`, `tsv`, `markdown`, `text`, or
160
+ `zip` selects deterministic PDF, editable macro-free OOXML, editable
161
+ macro-free and formula-free OOXML workbook, macro-free ODF 1.3, editable dependency-free RTF, machine-readable JSON, tabular CSV, fixed-order XML, canonical one-record-per-fact NDJSON, escaped fixed-column TSV, UTF-8 calculation facts/plain text, or the unchanged fixed
162
+ HTML/PDF/calculation-JSON bundle.
163
+
164
+ Vargas and Vimshottari always use sidereal coordinates with Lahiri default.
165
+ Vargas supports D1/D2/D3/D4/D5/D6/D7/D8/D9/D10/D11/D12/D16/D20/D24/D27/D30/D40/D45/D60/D81/D108/D144/D150 (default D9) for nine traditional bodies plus the
166
+ Ascendant, not outer planets. Vimshottari returns nine major periods, each with
167
+ nine subperiods, using a fixed 365.25-day Julian year. Its first period begins
168
+ at the theoretical natal-major start, which can precede birth. Arithmetic
169
+ schedule dates may extend outside the ephemeris input-date window; they are
170
+ not predictions or additional ephemeris calculations. Optional `depth: 3` adds
171
+ one fixed third level; omission or `depth: 2` preserves the original response.
172
+ No arbitrary depth,
173
+ year model, range or specialist signoff is claimed. Each of these beta routes
174
+ costs one standard-call unit under unchanged plan quotas/prices and uses the
175
+ normal request deadline.
176
+
177
+ ## Validation, deadlines and errors
178
+
179
+ The client checks required fields, supported enums, numeric coordinate bounds,
180
+ date/time formats, timeline ranges and the 128 KiB request limit before sending.
181
+ It accepts JSON numbers for coordinates and documented lowercase enum values;
182
+ unknown fields and explicit null optionals are rejected. Omit unused optional
183
+ fields. Timezone identifiers, DST ambiguity/nonexistence and undefined
184
+ Ascendant geometry remain server validation. Supplying a known UTC instant
185
+ avoids ambiguous local clock changes. No defaults are inserted into payloads.
186
+
187
+ Normal requests have a 20-second caller deadline; planetary events, eclipse geometry, electional search and aspect windows have 35 seconds; timelines have 70 seconds.
188
+ Configure `timeout=`, `planetary_events_timeout=`, and `timeline_timeout=` with finite positive seconds up to
189
+ 300. The deadline covers network establishment, TLS, headers, response body and
190
+ JSON parsing, but starts after local input validation/serialization.
191
+
192
+ The synchronous call uses a daemon transport worker so a stalled operating-system
193
+ DNS lookup or slowly arriving headers/body cannot keep the caller waiting past
194
+ its deadline. Cancellation shuts down the known socket. A DNS call itself
195
+ cannot be forcibly stopped; its worker retains a slot until it exits and checks
196
+ cancellation before sending credentials. There are at most eight outstanding
197
+ transport workers **across all client instances in a process**. Exhaustion raises
198
+ `ClientBusyError` without starting another request. No context manager or explicit
199
+ close is needed; every completed exchange closes its connection. Successful
200
+ calls do not reuse connections.
201
+
202
+ `max_response_bytes=` defaults to 4 MiB and may be set from 1 byte through 64 MiB.
203
+ Oversized, truncated, compressed, non-object, duplicate-key or malformed JSON
204
+ responses are rejected. Bodies are read incrementally; compressed responses are
205
+ not decompressed. The standard library also limits HTTP header count and line
206
+ length. Errors omit request payloads, response bodies, credentials, raw headers,
207
+ and underlying transport exceptions. `HTTPError` exposes only numeric `status`
208
+ and sanitized `retry_after` (integer seconds or a normalized HTTP date).
209
+
210
+ There are **no automatic retries**, including for 429, 503 or timeouts. Once the
211
+ gateway reserves quota, even an invalid payload, dependency failure or timeout
212
+ consumes a unit. A client disconnect does not cancel admitted server work.
213
+
214
+ ## Transport origin
215
+
216
+ The default is `https://api.astroapi.io`; calls use `POST /api/astro/...` with
217
+ `Authorization: Bearer`. A custom `base_url` must be a trusted HTTPS origin
218
+ without a path, credentials, query or fragment. Selecting another HTTPS origin
219
+ authorizes sending your key to that origin. TLS certificates and hostnames are
220
+ verified. Proxy environment variables, cookies and redirects are not used.
221
+ Every redirect is returned as `HTTPError`; keys are never forwarded to its
222
+ destination.
223
+
224
+ For local Node gateway testing only:
225
+
226
+ ```python
227
+ client = AstroAPIClient(
228
+ api_key=your_synthetic_key,
229
+ base_url="http://127.0.0.1:5100",
230
+ allow_http_localhost=True,
231
+ )
232
+ ```
233
+
234
+ HTTP requires explicit opt-in and both a loopback/localhost URL and a loopback
235
+ connected peer. Never point the client directly at the internal Python engine.
236
+
237
+ ## Offline checks
238
+
239
+ From this directory, after local installation:
240
+
241
+ ```sh
242
+ python -m unittest discover -s tests -v
243
+ ```
244
+
245
+ Tests use generated synthetic keys and local HTTP/HTTPS servers. Real TLS tests
246
+ generate temporary certificates with local OpenSSL and explicitly skip if it is
247
+ unavailable; no certificate keys are committed. They do not call the
248
+ production API or Stripe. The example under `examples/` makes a billable API
249
+ request when explicitly run with a real key. Its executable-example test replaces
250
+ the transport with a local mock and uses no real account key.
251
+
252
+ ## Additional bounded beta calculations
253
+
254
+ - Composite: strict personA/personB birth objects; shortest-arc planetary and angle midpoints. Equal/Whole Sign houses are synthetic from midpoint Ascendant, not physical/Davison houses. Near-antipodal sources reject; no Placidus, nodes or speeds.
255
+ - Panchang: strict at civil snapshot and optional ayanamsa, always sidereal. Latitude is inclusive [-88,88]. Classifications are at the requested instant; solar events use the USNO -50 arcminute horizon at elevation 0, not Hindu-geocentric sunrise. Civil weekday and sunrise-based vara differ. Unavailable bounded sunrise gives explicit null vara. Historical local offsets can include seconds; paired UTC fields are interoperable.
256
+ - Secondary progressions: strict birth plus target date/time/timezone, target>=birth. Planets only, fixed 365.2421904-day year in uniform TT; natal_context angles/houses are not progressed. Derived progressed_utc may contain actual leap second 60; preserve the string rather than passing it blindly to JavaScript Date/Python datetime. No progressed angles/houses/aspects/nodes/speeds or prediction claim.
257
+ - Varga SVG: birth plus optional ayanamsa, any supported D1/D2/D3/D4/D5/D6/D7/D8/D9/D10/D11/D12/D16/D20/D24/D27/D30/D40/D45/D60/D81/D108/D144/D150 division, layout north_indian/south_indian and theme light/dark/monochrome. Defaults D1/north_indian/dark; /vargas instead defaults D9. Original North fixed-house/South fixed-sign SVG, houses counted from divisional Ascendant; not bhava-chalit. Response.chart is the unchanged Vargas result; SVG is bounded to 128 KiB inside JSON.
258
+ D150 additionally accepts exact `d150_method` values `"deva_keralam_chandra_kala_nadi_non_uniform"`, `"deva_keralam_chandra_kala_nadi_uniform_direct"`, `"parivritti_cyclic"`, `"movable_aries_forward_fixed_taurus_reverse_dual_gemini_forward"`, `"movable_aries_forward_fixed_scorpio_reverse_dual_sagittarius_forward"`, `"movable_aries_forward_fixed_leo_reverse_dual_sagittarius_forward"`, and `"movable_source_sign_forward_fixed_source_sign_reverse_dual_source_sign_forward"`; omission retains the uniform-direct D150 response, and the field is rejected for every other division.
259
+
260
+ - Lunar nodes: `lunar_nodes` takes an `at` civil instant with no coordinates and optional zodiac/ayanamsa. The response provides north/south pairs for Meeus mean nodes and geometric DE421 osculating nodes, each with its explicit source frame. `LunarNodesInput` and `LunarNodesResponse` are exported types. Chart and timeline node fields keep the existing mean-node contract.
261
+ - Returns: `returns` takes birth, exact body `sun` or `moon`, and an `after` civil instant. Optional zodiac/ayanamsa, Equal/Whole Sign houses and `location:{lat,lon}` are supported; location defaults to birth coordinates. `ReturnsInput` and `ReturnsResponse` type the new request/envelope. Read `return_utc`, `chart` and `search`. The first return lies beyond a one-millisecond start exclusion and the full fixed 370-day Sun/32-day Moon TT window must fit the supported UTC range. `after` may precede birth. Preserve leap-second UTC strings; numerical tolerance does not establish physical accuracy.
262
+
263
+ - Solar arcs: `solar_arcs` takes strict birth plus target civil time and optional zodiac/ayanamsa/Equal or Whole Sign houses. Omitted `method` and exact `true_solar_arc` preserve the direct true tropical Sun arc. Exact `method: "mean_naibod"` applies the fixed 0.98564733° mean longitude key per elapsed 365.2421904-day TT model year. Both methods direct natal planets and Ascendant/MC uniformly and retain synthetic houses, one-unit accounting, the 1900–2050 dates, and natal-date ayanamsa policy. No relocation, converse/right-ascension arc, MC-derived angle recalculation, arbitrary key, aspects, nodes or speeds.
264
+ - Vimshottari depth: `depth` accepts integer 2, 3, or 4. Omission/2 and the complete depth-3 response remain unchanged. Depth 4 adds 6,561 Sookshma leaves, `birth_balance.sookshma_dasha_lord`, and exact count metadata. Fourth-level leaves omit the redundant display-only `years` field so the complete response stays below 1 MiB. Python rejects floats, strings and booleans. Depth 4 is synchronous-only under the durable-job 256 KiB cap. The arithmetic is bounded and deterministic, not arbitrary recursion or prediction; an admitted synchronous call costs one standard unit.
265
+ - Eclipse geometry: `eclipse_geometry` searches at most 366 UTC days. Optional `observer` coordinates add a fixed sea-level, no-refraction topocentric snapshot at each rounded global maximum. It does not calculate the location's maximum, contacts, visibility at other instants, paths, maps, terrain/weather, safety guidance or interpretation.
266
+ - Electional search: `electional_search` applies one or more documented AND-only filters at fixed hourly samples for at most seven days/168 samples. Results are sampled candidates without continuous guarantees, ranking, recommendations, void-of-course analysis or interpretation.
267
+ - Natal analysis: `natal_analysis` returns configured aspects, optional midpoint axes, and geometric Grand Trine, T-square, Grand Cross, and Yod matches for selected Sun-through-Pluto points. `additional_points` may explicitly select Vertex/Antivertex, Equatorial Ascendant/Descendant, and Lots of Fortune/Spirit; they stay separate from aspects and interpretation.
268
+ - Planetary hours: `planetary_hours` returns twelve daylight and twelve nighttime unequal temporal hours with Chaldean rulers. It requires a complete sunrise-sunset-next-sunrise sequence and supplies no electional advice.
269
+
270
+ All thirty deployed additive beta routes reserve one standard-call unit, accept only exact documented fields/enums, and retain existing prices, quotas and no automatic retry. Practitioner sign-off and a complete astrology catalogue are not claimed.
271
+
272
+ ### Aspect windows beta
273
+
274
+ Use `client.aspect_windows(...)` for exact-hit-anchored major-aspect orb intervals, repeat numbering, clipped boundaries and optional RFC 5545 text. Inputs span at most 31 UTC days and use a 35-second default deadline.
@@ -0,0 +1,262 @@
1
+ # AstroAPI.io Python client
2
+
3
+ The source client covers all forty source-candidate calculation routes: thirty-nine standard operations plus the separate Pro Nakshatra Timeline. Production R192 exposes thirty-six standard routes plus the timeline, for thirty-seven total. Minor-body calculations are not part of the contract. This package is not registry-published.
4
+
5
+ An unpublished, synchronous client for Python 3.10+, with no runtime dependencies.
6
+ The package import is `astroapi_client`; the distribution name is
7
+ `astroapi-io-client`. This source release does not imply publication to PyPI or
8
+ new deployed API capabilities.
9
+
10
+ Install from a local checkout using `python -m pip install ./clients/python`.
11
+ Building needs setuptools and wheel; the installed client uses only the standard
12
+ library. Keep API credentials in trusted server or command-line environments.
13
+
14
+ ```python
15
+ from astroapi_client import AstroAPIClient, HTTPError, RequestTimeoutError
16
+
17
+ # Reads ASTROAPI_KEY; alternatively pass api_key=your_key explicitly.
18
+ client = AstroAPIClient()
19
+ birth = {
20
+ "date": "1990-01-01",
21
+ "time": "12:00",
22
+ "timezone": "America/New_York",
23
+ "lat": 40.7128,
24
+ "lon": -74.006,
25
+ }
26
+
27
+ try:
28
+ result = client.birth_chart(birth)
29
+ sun = result["chart"]["planets"]["Sun"]
30
+ except HTTPError as error:
31
+ print(error.status, error.retry_after) # Sanitized; does not trigger a retry.
32
+ except RequestTimeoutError:
33
+ print("Deadline exceeded; this request may already have consumed quota.")
34
+ ```
35
+
36
+ An explicit key takes precedence over `ASTROAPI_KEY`. Keys must contain exactly
37
+ 64 hexadecimal characters. The SDK never reads another application's settings,
38
+ environment files, browser sessions, or stored keys. Do not place keys in source
39
+ control, browser code, URLs, or logs.
40
+
41
+ ## Methods and current response shapes
42
+
43
+ Each method takes one dictionary using the gateway's field names. It returns
44
+ the **complete parsed JSON object without changing its envelope**. Exported
45
+ `TypedDict` input types and `py.typed` support editors. Methods without a detailed response type return
46
+ `dict[str, Any]` (`JSONResponse`). `lunar_nodes`, `returns`, `solar_arcs` and
47
+ `vimshottari` provide detailed response type hints; Vimshottari is a union of
48
+ `VimshottariResponseDepth2`, `VimshottariResponseDepth3`, and
49
+ `VimshottariResponseDepth4`. The return's existing
50
+ nested chart remains `JSONResponse`.
51
+ Type hints describe the server contract and do not perform response validation.
52
+
53
+ | Method | OpenAPI operation ID | Input | Important response path |
54
+ |---|---|---|---|
55
+ | `birth_chart` | `calculateBirthChart` | Birth fields; optional zodiac/ayanamsa | `result["chart"]` |
56
+ | `place_search` **beta** | `searchPlaces` | Query; optional uppercase country codes and limit | Ordered versioned candidates in `result["results"]` |
57
+ | `timezone_resolve` **beta** | `resolvePlaceTimezone` | Versioned place ID plus local date/time | Explicit civil-time status/candidates and unique `resolved_input` |
58
+ | `natal_context` **beta** | `calculateNatalContext` | Birth; optional zodiac/ayanamsa/houses/formats | Stable facts, calculation hash, and selected text artifacts |
59
+ | `aspects` | `calculateAspects` | `{"birth": birth}`; optional zodiac/ayanamsa | `result["aspects"]["aspects"]` is the array |
60
+ | `houses` | `calculateHouses` | `{"birth": birth, "system": "whole_sign"}`; optional zodiac/ayanamsa | Full chart at top level; `result["houses"]["systems"]` |
61
+ | `synastry` | `calculateSynastry` | `{"personA": birth, "personB": other}`; optional zodiac/ayanamsa | `result["charts"]`, `result["aspects"]` |
62
+ | `transits` | `calculateTransits` | Birth plus required `transitDate`; optional `transitTime`, timezone, zodiac/ayanamsa | `result["transits"]["natal_chart"]`, `transit_chart`, `aspects` |
63
+ | `horoscope` **beta** | `calculateHoroscope` | Birth plus optional transit fields, timezone, zodiac/ayanamsa | `result["interpretation"]` |
64
+ | `nakshatra_timeline` | `calculateNakshatraTimeline` | Planet, `start_utc`, `end_utc`, ayanamsa | `result["segments"]` |
65
+ | `natal_svg` **beta** | `calculateNatalSvg` | Birth; optional zodiac, ayanamsa, houses/theme/show_aspects, named aspect profile or custom aspects, `include_png` | `result["svg"]`, optional `result["png"]` |
66
+ | `vargas` **beta** | `calculateVargas` | Birth; optional ayanamsa and one of twenty-four divisions through D150 | `result["bodies"]["moon"]["sign"]`, `result["ascendant"]` |
67
+ | `vimshottari` **beta** | `calculateVimshottari` | Birth; optional ayanamsa and depth 2/3/4 | `result["birth_balance"]`, `result["periods"]` |
68
+ | `ashtakoota` **beta** | `calculateAshtakoota` | Required groom/bride births; optional ayanamsa | `result["kootas"]`, `result["total"]`; no verdict/advice |
69
+ | `ashtakavarga` **beta** | `calculateAshtakavarga` | Required birth; optional ayanamsa and `include_reductions` | BAV, Prastara, SAV, and opt-in reductions/pindas; no interpretation |
70
+ | `numerology` **beta** | `calculateNumerology` | Gregorian `birth_date`; optional ASCII `name`, boolean `master_numbers`/`y_vowel`, integer `year` | `numbers` contains symbolic reduction traces; no interpretations or predictions |
71
+ | `composite` **beta** | `calculateComposite` | personA/personB; optional zodiac/ayanamsa/house_system | `result["planets"]`, synthetic `result["houses"]` |
72
+ | `panchang` **beta** | `calculatePanchang` | at civil snapshot; optional ayanamsa | `result["vara"]`, `result["solar_events"]` |
73
+ | `secondary_progressions` **beta** | `calculateSecondaryProgressions` | birth and target civil time; optional zodiac/ayanamsa/house_system | `result["progressed_planets"]`, separate natal_context |
74
+ | `varga_svg` **beta** | `calculateVargaSvg` | birth; optional ayanamsa/division/layout/theme/`include_png` | `result["svg"]`, `result["chart"]`, optional `result["png"]` |
75
+ | `lunar_nodes` **beta** | `calculateLunarNodes` | at civil instant without coordinates; optional zodiac/ayanamsa | `result["mean"]["north"]`, `result["true"]["north"]` |
76
+ | `returns` **beta** | `calculateReturns` | birth, body sun/moon, after civil time; optional zodiac/ayanamsa/house_system/location | `result["return_utc"]`, `result["chart"]`, `result["search"]` |
77
+ | `solar_arcs` **beta** | `calculateSolarArcs` | birth and target civil time; optional zodiac/ayanamsa/house_system | `result["arc"]`, `result["directed_planets"]`, `result["directed_angles"]`, `result["directed_houses"]` |
78
+ | `moon_phases` **beta** | `calculateMoonPhases` | instant `at` or calendar civil `start`/`end`; optional display timezone and `include_ical` | `result["result"]["phase_sector"]` or `result["result"]["events"]`; optional `result["result"]["ical"]` |
79
+ | `planetary_events` **beta** | `calculatePlanetaryEvents` | whole-second UTC interval, event types/bodies; optional aspects/zodiac/ayanamsa | `result["result"]["events"]` |
80
+ | `house_ingresses` **beta** | `calculateHouseIngresses` | reference birth, whole-second UTC interval, bodies; optional Equal/Whole Sign and zodiac/ayanamsa | `result["result"]["events"]`, fixed cusps |
81
+ | `aspect_windows` **beta** | `calculateAspectWindows` | whole-second UTC interval, bodies; optional aspects/orb/zodiac/ayanamsa/iCalendar | `result["result"]["windows"]` |
82
+ | `event_calendar` **beta** | `calculateEventCalendar` | whole-second UTC interval and selected event families | `result["result"]["events"]`; optional iCalendar, strict opt-in fixed-column CSV, and opt-in deduplicated pass groups |
83
+ | `synastry_svg` **beta** | `calculateSynastrySvg` | two births; optional zodiac/houses/theme/aspects/`include_png` | `result["svg"]`, optional `result["png"]` |
84
+ | `transit_svg` **beta** | `calculateTransitSvg` | birth and transit date; optional display controls/`include_png` | `result["svg"]`, optional `result["png"]` |
85
+ | `natal_report` **beta** | `calculateNatalReport` | birth; optional zodiac/houses/theme/SVG/download | `result["html"]`, source-linked `result["facts"]`, optional `result["download"]` |
86
+ | `birth_time_sensitivity` **beta** | `calculateBirthTimeSensitivity` | civil interval up to six hours, or explicit unknown/approximate time, plus coordinates | `result["result"]["samples"]`, optional workflow classification |
87
+ | `fixed_stars` **beta** | `calculateFixedStars` | civil instant and 1–6 documented stars | `result["objects"]`; no minor bodies |
88
+ | `relocation_chart` **beta** | `calculateRelocationChart` | birth and destination coordinates; optional zodiac/ayanamsa/houses | `result["natal_reference"]`, `result["relocated_chart"]` |
89
+ | `astrocartography` **beta** | `calculateAstrocartography` | civil instant, 1–10 unique Sun-through-Pluto bodies, optional `include_geojson`, `include_geojson_seq`, `include_ndjson`, `include_topojson`, `include_kml`, `include_gpx`, `include_csv`, `include_wkt`, `include_wkb`, `include_gml`, `basemap`, `include_svg`, `include_png`, and `theme` | bounded geometry plus optional deterministic RFC 7946 GeoJSON, RFC 8142 GeoJSON Text Sequences, newline-delimited GeoJSON Features, unquantized TopoJSON, OGC KML 2.2, GPX 1.1, RFC 4180 CSV, WKT, WKB, GML 3.2 artifacts, and 1200×800 SVG/PNG coordinate-grid or bundled Natural Earth coastline map |
90
+ | `eclipse_geometry` **beta** | `calculateEclipseGeometry` | whole-second UTC interval up to 366 days, selected solar/lunar types, optional WGS84 observer | global events and optional sea-level/no-refraction snapshot in each event |
91
+ | `electional_search` **beta** | `calculateElectionalSearch` | location, one or more AND-only filters, and UTC interval up to seven days | hourly matches and sampled windows |
92
+ | `natal_analysis` **beta** | `calculateNatalAnalysis` | birth, aspect controls, and strict sidereal graha-drishti or partial-Shadbala opt-ins | aspects/patterns plus optional `graha_drishti` or `shadbala_foundation` |
93
+ | `planetary_hours` **beta** | `calculatePlanetaryHours` | civil date, IANA timezone, and coordinates | twenty-four unequal day/night temporal hours and rulers |
94
+ | `davison` **beta** | `calculateDavison` | personA/personB; optional zodiac/ayanamsa/house_system | `result["chart"]`, `result["midpoint"]`, `result["metadata"]` |
95
+
96
+ The never-live extended-sky methods were retired rather than depend on licensed
97
+ minor-body source data. They are not SDK methods or API contracts.
98
+
99
+ Graha drishti is a separate opt-in on `natal_analysis`. Exact `True` requires
100
+ explicit `zodiac: "sidereal"`; omission/false preserves the existing response.
101
+ Ruleset `parashari_whole_sign_full_aspects_v1` includes Sun, Moon, Mars,
102
+ Mercury, Jupiter, Venus, and Saturn only. It returns full whole-sign targets and
103
+ occupying classical grahas, with no node aspects, partial strengths, or
104
+ interpretation. The request remains one standard quota unit.
105
+
106
+ Synastry cross-aspects retain `transit` for person B and `natal` for person A.
107
+ The horoscope route is a beta, deterministic structured interpretation; it is
108
+ not a stable prose-report or LLM contract. Birth-chart/aspects compute both
109
+ legacy house systems; the houses method filters to the requested system.
110
+ Its `placidus`, `porphyry`, `meridian`, `campanus`, `regiomontanus`,
111
+ `alcabitius`, `koch`, `morinus`, `topocentric`, `sripati`, `vehlow`, `horizon`, `krusinski`, `sunshine`, `sunshine_alt`, `savard`, `pullen_sd`, `pullen_sr`, `carter`, `apc`, `equal_mc`, and `natural` options are beta, with explicit rejection and no fallback. Gauquelin is Houses-only because its response contains 36 sectors; natal
112
+ SVG accepts the preceding twelve-house systems, while other chart/report routes do not accept
113
+ Porphyry, Meridian, Campanus, Regiomontanus, Alcabitius, Koch, Morinus, Topocentric, Sripati, Vehlow, Horizon/Azimuth, Krusinski-Pisa-Goelzer, Sunshine, Sunshine alternative, Savard-A, Pullen SD, Pullen SR, Carter, APC, Equal MC, or Natural. Meridian cusp 1 is
114
+ the Equatorial Ascendant; Campanus, Regiomontanus, Alcabitius, and Koch cusp 1
115
+ are the physical Ascendant. Morinus cusps are latitude-independent; its cusps 1 and 10 are not the physical Ascendant and Midheaven. Topocentric uses Polich/Page house geometry with the physical Ascendant and Midheaven as cusps 1 and 10; it does not alter planet positions. Sripati shifts each cusp to the midpoint of the preceding Porphyry sector; its cusps 1 and 10 are not the physical angles. Vehlow uses twelve equal 30-degree ecliptic houses with the physical Ascendant at the center of house 1; its cusps 1 and 10 are not the physical angles. Horizon/Azimuth projects twelve equal local-horizon divisions through vertical circles to the ecliptic; cusp 1 is not the physical Ascendant and cusp 10 is the Midheaven. Krusinski-Pisa-Goelzer projects twelve equal Ascendant-zenith great-circle divisions through celestial meridian circles; cusps 1 and 10 are the physical Ascendant and Midheaven. Sunshine uses the Treindl construction from trisected solar diurnal and nocturnal semi-arcs; cusps 1 and 10 are the physical Ascendant and Midheaven. Sunshine alternative uses the separate Makransky prime-vertical projection for the same solar house points. Savard-A projects one-third and two-thirds geographic-latitude circles through the prime vertical; cusps 1 and 10 are the physical Ascendant and Midheaven and opposite cusps are antipodal. Pullen SD redistributes each ecliptic quadrant's deviation from 90 degrees with quarter/half/quarter weighting; cusp 10 is the physical Midheaven and cusp 1 uses the orientation-adjusted Ascendant. Pullen SR proportions complementary quadrant house widths as rx, x, rx and r³x, r⁴x, r³x; cusp 10 is the physical Midheaven and cusp 1 uses the orientation-adjusted Ascendant. Carter divides right ascension into twelve equal arcs from the orientation-adjusted Ascendant and projects them to the ecliptic; cusp 10 is not generally the physical Midheaven. APC divides the Ascendant parallel into six sectors below and six above the horizon; cusps 1 and 10 are the orientation-adjusted angles and intermediate opposite cusps are not generally antipodal. Equal MC fixes the physical Midheaven at cusp 10 and places twelve equal 30-degree ecliptic houses; cusp 1 is generally not the physical Ascendant. Natural fixes cusp 1 at zero degrees Aries in the selected zodiac and places all cusps on sign boundaries; physical angles remain separate. Gauquelin is Houses-only and returns 36 clockwise semiarc sector boundaries under the existing houses map; sector 1 is Ascendant and sector 10 is Midheaven. Regiomontanus, Alcabitius, Koch, Morinus, Topocentric, Sripati, Vehlow, Horizon/Azimuth, Krusinski-Pisa-Goelzer, Sunshine, Sunshine alternative, Savard-A, Pullen SD, Pullen SR, Carter, APC, Equal MC, Natural, and Gauquelin are not admitted by saved
116
+ profiles. Equal/Whole Sign remain the existing chart defaults.
117
+
118
+ ```python
119
+ timeline = client.nakshatra_timeline({
120
+ "planet": "moon",
121
+ "start_utc": "2026-01-01T00:00:00Z",
122
+ "end_utc": "2026-01-03T00:00:00Z",
123
+ "ayanamsa": "lahiri",
124
+ })
125
+ ```
126
+
127
+ The timeline is Pro-only, always sidereal, supports the nine documented bodies,
128
+ and allows a positive range up to 180 days. It uses a separate quota. It does not
129
+ accept a sampling-step override. Dates across routes remain limited to
130
+ 1900–2050. Legacy chart nodes and timeline Rahu/Ketu remain mean-node calculations;
131
+ the separate `lunar_nodes` beta supplies a distinct geometric osculating model.
132
+ Installing the SDK does not change any existing calculation convention or accuracy.
133
+
134
+ The SVG route accepts birth data, not an uploaded chart, SVG, URL, font or image.
135
+ Defaults are Equal houses, light theme, major aspects shown, tropical zodiac
136
+ and Lahiri when sidereal. The result contains an SVG string inside JSON; it is
137
+ not a browser credential or hosted-widget API.
138
+
139
+ Natal, synastry, transit and varga SVG plus natal report accept exact `light`,
140
+ `dark`, or `monochrome`. Defaults are unchanged. Monochrome is a fixed
141
+ grayscale palette, not an accessibility certification; optional PNG pixels are
142
+ grayscale while retaining RGB encoding.
143
+
144
+ Natal report HTML accepts optional `report_branding` with a required 1–64 character bounded ASCII `display_name` and optional exact `amber`, `blue`, or `green` accent. It is escaped, text-only, visibly attributed to AstroAPI, and invalid with non-HTML formats; omission preserves exact predecessor bytes. No logo, URL, markup, template, remote resource, localization, or fact mutation is accepted.
145
+
146
+ Natal report downloads accept exact `include_download: True`. Omitted
147
+ `download_format` retains HTML; exact `pdf`, `docx`, `xlsx`, `ods`, `epub`, `odt`, `rtf`, `json`, `csv`, `xml`, `ndjson`, `tsv`, `markdown`, `text`, or
148
+ `zip` selects deterministic PDF, editable macro-free OOXML, editable
149
+ macro-free and formula-free OOXML workbook, macro-free ODF 1.3, editable dependency-free RTF, machine-readable JSON, tabular CSV, fixed-order XML, canonical one-record-per-fact NDJSON, escaped fixed-column TSV, UTF-8 calculation facts/plain text, or the unchanged fixed
150
+ HTML/PDF/calculation-JSON bundle.
151
+
152
+ Vargas and Vimshottari always use sidereal coordinates with Lahiri default.
153
+ Vargas supports D1/D2/D3/D4/D5/D6/D7/D8/D9/D10/D11/D12/D16/D20/D24/D27/D30/D40/D45/D60/D81/D108/D144/D150 (default D9) for nine traditional bodies plus the
154
+ Ascendant, not outer planets. Vimshottari returns nine major periods, each with
155
+ nine subperiods, using a fixed 365.25-day Julian year. Its first period begins
156
+ at the theoretical natal-major start, which can precede birth. Arithmetic
157
+ schedule dates may extend outside the ephemeris input-date window; they are
158
+ not predictions or additional ephemeris calculations. Optional `depth: 3` adds
159
+ one fixed third level; omission or `depth: 2` preserves the original response.
160
+ No arbitrary depth,
161
+ year model, range or specialist signoff is claimed. Each of these beta routes
162
+ costs one standard-call unit under unchanged plan quotas/prices and uses the
163
+ normal request deadline.
164
+
165
+ ## Validation, deadlines and errors
166
+
167
+ The client checks required fields, supported enums, numeric coordinate bounds,
168
+ date/time formats, timeline ranges and the 128 KiB request limit before sending.
169
+ It accepts JSON numbers for coordinates and documented lowercase enum values;
170
+ unknown fields and explicit null optionals are rejected. Omit unused optional
171
+ fields. Timezone identifiers, DST ambiguity/nonexistence and undefined
172
+ Ascendant geometry remain server validation. Supplying a known UTC instant
173
+ avoids ambiguous local clock changes. No defaults are inserted into payloads.
174
+
175
+ Normal requests have a 20-second caller deadline; planetary events, eclipse geometry, electional search and aspect windows have 35 seconds; timelines have 70 seconds.
176
+ Configure `timeout=`, `planetary_events_timeout=`, and `timeline_timeout=` with finite positive seconds up to
177
+ 300. The deadline covers network establishment, TLS, headers, response body and
178
+ JSON parsing, but starts after local input validation/serialization.
179
+
180
+ The synchronous call uses a daemon transport worker so a stalled operating-system
181
+ DNS lookup or slowly arriving headers/body cannot keep the caller waiting past
182
+ its deadline. Cancellation shuts down the known socket. A DNS call itself
183
+ cannot be forcibly stopped; its worker retains a slot until it exits and checks
184
+ cancellation before sending credentials. There are at most eight outstanding
185
+ transport workers **across all client instances in a process**. Exhaustion raises
186
+ `ClientBusyError` without starting another request. No context manager or explicit
187
+ close is needed; every completed exchange closes its connection. Successful
188
+ calls do not reuse connections.
189
+
190
+ `max_response_bytes=` defaults to 4 MiB and may be set from 1 byte through 64 MiB.
191
+ Oversized, truncated, compressed, non-object, duplicate-key or malformed JSON
192
+ responses are rejected. Bodies are read incrementally; compressed responses are
193
+ not decompressed. The standard library also limits HTTP header count and line
194
+ length. Errors omit request payloads, response bodies, credentials, raw headers,
195
+ and underlying transport exceptions. `HTTPError` exposes only numeric `status`
196
+ and sanitized `retry_after` (integer seconds or a normalized HTTP date).
197
+
198
+ There are **no automatic retries**, including for 429, 503 or timeouts. Once the
199
+ gateway reserves quota, even an invalid payload, dependency failure or timeout
200
+ consumes a unit. A client disconnect does not cancel admitted server work.
201
+
202
+ ## Transport origin
203
+
204
+ The default is `https://api.astroapi.io`; calls use `POST /api/astro/...` with
205
+ `Authorization: Bearer`. A custom `base_url` must be a trusted HTTPS origin
206
+ without a path, credentials, query or fragment. Selecting another HTTPS origin
207
+ authorizes sending your key to that origin. TLS certificates and hostnames are
208
+ verified. Proxy environment variables, cookies and redirects are not used.
209
+ Every redirect is returned as `HTTPError`; keys are never forwarded to its
210
+ destination.
211
+
212
+ For local Node gateway testing only:
213
+
214
+ ```python
215
+ client = AstroAPIClient(
216
+ api_key=your_synthetic_key,
217
+ base_url="http://127.0.0.1:5100",
218
+ allow_http_localhost=True,
219
+ )
220
+ ```
221
+
222
+ HTTP requires explicit opt-in and both a loopback/localhost URL and a loopback
223
+ connected peer. Never point the client directly at the internal Python engine.
224
+
225
+ ## Offline checks
226
+
227
+ From this directory, after local installation:
228
+
229
+ ```sh
230
+ python -m unittest discover -s tests -v
231
+ ```
232
+
233
+ Tests use generated synthetic keys and local HTTP/HTTPS servers. Real TLS tests
234
+ generate temporary certificates with local OpenSSL and explicitly skip if it is
235
+ unavailable; no certificate keys are committed. They do not call the
236
+ production API or Stripe. The example under `examples/` makes a billable API
237
+ request when explicitly run with a real key. Its executable-example test replaces
238
+ the transport with a local mock and uses no real account key.
239
+
240
+ ## Additional bounded beta calculations
241
+
242
+ - Composite: strict personA/personB birth objects; shortest-arc planetary and angle midpoints. Equal/Whole Sign houses are synthetic from midpoint Ascendant, not physical/Davison houses. Near-antipodal sources reject; no Placidus, nodes or speeds.
243
+ - Panchang: strict at civil snapshot and optional ayanamsa, always sidereal. Latitude is inclusive [-88,88]. Classifications are at the requested instant; solar events use the USNO -50 arcminute horizon at elevation 0, not Hindu-geocentric sunrise. Civil weekday and sunrise-based vara differ. Unavailable bounded sunrise gives explicit null vara. Historical local offsets can include seconds; paired UTC fields are interoperable.
244
+ - Secondary progressions: strict birth plus target date/time/timezone, target>=birth. Planets only, fixed 365.2421904-day year in uniform TT; natal_context angles/houses are not progressed. Derived progressed_utc may contain actual leap second 60; preserve the string rather than passing it blindly to JavaScript Date/Python datetime. No progressed angles/houses/aspects/nodes/speeds or prediction claim.
245
+ - Varga SVG: birth plus optional ayanamsa, any supported D1/D2/D3/D4/D5/D6/D7/D8/D9/D10/D11/D12/D16/D20/D24/D27/D30/D40/D45/D60/D81/D108/D144/D150 division, layout north_indian/south_indian and theme light/dark/monochrome. Defaults D1/north_indian/dark; /vargas instead defaults D9. Original North fixed-house/South fixed-sign SVG, houses counted from divisional Ascendant; not bhava-chalit. Response.chart is the unchanged Vargas result; SVG is bounded to 128 KiB inside JSON.
246
+ D150 additionally accepts exact `d150_method` values `"deva_keralam_chandra_kala_nadi_non_uniform"`, `"deva_keralam_chandra_kala_nadi_uniform_direct"`, `"parivritti_cyclic"`, `"movable_aries_forward_fixed_taurus_reverse_dual_gemini_forward"`, `"movable_aries_forward_fixed_scorpio_reverse_dual_sagittarius_forward"`, `"movable_aries_forward_fixed_leo_reverse_dual_sagittarius_forward"`, and `"movable_source_sign_forward_fixed_source_sign_reverse_dual_source_sign_forward"`; omission retains the uniform-direct D150 response, and the field is rejected for every other division.
247
+
248
+ - Lunar nodes: `lunar_nodes` takes an `at` civil instant with no coordinates and optional zodiac/ayanamsa. The response provides north/south pairs for Meeus mean nodes and geometric DE421 osculating nodes, each with its explicit source frame. `LunarNodesInput` and `LunarNodesResponse` are exported types. Chart and timeline node fields keep the existing mean-node contract.
249
+ - Returns: `returns` takes birth, exact body `sun` or `moon`, and an `after` civil instant. Optional zodiac/ayanamsa, Equal/Whole Sign houses and `location:{lat,lon}` are supported; location defaults to birth coordinates. `ReturnsInput` and `ReturnsResponse` type the new request/envelope. Read `return_utc`, `chart` and `search`. The first return lies beyond a one-millisecond start exclusion and the full fixed 370-day Sun/32-day Moon TT window must fit the supported UTC range. `after` may precede birth. Preserve leap-second UTC strings; numerical tolerance does not establish physical accuracy.
250
+
251
+ - Solar arcs: `solar_arcs` takes strict birth plus target civil time and optional zodiac/ayanamsa/Equal or Whole Sign houses. Omitted `method` and exact `true_solar_arc` preserve the direct true tropical Sun arc. Exact `method: "mean_naibod"` applies the fixed 0.98564733° mean longitude key per elapsed 365.2421904-day TT model year. Both methods direct natal planets and Ascendant/MC uniformly and retain synthetic houses, one-unit accounting, the 1900–2050 dates, and natal-date ayanamsa policy. No relocation, converse/right-ascension arc, MC-derived angle recalculation, arbitrary key, aspects, nodes or speeds.
252
+ - Vimshottari depth: `depth` accepts integer 2, 3, or 4. Omission/2 and the complete depth-3 response remain unchanged. Depth 4 adds 6,561 Sookshma leaves, `birth_balance.sookshma_dasha_lord`, and exact count metadata. Fourth-level leaves omit the redundant display-only `years` field so the complete response stays below 1 MiB. Python rejects floats, strings and booleans. Depth 4 is synchronous-only under the durable-job 256 KiB cap. The arithmetic is bounded and deterministic, not arbitrary recursion or prediction; an admitted synchronous call costs one standard unit.
253
+ - Eclipse geometry: `eclipse_geometry` searches at most 366 UTC days. Optional `observer` coordinates add a fixed sea-level, no-refraction topocentric snapshot at each rounded global maximum. It does not calculate the location's maximum, contacts, visibility at other instants, paths, maps, terrain/weather, safety guidance or interpretation.
254
+ - Electional search: `electional_search` applies one or more documented AND-only filters at fixed hourly samples for at most seven days/168 samples. Results are sampled candidates without continuous guarantees, ranking, recommendations, void-of-course analysis or interpretation.
255
+ - Natal analysis: `natal_analysis` returns configured aspects, optional midpoint axes, and geometric Grand Trine, T-square, Grand Cross, and Yod matches for selected Sun-through-Pluto points. `additional_points` may explicitly select Vertex/Antivertex, Equatorial Ascendant/Descendant, and Lots of Fortune/Spirit; they stay separate from aspects and interpretation.
256
+ - Planetary hours: `planetary_hours` returns twelve daylight and twelve nighttime unequal temporal hours with Chaldean rulers. It requires a complete sunrise-sunset-next-sunrise sequence and supplies no electional advice.
257
+
258
+ All thirty deployed additive beta routes reserve one standard-call unit, accept only exact documented fields/enums, and retain existing prices, quotas and no automatic retry. Practitioner sign-off and a complete astrology catalogue are not claimed.
259
+
260
+ ### Aspect windows beta
261
+
262
+ Use `client.aspect_windows(...)` for exact-hit-anchored major-aspect orb intervals, repeat numbering, clipped boundaries and optional RFC 5545 text. Inputs span at most 31 UTC days and use a 35-second default deadline.
@@ -0,0 +1,23 @@
1
+ [build-system]
2
+ requires = ["setuptools>=77", "wheel"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "astroapi-io-client"
7
+ version = "0.1.0"
8
+ description = "Synchronous Python client for the AstroAPI.io calculation gateway"
9
+ readme = "README.md"
10
+ license = "ISC"
11
+ license-files = ["LICENSE.txt"]
12
+ requires-python = ">=3.10"
13
+ dependencies = []
14
+ classifiers = [
15
+ "Programming Language :: Python :: 3",
16
+ "Programming Language :: Python :: 3 :: Only",
17
+ ]
18
+
19
+ [tool.setuptools.packages.find]
20
+ where = ["src"]
21
+
22
+ [tool.setuptools.package-data]
23
+ astroapi_client = ["py.typed"]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+