astroapi-io-client 0.2.7__tar.gz → 0.2.9__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 (26) hide show
  1. {astroapi_io_client-0.2.7 → astroapi_io_client-0.2.9}/PKG-INFO +6 -4
  2. {astroapi_io_client-0.2.7 → astroapi_io_client-0.2.9}/README.md +5 -3
  3. {astroapi_io_client-0.2.7 → astroapi_io_client-0.2.9}/pyproject.toml +1 -1
  4. {astroapi_io_client-0.2.7 → astroapi_io_client-0.2.9}/src/astroapi_client/__init__.py +3 -3
  5. {astroapi_io_client-0.2.7 → astroapi_io_client-0.2.9}/src/astroapi_client/_validation.py +44 -0
  6. {astroapi_io_client-0.2.7 → astroapi_io_client-0.2.9}/src/astroapi_client/client.py +11 -3
  7. {astroapi_io_client-0.2.7 → astroapi_io_client-0.2.9}/src/astroapi_client/types.py +35 -0
  8. {astroapi_io_client-0.2.7 → astroapi_io_client-0.2.9}/src/astroapi_io_client.egg-info/PKG-INFO +6 -4
  9. {astroapi_io_client-0.2.7 → astroapi_io_client-0.2.9}/tests/test_client.py +20 -0
  10. {astroapi_io_client-0.2.7 → astroapi_io_client-0.2.9}/LICENSE.txt +0 -0
  11. {astroapi_io_client-0.2.7 → astroapi_io_client-0.2.9}/setup.cfg +0 -0
  12. {astroapi_io_client-0.2.7 → astroapi_io_client-0.2.9}/src/astroapi_client/errors.py +0 -0
  13. {astroapi_io_client-0.2.7 → astroapi_io_client-0.2.9}/src/astroapi_client/py.typed +0 -0
  14. {astroapi_io_client-0.2.7 → astroapi_io_client-0.2.9}/src/astroapi_client/receipts.py +0 -0
  15. {astroapi_io_client-0.2.7 → astroapi_io_client-0.2.9}/src/astroapi_io_client.egg-info/SOURCES.txt +0 -0
  16. {astroapi_io_client-0.2.7 → astroapi_io_client-0.2.9}/src/astroapi_io_client.egg-info/dependency_links.txt +0 -0
  17. {astroapi_io_client-0.2.7 → astroapi_io_client-0.2.9}/src/astroapi_io_client.egg-info/requires.txt +0 -0
  18. {astroapi_io_client-0.2.7 → astroapi_io_client-0.2.9}/src/astroapi_io_client.egg-info/top_level.txt +0 -0
  19. {astroapi_io_client-0.2.7 → astroapi_io_client-0.2.9}/tests/test_aspect_windows_client.py +0 -0
  20. {astroapi_io_client-0.2.7 → astroapi_io_client-0.2.9}/tests/test_calendar_export.py +0 -0
  21. {astroapi_io_client-0.2.7 → astroapi_io_client-0.2.9}/tests/test_davison_input.py +0 -0
  22. {astroapi_io_client-0.2.7 → astroapi_io_client-0.2.9}/tests/test_house_ingresses_client.py +0 -0
  23. {astroapi_io_client-0.2.7 → astroapi_io_client-0.2.9}/tests/test_next_expansions_client.py +0 -0
  24. {astroapi_io_client-0.2.7 → astroapi_io_client-0.2.9}/tests/test_onboarding_context_client.py +0 -0
  25. {astroapi_io_client-0.2.7 → astroapi_io_client-0.2.9}/tests/test_planetary_events_client.py +0 -0
  26. {astroapi_io_client-0.2.7 → astroapi_io_client-0.2.9}/tests/test_varga_input.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: astroapi-io-client
3
- Version: 0.2.7
3
+ Version: 0.2.9
4
4
  Summary: Synchronous Python client for the AstroAPI.io calculation gateway
5
5
  License-Expression: ISC
6
6
  Classifier: Programming Language :: Python :: 3
@@ -14,13 +14,13 @@ Dynamic: license-file
14
14
 
15
15
  # AstroAPI.io Python client
16
16
 
17
- The R199 source client `astroapi-io-client==0.2.7` covers all forty-seven calculation routes: forty-six standard operations plus the separate Pro Nakshatra Timeline. Minor-body calculations are not part of the contract.
17
+ The R201 source client `astroapi-io-client==0.2.9` covers all forty-nine calculation routes: forty-eight standard operations plus the separate Pro Nakshatra Timeline. This package version is the verified R201 client publication; service availability still requires R201 promotion. Minor-body calculations are not part of the contract.
18
18
 
19
19
  A synchronous client for Python 3.10+, with no runtime dependencies.
20
20
  The package import is `astroapi_client`; the distribution name is
21
- `astroapi-io-client`. Version 0.2.7 is the R199 client; package availability never implies that source-only R199 API capabilities are deployed.
21
+ `astroapi-io-client`. Version 0.2.9 is the R201 source client; package availability never implies that source-only R201 API capabilities are deployed.
22
22
 
23
- Install from PyPI with `python -m pip install astroapi-io-client==0.2.7`, or install the current source from a local checkout using `python -m pip install ./clients/python`.
23
+ Install from PyPI with `python -m pip install astroapi-io-client==0.2.9`, or install the current source from a local checkout using `python -m pip install ./clients/python`.
24
24
  Building needs setuptools and wheel; the installed client uses only the standard
25
25
  library. Keep API credentials in trusted server or command-line environments.
26
26
 
@@ -100,6 +100,8 @@ Type hints describe the server contract and do not perform response validation.
100
100
  | `chart_input_readiness` **beta** | `calculateChartInputReadiness` | pinned place, exact/approximate/unknown time quality, explicit profile | route readiness plus resolved, candidate, or sampled evidence for 22 facts |
101
101
  | `house_system_readiness` **beta** | `calculateHouseSystemReadiness` | exact birth and explicit tropical/sidereal profile | fixed 25-system availability matrix with fingerprints and no fallback |
102
102
  | `house_placement_stability` **beta** | `calculateHousePlacementStability` | exact birth and explicit tropical/sidereal profile | exact assignments, unweighted agreement ratios, and deterministic equivalence groups across available 12-house systems |
103
+ | `aspect_policy_stability` **beta** | `calculateAspectPolicyStability` | exact birth, explicit profile, optional 3–10 points, and 2–4 named aspect policies | admission reasons, signed orb margins, set fingerprints, and deterministic equivalence groups |
104
+ | `house_boundary_proximity` **beta** | `calculateHouseBoundaryProximity` | exact birth, explicit profile, optional 1–10 points and 0.1–10° threshold | exact two-sided cusp clearance, normalized position, nearest-boundary ties, and explicit no-fallback geometry failures |
103
105
  | `synastry_svg` **beta** | `calculateSynastrySvg` | two births; optional zodiac/houses/theme/aspects/`include_png` | `result["svg"]`, optional `result["png"]` |
104
106
  | `transit_svg` **beta** | `calculateTransitSvg` | birth and transit date; optional display controls/`include_png` | `result["svg"]`, optional `result["png"]` |
105
107
  | `natal_report` **beta** | `calculateNatalReport` | birth; optional zodiac/houses/theme/SVG/download | `result["html"]`, source-linked `result["facts"]`, optional `result["download"]` |
@@ -1,12 +1,12 @@
1
1
  # AstroAPI.io Python client
2
2
 
3
- The R199 source client `astroapi-io-client==0.2.7` covers all forty-seven calculation routes: forty-six standard operations plus the separate Pro Nakshatra Timeline. Minor-body calculations are not part of the contract.
3
+ The R201 source client `astroapi-io-client==0.2.9` covers all forty-nine calculation routes: forty-eight standard operations plus the separate Pro Nakshatra Timeline. This package version is the verified R201 client publication; service availability still requires R201 promotion. Minor-body calculations are not part of the contract.
4
4
 
5
5
  A synchronous client for Python 3.10+, with no runtime dependencies.
6
6
  The package import is `astroapi_client`; the distribution name is
7
- `astroapi-io-client`. Version 0.2.7 is the R199 client; package availability never implies that source-only R199 API capabilities are deployed.
7
+ `astroapi-io-client`. Version 0.2.9 is the R201 source client; package availability never implies that source-only R201 API capabilities are deployed.
8
8
 
9
- Install from PyPI with `python -m pip install astroapi-io-client==0.2.7`, or install the current source from a local checkout using `python -m pip install ./clients/python`.
9
+ Install from PyPI with `python -m pip install astroapi-io-client==0.2.9`, or install the current source from a local checkout using `python -m pip install ./clients/python`.
10
10
  Building needs setuptools and wheel; the installed client uses only the standard
11
11
  library. Keep API credentials in trusted server or command-line environments.
12
12
 
@@ -86,6 +86,8 @@ Type hints describe the server contract and do not perform response validation.
86
86
  | `chart_input_readiness` **beta** | `calculateChartInputReadiness` | pinned place, exact/approximate/unknown time quality, explicit profile | route readiness plus resolved, candidate, or sampled evidence for 22 facts |
87
87
  | `house_system_readiness` **beta** | `calculateHouseSystemReadiness` | exact birth and explicit tropical/sidereal profile | fixed 25-system availability matrix with fingerprints and no fallback |
88
88
  | `house_placement_stability` **beta** | `calculateHousePlacementStability` | exact birth and explicit tropical/sidereal profile | exact assignments, unweighted agreement ratios, and deterministic equivalence groups across available 12-house systems |
89
+ | `aspect_policy_stability` **beta** | `calculateAspectPolicyStability` | exact birth, explicit profile, optional 3–10 points, and 2–4 named aspect policies | admission reasons, signed orb margins, set fingerprints, and deterministic equivalence groups |
90
+ | `house_boundary_proximity` **beta** | `calculateHouseBoundaryProximity` | exact birth, explicit profile, optional 1–10 points and 0.1–10° threshold | exact two-sided cusp clearance, normalized position, nearest-boundary ties, and explicit no-fallback geometry failures |
89
91
  | `synastry_svg` **beta** | `calculateSynastrySvg` | two births; optional zodiac/houses/theme/aspects/`include_png` | `result["svg"]`, optional `result["png"]` |
90
92
  | `transit_svg` **beta** | `calculateTransitSvg` | birth and transit date; optional display controls/`include_png` | `result["svg"]`, optional `result["png"]` |
91
93
  | `natal_report` **beta** | `calculateNatalReport` | birth; optional zodiac/houses/theme/SVG/download | `result["html"]`, source-linked `result["facts"]`, optional `result["download"]` |
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "astroapi-io-client"
7
- version = "0.2.7"
7
+ version = "0.2.9"
8
8
  description = "Synchronous Python client for the AstroAPI.io calculation gateway"
9
9
  readme = "README.md"
10
10
  license = "ISC"
@@ -17,7 +17,7 @@ from .types import (
17
17
  GeographicLocation, RelocationChartInput, AstrocartographyBody, AstrocartographyInput,
18
18
  EclipseType, EclipseGeometryInput, EclipseObserverSnapshot, EclipseEvent, EclipseGeometryResponse, MoonPhaseSector, ElectionalAspectFilter,
19
19
  ElectionalFilters, ElectionalSearchInput,
20
- EventRule, EventRuleSearchInput, ConventionProfile, ConventionCompareInput, ReleaseCompareInput, ChartRobustnessInput, CivilTimeAuditInput, ChartInputReadinessExactTime, ChartInputReadinessApproximateTime, ChartInputReadinessUnknownTime, ChartInputReadinessBirthTime, ChartInputReadinessInput, HouseSystemReadinessInput, HousePlacementStabilityInput,
20
+ EventRule, EventRuleSearchInput, ConventionProfile, ConventionCompareInput, ReleaseCompareInput, ChartRobustnessInput, CivilTimeAuditInput, ChartInputReadinessExactTime, ChartInputReadinessApproximateTime, ChartInputReadinessUnknownTime, ChartInputReadinessBirthTime, ChartInputReadinessInput, HouseSystemReadinessInput, HousePlacementStabilityInput, AspectPolicyStabilityPoint, AspectPolicyStabilityPolicy, AspectPolicyStabilityInput, HouseBoundaryProximityPoint, HouseBoundaryProximityInput,
21
21
  NatalAnalysisAspectName, NatalAnalysisAspectOption, NatalAnalysisInput,
22
22
  AshtakootaInput, AshtakavargaInput, NumerologyInput, PlanetaryHoursInput,
23
23
  FixedStar, FixedStarsInput,
@@ -30,7 +30,7 @@ from .types import (
30
30
  PlaceSearchInput, TimezoneResolveInput, NatalContextInput,
31
31
  )
32
32
 
33
- __version__ = "0.2.7"
33
+ __version__ = "0.2.9"
34
34
  __all__ = [
35
35
  "NumerologyInput", "verify_calculation_receipt",
36
36
  "AstroAPIClient", "AstroAPIError", "ClientBusyError", "HTTPError", "DavisonInput",
@@ -45,7 +45,7 @@ __all__ = [
45
45
  "GeographicLocation", "RelocationChartInput", "AstrocartographyBody", "AstrocartographyInput",
46
46
  "EclipseType", "EclipseGeometryInput", "EclipseObserverSnapshot", "EclipseEvent", "EclipseGeometryResponse", "MoonPhaseSector", "ElectionalAspectFilter",
47
47
  "ElectionalFilters", "ElectionalSearchInput",
48
- "EventRule", "EventRuleSearchInput", "ConventionProfile", "ConventionCompareInput", "ReleaseCompareInput", "ChartRobustnessInput", "CivilTimeAuditInput", "ChartInputReadinessExactTime", "ChartInputReadinessApproximateTime", "ChartInputReadinessUnknownTime", "ChartInputReadinessBirthTime", "ChartInputReadinessInput", "HouseSystemReadinessInput", "HousePlacementStabilityInput",
48
+ "EventRule", "EventRuleSearchInput", "ConventionProfile", "ConventionCompareInput", "ReleaseCompareInput", "ChartRobustnessInput", "CivilTimeAuditInput", "ChartInputReadinessExactTime", "ChartInputReadinessApproximateTime", "ChartInputReadinessUnknownTime", "ChartInputReadinessBirthTime", "ChartInputReadinessInput", "HouseSystemReadinessInput", "HousePlacementStabilityInput", "AspectPolicyStabilityPoint", "AspectPolicyStabilityPolicy", "AspectPolicyStabilityInput", "HouseBoundaryProximityPoint", "HouseBoundaryProximityInput",
49
49
  "NatalAnalysisAspectName", "NatalAnalysisAspectOption", "NatalAnalysisInput",
50
50
  "AshtakootaInput", "AshtakavargaInput", "PlanetaryHoursInput",
51
51
  "FixedStar", "FixedStarsInput",
@@ -702,6 +702,50 @@ def encode_request(route: str, payload: Any) -> bytes:
702
702
  _invalid("tropical profiles cannot include ayanamsa.")
703
703
  if profile["zodiac"] == "sidereal" and ("ayanamsa" not in profile or profile["ayanamsa"] not in _AYANAMSAS):
704
704
  _invalid("sidereal profiles require a supported ayanamsa.")
705
+ elif route == "aspect-policy-stability":
706
+ _object(data, {"birth", "profile", "points", "policies"}, {"birth", "profile", "policies"})
707
+ birth = _object(data["birth"], {"date", "time", "timezone", "lat", "lon"}, {"date", "time", "timezone", "lat", "lon"})
708
+ _birth(birth)
709
+ profile = _object(data["profile"], {"zodiac", "ayanamsa"}, {"zodiac"})
710
+ _enum(profile["zodiac"], {"tropical", "sidereal"}, "zodiac must be tropical or sidereal.")
711
+ if profile["zodiac"] == "tropical" and "ayanamsa" in profile:
712
+ _invalid("tropical profiles cannot include ayanamsa.")
713
+ if profile["zodiac"] == "sidereal" and ("ayanamsa" not in profile or profile["ayanamsa"] not in _AYANAMSAS):
714
+ _invalid("sidereal profiles require a supported ayanamsa.")
715
+ if "points" in data:
716
+ _unique_enum_list(data["points"], _PLANETARY_EVENT_BODIES, 10, "points")
717
+ if len(data["points"]) < 3:
718
+ _invalid("aspect-policy stability requires at least three points.")
719
+ if not isinstance(data["policies"], list) or not 2 <= len(data["policies"]) <= 4:
720
+ _invalid("policies must contain two through four entries.")
721
+ identifiers = set()
722
+ for policy in data["policies"]:
723
+ policy = _object(policy, {"id", "aspects"}, {"id", "aspects"})
724
+ identifier = policy["id"]
725
+ if (not isinstance(identifier, str)
726
+ or re.fullmatch(r"[a-z][a-z0-9_-]{0,31}", identifier) is None
727
+ or identifier in identifiers):
728
+ _invalid("policy ids must be unique lowercase identifiers of at most 32 characters.")
729
+ identifiers.add(identifier)
730
+ _aspect_configuration(policy)
731
+ elif route == "house-boundary-proximity":
732
+ _object(data, {"birth", "profile", "points", "threshold_degrees"}, {"birth", "profile"})
733
+ birth = _object(data["birth"], {"date", "time", "timezone", "lat", "lon"}, {"date", "time", "timezone", "lat", "lon"})
734
+ _birth(birth)
735
+ profile = _object(data["profile"], {"zodiac", "ayanamsa"}, {"zodiac"})
736
+ _enum(profile["zodiac"], {"tropical", "sidereal"}, "zodiac must be tropical or sidereal.")
737
+ if profile["zodiac"] == "tropical" and "ayanamsa" in profile:
738
+ _invalid("tropical profiles cannot include ayanamsa.")
739
+ if profile["zodiac"] == "sidereal" and ("ayanamsa" not in profile or profile["ayanamsa"] not in _AYANAMSAS):
740
+ _invalid("sidereal profiles require a supported ayanamsa.")
741
+ if "points" in data:
742
+ _unique_enum_list(data["points"], _PLANETARY_EVENT_BODIES, 10, "points")
743
+ if "threshold_degrees" in data:
744
+ threshold = data["threshold_degrees"]
745
+ if (type(threshold) not in {int, float} or not math.isfinite(threshold)
746
+ or not 0.1 <= threshold <= 10.0
747
+ or abs(threshold * 10 - round(threshold * 10)) > 1e-9):
748
+ _invalid("threshold_degrees must be 0.1 through 10.0 in 0.1 degree increments.")
705
749
  elif route in {"house-system-readiness", "house-placement-stability"}:
706
750
  _object(data, {"birth", "profile"}, {"birth", "profile"})
707
751
  birth = _object(data["birth"], {"date", "time", "timezone", "lat", "lon"}, {"date", "time", "timezone", "lat", "lon"})
@@ -32,7 +32,7 @@ from .types import (
32
32
  MoonPhaseInput, MoonPhaseResponse, DavisonInput, PlanetaryEventsInput, PlanetaryEventsResponse,
33
33
  HouseIngressesInput, HouseIngressesResponse,
34
34
  AspectWindowsInput, AspectWindowsResponse,
35
- EventRuleSearchInput, ConventionCompareInput, ReleaseCompareInput, ChartRobustnessInput, CivilTimeAuditInput, ChartInputReadinessInput, HouseSystemReadinessInput, HousePlacementStabilityInput,
35
+ EventRuleSearchInput, ConventionCompareInput, ReleaseCompareInput, ChartRobustnessInput, CivilTimeAuditInput, ChartInputReadinessInput, HouseSystemReadinessInput, HousePlacementStabilityInput, AspectPolicyStabilityInput, HouseBoundaryProximityInput,
36
36
  EventCalendarInput, SynastrySvgInput, TransitSvgInput, NatalReportInput,
37
37
  BirthTimeSensitivityInput, RelocationChartInput, AstrocartographyInput,
38
38
  PlaceSearchInput, TimezoneResolveInput, NatalContextInput,
@@ -181,7 +181,7 @@ def _perform(
181
181
  "Content-Type": "application/json",
182
182
  "Accept": "application/json",
183
183
  "Accept-Encoding": "identity",
184
- "User-Agent": "astroapi-io-client/0.2.7",
184
+ "User-Agent": "astroapi-io-client/0.2.9",
185
185
  "Connection": "close",
186
186
  **({"X-AstroAPI-Receipt": "v1"} if request_receipt else {}),
187
187
  })
@@ -285,7 +285,7 @@ class AstroAPIClient:
285
285
 
286
286
  def _request(self, route: str, payload: object) -> JSONResponse:
287
287
  body = encode_request(route, payload)
288
- timeout = (self._timeline_timeout if route in {"nakshatra-timeline", "birth-time-sensitivity", "chart-robustness", "chart-input-readiness", "house-system-readiness", "house-placement-stability"}
288
+ timeout = (self._timeline_timeout if route in {"nakshatra-timeline", "birth-time-sensitivity", "chart-robustness", "chart-input-readiness", "house-system-readiness", "house-placement-stability", "house-boundary-proximity"}
289
289
  else self._planetary_events_timeout if route in {"planetary-events", "house-ingresses", "eclipse-geometry", "electional-search", "event-rule-search"}
290
290
  else self._aspect_windows_timeout if route == "aspect-windows"
291
291
  else self._timeout)
@@ -444,6 +444,14 @@ class AstroAPIClient:
444
444
  """BETA: quantify unweighted planet-house stability across comparable systems."""
445
445
  return self._request("house-placement-stability", payload)
446
446
 
447
+ def aspect_policy_stability(self, payload: AspectPolicyStabilityInput) -> JSONResponse:
448
+ """BETA: compare several explicit aspect-orb policies on one exact chart."""
449
+ return self._request("aspect-policy-stability", payload)
450
+
451
+ def house_boundary_proximity(self, payload: HouseBoundaryProximityInput) -> JSONResponse:
452
+ """BETA: audit exact two-sided cusp clearance across 12-house systems."""
453
+ return self._request("house-boundary-proximity", payload)
454
+
447
455
  def synastry_svg(self, payload: SynastrySvgInput) -> JSONResponse:
448
456
  """BETA: original deterministic double-wheel relationship SVG."""
449
457
  return self._request("synastry-svg", payload)
@@ -420,6 +420,20 @@ class HouseSystemReadinessInput(TypedDict):
420
420
 
421
421
 
422
422
  HousePlacementStabilityInput = HouseSystemReadinessInput
423
+ HouseBoundaryProximityPoint = Literal[
424
+ "sun", "moon", "mercury", "venus", "mars",
425
+ "jupiter", "saturn", "uranus", "neptune", "pluto",
426
+ ]
427
+
428
+
429
+ class _HouseBoundaryProximityRequired(TypedDict):
430
+ birth: BirthInput
431
+ profile: HouseSystemReadinessProfile
432
+
433
+
434
+ class HouseBoundaryProximityInput(_HouseBoundaryProximityRequired, total=False):
435
+ points: list[HouseBoundaryProximityPoint]
436
+ threshold_degrees: float
423
437
 
424
438
 
425
439
  class ReleaseCompareInput(TypedDict):
@@ -466,6 +480,27 @@ class NatalAnalysisInput(_NatalAnalysisRequired, total=False):
466
480
  ayanamsa: Ayanamsa
467
481
 
468
482
 
483
+ AspectPolicyStabilityPoint = Literal[
484
+ "sun", "moon", "mercury", "venus", "mars",
485
+ "jupiter", "saturn", "uranus", "neptune", "pluto",
486
+ ]
487
+
488
+
489
+ class AspectPolicyStabilityPolicy(TypedDict):
490
+ id: str
491
+ aspects: list[NatalAnalysisAspectOption]
492
+
493
+
494
+ class _AspectPolicyStabilityRequired(TypedDict):
495
+ birth: BirthInput
496
+ profile: HouseSystemReadinessProfile
497
+ policies: list[AspectPolicyStabilityPolicy]
498
+
499
+
500
+ class AspectPolicyStabilityInput(_AspectPolicyStabilityRequired, total=False):
501
+ points: list[AspectPolicyStabilityPoint]
502
+
503
+
469
504
  class PlanetaryHoursInput(TypedDict):
470
505
  date: str
471
506
  timezone: str
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: astroapi-io-client
3
- Version: 0.2.7
3
+ Version: 0.2.9
4
4
  Summary: Synchronous Python client for the AstroAPI.io calculation gateway
5
5
  License-Expression: ISC
6
6
  Classifier: Programming Language :: Python :: 3
@@ -14,13 +14,13 @@ Dynamic: license-file
14
14
 
15
15
  # AstroAPI.io Python client
16
16
 
17
- The R199 source client `astroapi-io-client==0.2.7` covers all forty-seven calculation routes: forty-six standard operations plus the separate Pro Nakshatra Timeline. Minor-body calculations are not part of the contract.
17
+ The R201 source client `astroapi-io-client==0.2.9` covers all forty-nine calculation routes: forty-eight standard operations plus the separate Pro Nakshatra Timeline. This package version is the verified R201 client publication; service availability still requires R201 promotion. Minor-body calculations are not part of the contract.
18
18
 
19
19
  A synchronous client for Python 3.10+, with no runtime dependencies.
20
20
  The package import is `astroapi_client`; the distribution name is
21
- `astroapi-io-client`. Version 0.2.7 is the R199 client; package availability never implies that source-only R199 API capabilities are deployed.
21
+ `astroapi-io-client`. Version 0.2.9 is the R201 source client; package availability never implies that source-only R201 API capabilities are deployed.
22
22
 
23
- Install from PyPI with `python -m pip install astroapi-io-client==0.2.7`, or install the current source from a local checkout using `python -m pip install ./clients/python`.
23
+ Install from PyPI with `python -m pip install astroapi-io-client==0.2.9`, or install the current source from a local checkout using `python -m pip install ./clients/python`.
24
24
  Building needs setuptools and wheel; the installed client uses only the standard
25
25
  library. Keep API credentials in trusted server or command-line environments.
26
26
 
@@ -100,6 +100,8 @@ Type hints describe the server contract and do not perform response validation.
100
100
  | `chart_input_readiness` **beta** | `calculateChartInputReadiness` | pinned place, exact/approximate/unknown time quality, explicit profile | route readiness plus resolved, candidate, or sampled evidence for 22 facts |
101
101
  | `house_system_readiness` **beta** | `calculateHouseSystemReadiness` | exact birth and explicit tropical/sidereal profile | fixed 25-system availability matrix with fingerprints and no fallback |
102
102
  | `house_placement_stability` **beta** | `calculateHousePlacementStability` | exact birth and explicit tropical/sidereal profile | exact assignments, unweighted agreement ratios, and deterministic equivalence groups across available 12-house systems |
103
+ | `aspect_policy_stability` **beta** | `calculateAspectPolicyStability` | exact birth, explicit profile, optional 3–10 points, and 2–4 named aspect policies | admission reasons, signed orb margins, set fingerprints, and deterministic equivalence groups |
104
+ | `house_boundary_proximity` **beta** | `calculateHouseBoundaryProximity` | exact birth, explicit profile, optional 1–10 points and 0.1–10° threshold | exact two-sided cusp clearance, normalized position, nearest-boundary ties, and explicit no-fallback geometry failures |
103
105
  | `synastry_svg` **beta** | `calculateSynastrySvg` | two births; optional zodiac/houses/theme/aspects/`include_png` | `result["svg"]`, optional `result["png"]` |
104
106
  | `transit_svg` **beta** | `calculateTransitSvg` | birth and transit date; optional display controls/`include_png` | `result["svg"]`, optional `result["png"]` |
105
107
  | `natal_report` **beta** | `calculateNatalReport` | birth; optional zodiac/houses/theme/SVG/download | `result["html"]`, source-linked `result["facts"]`, optional `result["download"]` |
@@ -66,6 +66,14 @@ CIVIL_TIME_AUDIT_INPUT = {"place_id": "geonames:5128581:2026-10-02", "date": "20
66
66
  CHART_INPUT_READINESS_INPUT = {"place_id": "geonames:5128581:2026-10-02", "birth_time": {"status": "exact", "date": "2024-11-03", "time": "01:30"}, "profile": {"zodiac": "tropical", "house_system": "equal"}}
67
67
  HOUSE_SYSTEM_READINESS_INPUT = {"birth": BIRTH, "profile": {"zodiac": "tropical"}}
68
68
  HOUSE_PLACEMENT_STABILITY_INPUT = HOUSE_SYSTEM_READINESS_INPUT
69
+ ASPECT_POLICY_STABILITY_INPUT = {
70
+ "birth": BIRTH, "profile": {"zodiac": "tropical"},
71
+ "points": ["sun", "moon", "mercury"],
72
+ "policies": [
73
+ {"id": "tight", "aspects": [{"aspect": "square", "max_orb_degrees": 3.0}]},
74
+ {"id": "wide", "aspects": [{"aspect": "square", "max_orb_degrees": 8.0}]},
75
+ ],
76
+ }
69
77
  CHART_ROBUSTNESS_INPUT = {"birth_time": {"status": "approximate", "date": "2000-01-01", "time": "12:00", "timezone": "UTC", "uncertainty_minutes": 30}, "lat": 0, "lon": 0, "profiles": [{"zodiac": "tropical", "house_system": "equal"}, {"zodiac": "sidereal", "ayanamsa": "lahiri", "house_system": "whole_sign"}]}
70
78
  ROUTES += [
71
79
  ("event_rule_search", "event-rule-search", RULE_INPUT, {"type": "event_rule_search", "status": "beta", "rule_analysis": {}, "matching_samples": [], "sampled_candidate_windows": [], "metadata": {}}),
@@ -75,6 +83,8 @@ ROUTES += [
75
83
  ("chart_input_readiness", "chart-input-readiness", CHART_INPUT_READINESS_INPUT, {"type": "chart_input_readiness", "status": "beta", "birth_time_status": "exact", "profile": {}, "resolution": {}, "exact_chart_ready": False, "next_action": "select_civil_time_candidate", "workflows": [{}, {}, {}, {}], "fact_evidence": [], "summary": {}, "evidence": {}, "metadata": {}}),
76
84
  ("house_system_readiness", "house-system-readiness", HOUSE_SYSTEM_READINESS_INPUT, {"type": "house_system_readiness", "status": "beta", "input": BIRTH, "profile": {}, "systems": [{} for _ in range(25)], "summary": {}, "fallback": {"performed": False, "selected_system": None}, "metadata": {}}),
77
85
  ("house_placement_stability", "house-placement-stability", HOUSE_PLACEMENT_STABILITY_INPUT, {"type": "house_placement_stability", "status": "beta", "input": BIRTH, "profile": {}, "systems": [{} for _ in range(25)], "planets": [{} for _ in range(10)], "placement_groups": [], "summary": {}, "fallback": {"performed": False, "selected_system": None}, "metadata": {}}),
86
+ ("aspect_policy_stability", "aspect-policy-stability", ASPECT_POLICY_STABILITY_INPUT, {"type": "aspect_policy_stability", "status": "beta", "input": {}, "positions": {}, "evaluations": [], "policy_results": [{}, {}], "policy_groups": [{}], "summary": {}, "metadata": {}}),
87
+ ("house_boundary_proximity", "house-boundary-proximity", {"birth": BIRTH, "profile": {"zodiac": "tropical"}, "points": ["sun", "moon"], "threshold_degrees": 2.0}, {"type": "house_boundary_proximity", "status": "beta", "input": {}, "systems": [{} for _ in range(25)], "point_summaries": [{}, {}], "summary": {}, "fallback": {"performed": False, "selected_system": None}, "metadata": {}}),
78
88
  ]
79
89
 
80
90
  @contextmanager
@@ -161,6 +171,14 @@ CIVIL_TIME_AUDIT_INPUT = {"place_id": "geonames:5128581:2026-10-02", "date": "20
161
171
  CHART_INPUT_READINESS_INPUT = {"place_id": "geonames:5128581:2026-10-02", "birth_time": {"status": "exact", "date": "2024-11-03", "time": "01:30"}, "profile": {"zodiac": "tropical", "house_system": "equal"}}
162
172
  HOUSE_SYSTEM_READINESS_INPUT = {"birth": BIRTH, "profile": {"zodiac": "tropical"}}
163
173
  HOUSE_PLACEMENT_STABILITY_INPUT = HOUSE_SYSTEM_READINESS_INPUT
174
+ ASPECT_POLICY_STABILITY_INPUT = {
175
+ "birth": BIRTH, "profile": {"zodiac": "tropical"},
176
+ "points": ["sun", "moon", "mercury"],
177
+ "policies": [
178
+ {"id": "tight", "aspects": [{"aspect": "square", "max_orb_degrees": 3.0}]},
179
+ {"id": "wide", "aspects": [{"aspect": "square", "max_orb_degrees": 8.0}]},
180
+ ],
181
+ }
164
182
  CHART_ROBUSTNESS_INPUT = {"birth_time": {"status": "approximate", "date": "2000-01-01", "time": "12:00", "timezone": "UTC", "uncertainty_minutes": 30}, "lat": 0, "lon": 0, "profiles": [{"zodiac": "tropical", "house_system": "equal"}, {"zodiac": "sidereal", "ayanamsa": "lahiri", "house_system": "whole_sign"}]}
165
183
  ROUTES += [
166
184
  ("event_rule_search", "event-rule-search", RULE_INPUT, {"type": "event_rule_search", "status": "beta", "rule_analysis": {}, "matching_samples": [], "sampled_candidate_windows": [], "metadata": {}}),
@@ -170,6 +188,8 @@ ROUTES += [
170
188
  ("chart_input_readiness", "chart-input-readiness", CHART_INPUT_READINESS_INPUT, {"type": "chart_input_readiness", "status": "beta", "birth_time_status": "exact", "profile": {}, "resolution": {}, "exact_chart_ready": False, "next_action": "select_civil_time_candidate", "workflows": [{}, {}, {}, {}], "fact_evidence": [], "summary": {}, "evidence": {}, "metadata": {}}),
171
189
  ("house_system_readiness", "house-system-readiness", HOUSE_SYSTEM_READINESS_INPUT, {"type": "house_system_readiness", "status": "beta", "input": BIRTH, "profile": {}, "systems": [{} for _ in range(25)], "summary": {}, "fallback": {"performed": False, "selected_system": None}, "metadata": {}}),
172
190
  ("house_placement_stability", "house-placement-stability", HOUSE_PLACEMENT_STABILITY_INPUT, {"type": "house_placement_stability", "status": "beta", "input": BIRTH, "profile": {}, "systems": [{} for _ in range(25)], "planets": [{} for _ in range(10)], "placement_groups": [], "summary": {}, "fallback": {"performed": False, "selected_system": None}, "metadata": {}}),
191
+ ("aspect_policy_stability", "aspect-policy-stability", ASPECT_POLICY_STABILITY_INPUT, {"type": "aspect_policy_stability", "status": "beta", "input": {}, "positions": {}, "evaluations": [], "policy_results": [{}, {}], "policy_groups": [{}], "summary": {}, "metadata": {}}),
192
+ ("house_boundary_proximity", "house-boundary-proximity", {"birth": BIRTH, "profile": {"zodiac": "tropical"}, "points": ["sun", "moon"], "threshold_degrees": 2.0}, {"type": "house_boundary_proximity", "status": "beta", "input": {}, "systems": [{} for _ in range(25)], "point_summaries": [{}, {}], "summary": {}, "fallback": {"performed": False, "selected_system": None}, "metadata": {}}),
173
193
  ]
174
194
 
175
195
  @contextmanager