divineapi 1.2.0__tar.gz → 1.4.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 (35) hide show
  1. {divineapi-1.2.0 → divineapi-1.4.0}/PKG-INFO +26 -12
  2. {divineapi-1.2.0 → divineapi-1.4.0}/README.md +25 -11
  3. {divineapi-1.2.0 → divineapi-1.4.0}/divineapi/__init__.py +6 -4
  4. {divineapi-1.2.0 → divineapi-1.4.0}/divineapi/calculators.py +2 -2
  5. {divineapi-1.2.0 → divineapi-1.4.0}/divineapi/horoscope.py +27 -11
  6. {divineapi-1.2.0 → divineapi-1.4.0}/divineapi/indian/kundli.py +33 -0
  7. {divineapi-1.2.0 → divineapi-1.4.0}/divineapi/pdf.py +23 -16
  8. divineapi-1.4.0/divineapi/western/_house_system.py +52 -0
  9. {divineapi-1.2.0 → divineapi-1.4.0}/divineapi/western/composite.py +2 -1
  10. {divineapi-1.2.0 → divineapi-1.4.0}/divineapi/western/natal.py +5 -4
  11. {divineapi-1.2.0 → divineapi-1.4.0}/divineapi/western/planet_returns.py +5 -3
  12. {divineapi-1.2.0 → divineapi-1.4.0}/divineapi/western/prenatal.py +2 -1
  13. {divineapi-1.2.0 → divineapi-1.4.0}/divineapi/western/progressions.py +16 -5
  14. {divineapi-1.2.0 → divineapi-1.4.0}/divineapi/western/synastry.py +2 -1
  15. divineapi-1.4.0/divineapi/western/transit.py +218 -0
  16. {divineapi-1.2.0 → divineapi-1.4.0}/divineapi.egg-info/PKG-INFO +26 -12
  17. {divineapi-1.2.0 → divineapi-1.4.0}/divineapi.egg-info/SOURCES.txt +1 -0
  18. {divineapi-1.2.0 → divineapi-1.4.0}/pyproject.toml +1 -1
  19. divineapi-1.2.0/divineapi/western/transit.py +0 -110
  20. {divineapi-1.2.0 → divineapi-1.4.0}/LICENSE +0 -0
  21. {divineapi-1.2.0 → divineapi-1.4.0}/divineapi/client.py +0 -0
  22. {divineapi-1.2.0 → divineapi-1.4.0}/divineapi/exceptions.py +0 -0
  23. {divineapi-1.2.0 → divineapi-1.4.0}/divineapi/indian/__init__.py +0 -0
  24. {divineapi-1.2.0 → divineapi-1.4.0}/divineapi/indian/festival.py +0 -0
  25. {divineapi-1.2.0 → divineapi-1.4.0}/divineapi/indian/match_making.py +0 -0
  26. {divineapi-1.2.0 → divineapi-1.4.0}/divineapi/indian/panchang.py +0 -0
  27. {divineapi-1.2.0 → divineapi-1.4.0}/divineapi/lifestyle.py +0 -0
  28. {divineapi-1.2.0 → divineapi-1.4.0}/divineapi/numerology.py +0 -0
  29. {divineapi-1.2.0 → divineapi-1.4.0}/divineapi/py.typed +0 -0
  30. {divineapi-1.2.0 → divineapi-1.4.0}/divineapi/western/__init__.py +0 -0
  31. {divineapi-1.2.0 → divineapi-1.4.0}/divineapi.egg-info/dependency_links.txt +0 -0
  32. {divineapi-1.2.0 → divineapi-1.4.0}/divineapi.egg-info/requires.txt +0 -0
  33. {divineapi-1.2.0 → divineapi-1.4.0}/divineapi.egg-info/top_level.txt +0 -0
  34. {divineapi-1.2.0 → divineapi-1.4.0}/setup.cfg +0 -0
  35. {divineapi-1.2.0 → divineapi-1.4.0}/setup.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: divineapi
3
- Version: 1.2.0
3
+ Version: 1.4.0
4
4
  Summary: Official Python SDK for the Divine API - Astrology, Numerology, Tarot & more
5
5
  Author-email: Divine API <support@divineapi.com>
6
6
  License: MIT
@@ -51,9 +51,9 @@ from divineapi import DivineApi
51
51
 
52
52
  client = DivineApi(api_key="your-api-key", auth_token="your-bearer-token")
53
53
 
54
- # Daily Horoscope
54
+ # Daily Horoscope (h_day = today, tomorrow, or yesterday)
55
55
  result = client.horoscope.daily(
56
- sign="aries", day=10, month=3, year=2024, tzone=5.5
56
+ sign="aries", h_day="today", tzone=5.5
57
57
  )
58
58
  print(result)
59
59
  ```
@@ -64,10 +64,11 @@ print(result)
64
64
 
65
65
  ```python
66
66
  # Daily / Weekly / Monthly / Yearly Horoscope
67
- client.horoscope.daily(sign="aries", day=10, month=3, year=2024, tzone=5.5)
68
- client.horoscope.weekly(sign="leo", week="thisweek", tzone=5.5)
69
- client.horoscope.monthly(sign="cancer", month=3, tzone=5.5)
70
- client.horoscope.yearly(sign="virgo", year=2024, tzone=5.5)
67
+ # daily h_day = today / tomorrow / yesterday; weekly/monthly/yearly = current / prev / next
68
+ client.horoscope.daily(sign="aries", h_day="today", tzone=5.5)
69
+ client.horoscope.weekly(sign="leo", week="current", tzone=5.5)
70
+ client.horoscope.monthly(sign="cancer", month="current", tzone=5.5)
71
+ client.horoscope.yearly(sign="virgo", year="current", tzone=5.5)
71
72
 
72
73
  # Chinese & Numerology Horoscope
73
74
  client.horoscope.chinese(sign="rat", h_day="today", tzone=5.5)
@@ -140,6 +141,14 @@ client.indian.kundli.lal_kitab_varshphal_chart(2026, **birth)
140
141
  # Lal Kitab dasha content (no birth data needed)
141
142
  client.indian.kundli.lal_kitab_mahadasha_content(maha_dasha="saturn")
142
143
  client.indian.kundli.lal_kitab_antardasha_content(maha_dasha="saturn", antar_dasha="mercury")
144
+
145
+ # Additional kundli analysis
146
+ client.indian.kundli.vargottama_planets(**birth)
147
+ client.indian.kundli.bhav_bala(**birth)
148
+ client.indian.kundli.shani_ashtam_shani(**birth)
149
+ client.indian.kundli.bhava_analysis(**birth)
150
+ client.indian.kundli.bhava_group_predictions(**birth)
151
+ client.indian.kundli.planet_remedies("sun", **birth) # analysis_planet + birth
143
152
  ```
144
153
 
145
154
  ### Indian Astrology - Match Making
@@ -164,8 +173,9 @@ client.indian.festival.chaitra_festivals(
164
173
  client.indian.festival.english_calendar(
165
174
  year=2024, month=3, place="Delhi", lat=28.6, lon=77.2, tzone=5.5
166
175
  )
176
+ # festival is a lowercase snake_case slug from the Divine festival list
167
177
  client.indian.festival.find_festival(
168
- festival="diwali", year=2024,
178
+ festival="maha_shivratri", year=2024,
169
179
  place="Delhi", lat=28.6, lon=77.2, tzone=5.5
170
180
  )
171
181
  client.indian.festival.malayalam_festivals(
@@ -193,7 +203,7 @@ client.western.natal.house_cusps(**birth_w)
193
203
  client.western.natal.aspect_table(**birth_w)
194
204
  client.western.natal.natal_wheel_chart(**birth_w)
195
205
  client.western.natal.natal_insights(**birth_w)
196
- client.western.natal.dominants(method="traditional", **birth_w)
206
+ client.western.natal.dominants(method="TRADITIONAL", **birth_w)
197
207
  ```
198
208
 
199
209
  ### Western Astrology - Synastry
@@ -252,7 +262,7 @@ client.lifestyle.astro_chic_picks(sign="virgo", h_day="today", tzone=5.5)
252
262
  ### Calculators
253
263
 
254
264
  ```python
255
- client.calculators.flames(full_name="John", partner_name="Jane")
265
+ client.calculators.flames(your_name="John", partner_name="Jane")
256
266
  client.calculators.love_calculator(
257
267
  your_name="John", partner_name="Jane",
258
268
  your_gender="male", partner_gender="female",
@@ -262,11 +272,15 @@ client.calculators.love_calculator(
262
272
  ### PDF Reports
263
273
 
264
274
  ```python
275
+ # The six branding fields (company_url, logo_url, footer_text, company_name,
276
+ # company_email, company_bio) are REQUIRED by the PDF backend.
265
277
  client.pdf.kundali_sampoorna(
266
278
  full_name="John", day=1, month=1, year=1990,
267
279
  hour=10, min=30, sec=0, gender="male",
268
280
  place="Mumbai", lat=19.07, lon=72.87, tzone=5.5,
269
281
  company_name="AstroCo", company_url="https://example.com",
282
+ company_email="hello@example.com", company_bio="We do astrology.",
283
+ logo_url="https://example.com/logo.png", footer_text="(c) AstroCo",
270
284
  )
271
285
  ```
272
286
 
@@ -278,7 +292,7 @@ from divineapi import DivineApi, AuthenticationError, ValidationError, RateLimit
278
292
  client = DivineApi(api_key="...", auth_token="...")
279
293
 
280
294
  try:
281
- result = client.horoscope.daily(sign="aries", day=10, month=3, year=2024, tzone=5.5)
295
+ result = client.horoscope.daily(sign="aries", h_day="today", tzone=5.5)
282
296
  except AuthenticationError as e:
283
297
  print(f"Auth failed: {e}")
284
298
  except ValidationError as e:
@@ -291,7 +305,7 @@ except RateLimitError as e:
291
305
 
292
306
  ```python
293
307
  with DivineApi(api_key="...", auth_token="...") as client:
294
- result = client.horoscope.daily(sign="aries", day=10, month=3, year=2024, tzone=5.5)
308
+ result = client.horoscope.daily(sign="aries", h_day="today", tzone=5.5)
295
309
  ```
296
310
 
297
311
  ## Configuration
@@ -23,9 +23,9 @@ from divineapi import DivineApi
23
23
 
24
24
  client = DivineApi(api_key="your-api-key", auth_token="your-bearer-token")
25
25
 
26
- # Daily Horoscope
26
+ # Daily Horoscope (h_day = today, tomorrow, or yesterday)
27
27
  result = client.horoscope.daily(
28
- sign="aries", day=10, month=3, year=2024, tzone=5.5
28
+ sign="aries", h_day="today", tzone=5.5
29
29
  )
30
30
  print(result)
31
31
  ```
@@ -36,10 +36,11 @@ print(result)
36
36
 
37
37
  ```python
38
38
  # Daily / Weekly / Monthly / Yearly Horoscope
39
- client.horoscope.daily(sign="aries", day=10, month=3, year=2024, tzone=5.5)
40
- client.horoscope.weekly(sign="leo", week="thisweek", tzone=5.5)
41
- client.horoscope.monthly(sign="cancer", month=3, tzone=5.5)
42
- client.horoscope.yearly(sign="virgo", year=2024, tzone=5.5)
39
+ # daily h_day = today / tomorrow / yesterday; weekly/monthly/yearly = current / prev / next
40
+ client.horoscope.daily(sign="aries", h_day="today", tzone=5.5)
41
+ client.horoscope.weekly(sign="leo", week="current", tzone=5.5)
42
+ client.horoscope.monthly(sign="cancer", month="current", tzone=5.5)
43
+ client.horoscope.yearly(sign="virgo", year="current", tzone=5.5)
43
44
 
44
45
  # Chinese & Numerology Horoscope
45
46
  client.horoscope.chinese(sign="rat", h_day="today", tzone=5.5)
@@ -112,6 +113,14 @@ client.indian.kundli.lal_kitab_varshphal_chart(2026, **birth)
112
113
  # Lal Kitab dasha content (no birth data needed)
113
114
  client.indian.kundli.lal_kitab_mahadasha_content(maha_dasha="saturn")
114
115
  client.indian.kundli.lal_kitab_antardasha_content(maha_dasha="saturn", antar_dasha="mercury")
116
+
117
+ # Additional kundli analysis
118
+ client.indian.kundli.vargottama_planets(**birth)
119
+ client.indian.kundli.bhav_bala(**birth)
120
+ client.indian.kundli.shani_ashtam_shani(**birth)
121
+ client.indian.kundli.bhava_analysis(**birth)
122
+ client.indian.kundli.bhava_group_predictions(**birth)
123
+ client.indian.kundli.planet_remedies("sun", **birth) # analysis_planet + birth
115
124
  ```
116
125
 
117
126
  ### Indian Astrology - Match Making
@@ -136,8 +145,9 @@ client.indian.festival.chaitra_festivals(
136
145
  client.indian.festival.english_calendar(
137
146
  year=2024, month=3, place="Delhi", lat=28.6, lon=77.2, tzone=5.5
138
147
  )
148
+ # festival is a lowercase snake_case slug from the Divine festival list
139
149
  client.indian.festival.find_festival(
140
- festival="diwali", year=2024,
150
+ festival="maha_shivratri", year=2024,
141
151
  place="Delhi", lat=28.6, lon=77.2, tzone=5.5
142
152
  )
143
153
  client.indian.festival.malayalam_festivals(
@@ -165,7 +175,7 @@ client.western.natal.house_cusps(**birth_w)
165
175
  client.western.natal.aspect_table(**birth_w)
166
176
  client.western.natal.natal_wheel_chart(**birth_w)
167
177
  client.western.natal.natal_insights(**birth_w)
168
- client.western.natal.dominants(method="traditional", **birth_w)
178
+ client.western.natal.dominants(method="TRADITIONAL", **birth_w)
169
179
  ```
170
180
 
171
181
  ### Western Astrology - Synastry
@@ -224,7 +234,7 @@ client.lifestyle.astro_chic_picks(sign="virgo", h_day="today", tzone=5.5)
224
234
  ### Calculators
225
235
 
226
236
  ```python
227
- client.calculators.flames(full_name="John", partner_name="Jane")
237
+ client.calculators.flames(your_name="John", partner_name="Jane")
228
238
  client.calculators.love_calculator(
229
239
  your_name="John", partner_name="Jane",
230
240
  your_gender="male", partner_gender="female",
@@ -234,11 +244,15 @@ client.calculators.love_calculator(
234
244
  ### PDF Reports
235
245
 
236
246
  ```python
247
+ # The six branding fields (company_url, logo_url, footer_text, company_name,
248
+ # company_email, company_bio) are REQUIRED by the PDF backend.
237
249
  client.pdf.kundali_sampoorna(
238
250
  full_name="John", day=1, month=1, year=1990,
239
251
  hour=10, min=30, sec=0, gender="male",
240
252
  place="Mumbai", lat=19.07, lon=72.87, tzone=5.5,
241
253
  company_name="AstroCo", company_url="https://example.com",
254
+ company_email="hello@example.com", company_bio="We do astrology.",
255
+ logo_url="https://example.com/logo.png", footer_text="(c) AstroCo",
242
256
  )
243
257
  ```
244
258
 
@@ -250,7 +264,7 @@ from divineapi import DivineApi, AuthenticationError, ValidationError, RateLimit
250
264
  client = DivineApi(api_key="...", auth_token="...")
251
265
 
252
266
  try:
253
- result = client.horoscope.daily(sign="aries", day=10, month=3, year=2024, tzone=5.5)
267
+ result = client.horoscope.daily(sign="aries", h_day="today", tzone=5.5)
254
268
  except AuthenticationError as e:
255
269
  print(f"Auth failed: {e}")
256
270
  except ValidationError as e:
@@ -263,7 +277,7 @@ except RateLimitError as e:
263
277
 
264
278
  ```python
265
279
  with DivineApi(api_key="...", auth_token="...") as client:
266
- result = client.horoscope.daily(sign="aries", day=10, month=3, year=2024, tzone=5.5)
280
+ result = client.horoscope.daily(sign="aries", h_day="today", tzone=5.5)
267
281
  ```
268
282
 
269
283
  ## Configuration
@@ -5,7 +5,7 @@ Usage::
5
5
  from divineapi import DivineApi
6
6
 
7
7
  client = DivineApi(api_key="your-api-key", auth_token="your-bearer-token")
8
- result = client.horoscope.daily(sign="aries", day=10, month=3, year=2024, tzone=5.5)
8
+ result = client.horoscope.daily(sign="aries", h_day="today", tzone=5.5)
9
9
  print(result)
10
10
  """
11
11
 
@@ -29,7 +29,7 @@ from .numerology import NumerologyApi
29
29
  from .pdf import PdfReportApi
30
30
  from .western import WesternApi
31
31
 
32
- __version__ = "1.2.0"
32
+ __version__ = "1.4.0"
33
33
 
34
34
  __all__ = [
35
35
  "DivineApi",
@@ -57,7 +57,7 @@ class DivineApi:
57
57
  client = DivineApi(api_key="...", auth_token="...")
58
58
 
59
59
  # Horoscope & Tarot
60
- client.horoscope.daily(sign="aries", day=10, month=3, year=2024, tzone=5.5)
60
+ client.horoscope.daily(sign="aries", h_day="today", tzone=5.5)
61
61
 
62
62
  # Indian Panchang
63
63
  client.indian.panchang.find_panchang(
@@ -100,7 +100,9 @@ class DivineApi:
100
100
  full_name="John", day=1, month=1, year=1990,
101
101
  hour=10, min=30, sec=0, gender="male",
102
102
  place="Mumbai", lat=19.07, lon=72.87, tzone=5.5,
103
- company_name="MyCompany",
103
+ company_name="MyCompany", company_url="https://example.com",
104
+ company_email="hello@example.com", company_bio="We do astrology.",
105
+ logo_url="https://example.com/logo.png", footer_text="(c) MyCompany",
104
106
  )
105
107
  """
106
108
 
@@ -13,10 +13,10 @@ class CalculatorApi:
13
13
  def __init__(self, client: BaseClient) -> None:
14
14
  self._c = client
15
15
 
16
- def flames(self, full_name: str, partner_name: str) -> Dict[str, Any]:
16
+ def flames(self, your_name: str, partner_name: str) -> Dict[str, Any]:
17
17
  """FLAMES Calculator."""
18
18
  return self._c.post(HOST, "/calculator/v1/flames-calculator", {
19
- "full_name": full_name, "partner_name": partner_name,
19
+ "your_name": your_name, "partner_name": partner_name,
20
20
  })
21
21
 
22
22
  def love_calculator(
@@ -20,16 +20,21 @@ class HoroscopeApi:
20
20
  def daily(
21
21
  self,
22
22
  sign: str,
23
- day: int,
24
- month: int,
25
- year: int,
23
+ h_day: str,
26
24
  tzone: float,
27
25
  lan: str = "en",
26
+ day: Optional[int] = None,
27
+ month: Optional[int] = None,
28
+ year: Optional[int] = None,
28
29
  ) -> Dict[str, Any]:
29
- """Daily Horoscope."""
30
+ """Daily Horoscope.
31
+
32
+ The reading is selected with ``h_day`` ('today', 'tomorrow', or
33
+ 'yesterday'). ``day``/``month``/``year`` are deprecated: the API
34
+ ignores them and they are accepted only for backward compatibility.
35
+ """
30
36
  return self._c.post(HOST, "/api/v5/daily-horoscope", {
31
- "sign": sign, "day": day, "month": month, "year": year,
32
- "tzone": tzone, "lan": lan,
37
+ "sign": sign, "h_day": h_day, "tzone": tzone, "lan": lan,
33
38
  })
34
39
 
35
40
  def weekly(
@@ -39,7 +44,10 @@ class HoroscopeApi:
39
44
  tzone: float,
40
45
  lan: str = "en",
41
46
  ) -> Dict[str, Any]:
42
- """Weekly Horoscope."""
47
+ """Weekly Horoscope.
48
+
49
+ ``week`` is a selector: 'current', 'prev', or 'next' (not a date).
50
+ """
43
51
  return self._c.post(HOST, "/api/v5/weekly-horoscope", {
44
52
  "sign": sign, "week": week, "tzone": tzone, "lan": lan,
45
53
  })
@@ -47,11 +55,15 @@ class HoroscopeApi:
47
55
  def monthly(
48
56
  self,
49
57
  sign: str,
50
- month: int,
58
+ month: str,
51
59
  tzone: float,
52
60
  lan: str = "en",
53
61
  ) -> Dict[str, Any]:
54
- """Monthly Horoscope."""
62
+ """Monthly Horoscope.
63
+
64
+ ``month`` is a selector: 'current', 'prev', or 'next' (not a calendar
65
+ month number).
66
+ """
55
67
  return self._c.post(HOST, "/api/v5/monthly-horoscope", {
56
68
  "sign": sign, "month": month, "tzone": tzone, "lan": lan,
57
69
  })
@@ -59,11 +71,15 @@ class HoroscopeApi:
59
71
  def yearly(
60
72
  self,
61
73
  sign: str,
62
- year: int,
74
+ year: str,
63
75
  tzone: float,
64
76
  lan: str = "en",
65
77
  ) -> Dict[str, Any]:
66
- """Yearly Horoscope."""
78
+ """Yearly Horoscope.
79
+
80
+ ``year`` is a selector: 'current', 'prev', or 'next' (not a calendar
81
+ year number).
82
+ """
67
83
  return self._c.post(HOST, "/api/v5/yearly-horoscope", {
68
84
  "sign": sign, "year": year, "tzone": tzone, "lan": lan,
69
85
  })
@@ -323,3 +323,36 @@ class KundliApi:
323
323
  """Lal Kitab Varshphal Chart for the given year."""
324
324
  kw["varshphal_year"] = varshphal_year
325
325
  return self._post_birth("/indian-api/v1/lal-kitab/varshphal/chart", **kw)
326
+
327
+ # ------------------------------------------------------------------ #
328
+ # Additional Kundli Analysis (6 endpoints)
329
+ # ------------------------------------------------------------------ #
330
+
331
+ def vargottama_planets(self, **kw: Any) -> Dict[str, Any]:
332
+ """Vargottama Planets."""
333
+ return self._post_birth("/indian-api/v1/vargottama-planets", **kw)
334
+
335
+ def bhav_bala(self, **kw: Any) -> Dict[str, Any]:
336
+ """Bhav Bala."""
337
+ return self._post_birth("/indian-api/v1/bhav-bala", **kw)
338
+
339
+ def shani_ashtam_shani(self, **kw: Any) -> Dict[str, Any]:
340
+ """Shani Ashtam Shani."""
341
+ return self._post_birth("/indian-api/v1/shani-ashtam-shani", **kw)
342
+
343
+ def bhava_analysis(self, **kw: Any) -> Dict[str, Any]:
344
+ """Bhava Analysis."""
345
+ return self._post_birth("/indian-api/v1/bhava-analysis", **kw)
346
+
347
+ def bhava_group_predictions(self, **kw: Any) -> Dict[str, Any]:
348
+ """Bhava Group Predictions."""
349
+ return self._post_birth("/indian-api/v1/bhava-group-predictions", **kw)
350
+
351
+ def planet_remedies(self, analysis_planet: str, **kw: Any) -> Dict[str, Any]:
352
+ """Planet Remedies for a given planet.
353
+
354
+ analysis_planet: sun, moon, mars, mercury, jupiter, venus, saturn,
355
+ rahu or ketu.
356
+ """
357
+ kw["analysis_planet"] = analysis_planet
358
+ return self._post_birth("/indian-api/v1/planet-remedies", **kw)
@@ -22,23 +22,26 @@ class PdfReportApi:
22
22
  full_name: str, day: int, month: int, year: int,
23
23
  hour: int, min: int, sec: int, gender: str,
24
24
  place: str, lat: float, lon: float, tzone: float,
25
- lan: str = "en",
26
- company_name: str = "", company_url: str = "",
27
- company_email: str = "", company_mobile: str = "",
28
- company_bio: str = "", logo_url: str = "",
29
- footer_text: str = "",
25
+ company_name: str, company_url: str, company_email: str,
26
+ company_bio: str, logo_url: str, footer_text: str,
27
+ lan: str = "en", company_mobile: str = "",
30
28
  **extra: Any,
31
29
  ) -> Dict[str, Any]:
30
+ # The six branding fields are REQUIRED by the PDF backend (it rejects
31
+ # requests without them). company_mobile is optional and sent only when
32
+ # provided.
32
33
  d: Dict[str, Any] = {
33
34
  "full_name": full_name, "day": day, "month": month, "year": year,
34
35
  "hour": hour, "min": min, "sec": sec, "gender": gender,
35
36
  "place": place, "lat": lat, "lon": lon, "tzone": tzone,
36
37
  "lan": lan,
37
38
  "company_name": company_name, "company_url": company_url,
38
- "company_email": company_email, "company_mobile": company_mobile,
39
+ "company_email": company_email,
39
40
  "company_bio": company_bio, "logo_url": logo_url,
40
41
  "footer_text": footer_text,
41
42
  }
43
+ if company_mobile:
44
+ d["company_mobile"] = company_mobile
42
45
  d.update(extra)
43
46
  return d
44
47
 
@@ -50,13 +53,12 @@ class PdfReportApi:
50
53
  p2_full_name: str, p2_day: int, p2_month: int, p2_year: int,
51
54
  p2_hour: int, p2_min: int, p2_sec: int, p2_gender: str,
52
55
  p2_place: str, p2_lat: float, p2_lon: float, p2_tzone: float,
53
- lan: str = "en",
54
- company_name: str = "", company_url: str = "",
55
- company_email: str = "", company_mobile: str = "",
56
- company_bio: str = "", logo_url: str = "",
57
- footer_text: str = "",
56
+ company_name: str, company_url: str, company_email: str,
57
+ company_bio: str, logo_url: str, footer_text: str,
58
+ lan: str = "en", company_mobile: str = "",
58
59
  **extra: Any,
59
60
  ) -> Dict[str, Any]:
61
+ # The six branding fields are REQUIRED by the PDF backend.
60
62
  d: Dict[str, Any] = {
61
63
  "p1_full_name": p1_full_name, "p1_day": p1_day, "p1_month": p1_month,
62
64
  "p1_year": p1_year, "p1_hour": p1_hour, "p1_min": p1_min,
@@ -68,10 +70,12 @@ class PdfReportApi:
68
70
  "p2_lat": p2_lat, "p2_lon": p2_lon, "p2_tzone": p2_tzone,
69
71
  "lan": lan,
70
72
  "company_name": company_name, "company_url": company_url,
71
- "company_email": company_email, "company_mobile": company_mobile,
73
+ "company_email": company_email,
72
74
  "company_bio": company_bio, "logo_url": logo_url,
73
75
  "footer_text": footer_text,
74
76
  }
77
+ if company_mobile:
78
+ d["company_mobile"] = company_mobile
75
79
  d.update(extra)
76
80
  return d
77
81
 
@@ -133,11 +137,14 @@ class PdfReportApi:
133
137
  # Astrology / Numerology reports with report_code
134
138
  # ------------------------------------------------------------------ #
135
139
 
136
- def natal_report(self, report_code: str, theme: str = "", **kw: Any) -> Dict[str, Any]:
137
- """Natal Report PDF."""
140
+ def natal_report(self, report_code: str, theme: str, **kw: Any) -> Dict[str, Any]:
141
+ """Natal Report PDF.
142
+
143
+ Both ``report_code`` (e.g. 'CAREER-REPORT') and ``theme`` (e.g. '001')
144
+ are required by the API, along with the six company branding fields.
145
+ """
138
146
  kw["report_code"] = report_code
139
- if theme:
140
- kw["theme"] = theme
147
+ kw["theme"] = theme
141
148
  return self._post_birth("/astrology/v2/report", **kw)
142
149
 
143
150
  def natal_couple_report(self, report_code: str, **kw: Any) -> Dict[str, Any]:
@@ -0,0 +1,52 @@
1
+ """House-system name resolution for the Western API.
2
+
3
+ The live Western API accepts ONLY single-letter Swiss Ephemeris house-system
4
+ codes. Word values like ``placidus`` are rejected on astroapi-4 and silently
5
+ ignored on astroapi-8, so every payload must send the letter code. This mirrors
6
+ the mapping used by the Divine Western MCP server.
7
+ """
8
+
9
+ from typing import Optional
10
+
11
+ # Friendly house-system names mapped to the single-letter API codes.
12
+ HOUSE_SYSTEM_MAP = {
13
+ "placidus": "P",
14
+ "koch": "K",
15
+ "porphyry": "O",
16
+ "regiomontanus": "R",
17
+ "campanus": "C",
18
+ "equal": "E",
19
+ "whole-sign": "W",
20
+ "whole_sign": "W",
21
+ "wholesign": "W",
22
+ "morinus": "M",
23
+ "alcabitius": "B",
24
+ }
25
+ VALID_HOUSE_SYSTEM_LETTERS = set(HOUSE_SYSTEM_MAP.values())
26
+ HOUSE_SYSTEM_FRIENDLY_NAMES = (
27
+ "placidus, koch, porphyry, regiomontanus, campanus, equal, "
28
+ "whole-sign, morinus, alcabitius"
29
+ )
30
+
31
+
32
+ def resolve_house_system(value: Optional[str]) -> str:
33
+ """Map a friendly house-system name to its single-letter API code.
34
+
35
+ Accepts friendly names case-insensitively (e.g. ``placidus``, ``Whole-Sign``)
36
+ and already-valid single letters (passed through unchanged, upper-cased).
37
+ An empty value defaults to ``P`` (Placidus). Raises ``ValueError`` for
38
+ anything else.
39
+ """
40
+ hs = (value or "").strip()
41
+ if not hs:
42
+ return "P"
43
+ if hs.upper() in VALID_HOUSE_SYSTEM_LETTERS:
44
+ return hs.upper()
45
+ mapped = HOUSE_SYSTEM_MAP.get(hs.lower())
46
+ if mapped:
47
+ return mapped
48
+ raise ValueError(
49
+ f"Invalid house_system '{value}'. Must be one of: "
50
+ f"{HOUSE_SYSTEM_FRIENDLY_NAMES} (or a single-letter code: "
51
+ f"{', '.join(sorted(VALID_HOUSE_SYSTEM_LETTERS))})"
52
+ )
@@ -3,6 +3,7 @@
3
3
  from typing import Any, Dict
4
4
 
5
5
  from ..client import BaseClient
6
+ from ._house_system import resolve_house_system
6
7
 
7
8
  HOST = "astroapi-8"
8
9
 
@@ -33,7 +34,7 @@ class CompositeApi:
33
34
  "p2_year": p2_year, "p2_hour": p2_hour, "p2_min": p2_min,
34
35
  "p2_sec": p2_sec, "p2_gender": p2_gender, "p2_place": p2_place,
35
36
  "p2_lat": p2_lat, "p2_lon": p2_lon, "p2_tzone": p2_tzone,
36
- "lan": lan, "house_system": house_system,
37
+ "lan": lan, "house_system": resolve_house_system(house_system),
37
38
  }
38
39
  d.update(extra)
39
40
  return d
@@ -1,8 +1,9 @@
1
1
  """Western API - Natal endpoints (astroapi-4 and astroapi-8)."""
2
2
 
3
- from typing import Any, Dict, List, Optional
3
+ from typing import Any, Dict
4
4
 
5
5
  from ..client import BaseClient
6
+ from ._house_system import resolve_house_system
6
7
 
7
8
 
8
9
  class NatalApi:
@@ -27,7 +28,7 @@ class NatalApi:
27
28
  "full_name": full_name, "day": day, "month": month, "year": year,
28
29
  "hour": hour, "min": min, "sec": sec, "gender": gender,
29
30
  "place": place, "lat": lat, "lon": lon, "tzone": tzone,
30
- "lan": lan, "house_system": house_system,
31
+ "lan": lan, "house_system": resolve_house_system(house_system),
31
32
  }
32
33
  d.update(extra)
33
34
  return d
@@ -44,11 +45,11 @@ class NatalApi:
44
45
 
45
46
  def planetary_positions(self, **kw: Any) -> Dict[str, Any]:
46
47
  """Natal Planetary Positions."""
47
- return self._post4("/western-api/v2/planetary-positions", **kw)
48
+ return self._post4("/western-api/v1/planetary-positions", **kw)
48
49
 
49
50
  def house_cusps(self, **kw: Any) -> Dict[str, Any]:
50
51
  """Natal House Cusps."""
51
- return self._post4("/western-api/v2/house-cusps", **kw)
52
+ return self._post4("/western-api/v1/house-cusps", **kw)
52
53
 
53
54
  def aspect_table(self, **kw: Any) -> Dict[str, Any]:
54
55
  """Natal Aspect Table."""
@@ -3,6 +3,7 @@
3
3
  from typing import Any, Dict
4
4
 
5
5
  from ..client import BaseClient
6
+ from ._house_system import resolve_house_system
6
7
 
7
8
  HOST = "astroapi-8"
8
9
 
@@ -25,7 +26,7 @@ class PlanetReturnsApi:
25
26
  "full_name": full_name, "day": day, "month": month, "year": year,
26
27
  "hour": hour, "min": min, "sec": sec, "gender": gender,
27
28
  "place": place, "lat": lat, "lon": lon, "tzone": tzone,
28
- "lan": lan, "house_system": house_system,
29
+ "lan": lan, "house_system": resolve_house_system(house_system),
29
30
  }
30
31
  d.update(extra)
31
32
  return d
@@ -46,14 +47,15 @@ class PlanetReturnsApi:
46
47
  return self._c.post(HOST, "/western-api/v1/planet-returns-list", self._birth(**kw))
47
48
 
48
49
  def planet_return_details(
49
- self, planet: str, return_key: str,
50
+ self, planet: str, return_key: str, return_year: int,
50
51
  return_lat: float, return_lon: float,
51
52
  return_tzone: float, return_place: str,
52
53
  **kw: Any,
53
54
  ) -> Dict[str, Any]:
54
- """Planet Return Details."""
55
+ """Planet Return Details (``return_key`` comes from ``planet_return_list``)."""
55
56
  kw["planet"] = planet
56
57
  kw["return_key"] = return_key
58
+ kw["return_year"] = return_year
57
59
  kw["return_lat"] = return_lat
58
60
  kw["return_lon"] = return_lon
59
61
  kw["return_tzone"] = return_tzone
@@ -3,6 +3,7 @@
3
3
  from typing import Any, Dict
4
4
 
5
5
  from ..client import BaseClient
6
+ from ._house_system import resolve_house_system
6
7
 
7
8
  HOST = "astroapi-8"
8
9
 
@@ -25,7 +26,7 @@ class PrenatalApi:
25
26
  "full_name": full_name, "day": day, "month": month, "year": year,
26
27
  "hour": hour, "min": min, "sec": sec, "gender": gender,
27
28
  "place": place, "lat": lat, "lon": lon, "tzone": tzone,
28
- "lan": lan, "house_system": house_system,
29
+ "lan": lan, "house_system": resolve_house_system(house_system),
29
30
  }
30
31
  d.update(extra)
31
32
  return d
@@ -1,8 +1,9 @@
1
1
  """Western API - Progressions endpoints (astroapi-8)."""
2
2
 
3
- from typing import Any, Dict
3
+ from typing import Any, Dict, Optional
4
4
 
5
5
  from ..client import BaseClient
6
+ from ._house_system import resolve_house_system
6
7
 
7
8
  HOST = "astroapi-8"
8
9
 
@@ -25,7 +26,7 @@ class ProgressionsApi:
25
26
  "full_name": full_name, "day": day, "month": month, "year": year,
26
27
  "hour": hour, "min": min, "sec": sec, "gender": gender,
27
28
  "place": place, "lat": lat, "lon": lon, "tzone": tzone,
28
- "lan": lan, "house_system": house_system,
29
+ "lan": lan, "house_system": resolve_house_system(house_system),
29
30
  }
30
31
  d.update(extra)
31
32
  return d
@@ -48,15 +49,25 @@ class ProgressionsApi:
48
49
  return self._c.post(HOST, "/western-api/v1/planetary-arc-directions", self._birth(**kw))
49
50
 
50
51
  def secondary_progressions(
51
- self, planet: str,
52
+ self,
52
53
  progressed_day: int, progressed_month: int, progressed_year: int,
54
+ progressed_hour: int, progressed_min: int, progressed_sec: int,
53
55
  progressed_type: str,
56
+ planet: Optional[str] = None,
54
57
  **kw: Any,
55
58
  ) -> Dict[str, Any]:
56
- """Secondary Progressions."""
57
- kw["planet"] = planet
59
+ """Secondary Progressions.
60
+
61
+ ``progressed_day/month/year/hour/min/sec`` and ``progressed_type`` are
62
+ required by the API; ``planet`` is optional.
63
+ """
58
64
  kw["progressed_day"] = progressed_day
59
65
  kw["progressed_month"] = progressed_month
60
66
  kw["progressed_year"] = progressed_year
67
+ kw["progressed_hour"] = progressed_hour
68
+ kw["progressed_min"] = progressed_min
69
+ kw["progressed_sec"] = progressed_sec
61
70
  kw["progressed_type"] = progressed_type
71
+ if planet is not None:
72
+ kw["planet"] = planet
62
73
  return self._c.post(HOST, "/western-api/v1/secondary-progressions", self._birth(**kw))
@@ -3,6 +3,7 @@
3
3
  from typing import Any, Dict
4
4
 
5
5
  from ..client import BaseClient
6
+ from ._house_system import resolve_house_system
6
7
 
7
8
  HOST = "astroapi-4"
8
9
  HOST8 = "astroapi-8"
@@ -38,7 +39,7 @@ class SynastryApi:
38
39
  "p2_year": p2_year, "p2_hour": p2_hour, "p2_min": p2_min,
39
40
  "p2_sec": p2_sec, "p2_gender": p2_gender, "p2_place": p2_place,
40
41
  "p2_lat": p2_lat, "p2_lon": p2_lon, "p2_tzone": p2_tzone,
41
- "lan": lan, "house_system": house_system,
42
+ "lan": lan, "house_system": resolve_house_system(house_system),
42
43
  }
43
44
  d.update(extra)
44
45
  return d
@@ -0,0 +1,218 @@
1
+ """Western API - Transit endpoints (astroapi-4 and astroapi-8)."""
2
+
3
+ from typing import Any, Dict
4
+
5
+ from ..client import BaseClient
6
+ from ._house_system import resolve_house_system
7
+
8
+
9
+ class TransitApi:
10
+ """Transit endpoints for Western astrology."""
11
+
12
+ def __init__(self, client: BaseClient) -> None:
13
+ self._c = client
14
+
15
+ # ------------------------------------------------------------------ #
16
+ # Payload helpers
17
+ # ------------------------------------------------------------------ #
18
+
19
+ @staticmethod
20
+ def _birth(
21
+ full_name: str, day: int, month: int, year: int,
22
+ hour: int, min: int, sec: int, gender: str,
23
+ place: str, lat: float, lon: float, tzone: float,
24
+ lan: str = "en", house_system: str = "placidus",
25
+ **extra: Any,
26
+ ) -> Dict[str, Any]:
27
+ d: Dict[str, Any] = {
28
+ "full_name": full_name, "day": day, "month": month, "year": year,
29
+ "hour": hour, "min": min, "sec": sec, "gender": gender,
30
+ "place": place, "lat": lat, "lon": lon, "tzone": tzone,
31
+ "lan": lan, "house_system": resolve_house_system(house_system),
32
+ }
33
+ d.update(extra)
34
+ return d
35
+
36
+ @staticmethod
37
+ def _transit_dt(
38
+ transit_day: int, transit_month: int, transit_year: int,
39
+ transit_hour: int = 0, transit_min: int = 0, transit_sec: int = 0,
40
+ ) -> Dict[str, Any]:
41
+ return {
42
+ "transit_day": transit_day, "transit_month": transit_month,
43
+ "transit_year": transit_year, "transit_hour": transit_hour,
44
+ "transit_min": transit_min, "transit_sec": transit_sec,
45
+ }
46
+
47
+ @staticmethod
48
+ def _transit_loc(
49
+ transit_lat: float, transit_lon: float,
50
+ transit_tzone: float, transit_place: str,
51
+ ) -> Dict[str, Any]:
52
+ return {
53
+ "transit_lat": transit_lat, "transit_lon": transit_lon,
54
+ "transit_tzone": transit_tzone, "transit_place": transit_place,
55
+ }
56
+
57
+ def _post4(self, path: str, **kw: Any) -> Dict[str, Any]:
58
+ return self._c.post("astroapi-4", path, self._birth(**kw))
59
+
60
+ def _post8(self, path: str, **kw: Any) -> Dict[str, Any]:
61
+ return self._c.post("astroapi-8", path, self._birth(**kw))
62
+
63
+ # ------------------------------------------------------------------ #
64
+ # astroapi-4 transit endpoints
65
+ # ------------------------------------------------------------------ #
66
+
67
+ def basic(
68
+ self,
69
+ transit_day: int, transit_month: int, transit_year: int,
70
+ transit_hour: int = 0, transit_min: int = 0, transit_sec: int = 0,
71
+ **kw: Any,
72
+ ) -> Dict[str, Any]:
73
+ """Transit Basic. Requires the transit date; time defaults to 00:00:00."""
74
+ payload = self._birth(**kw)
75
+ payload.update(self._transit_dt(
76
+ transit_day, transit_month, transit_year,
77
+ transit_hour, transit_min, transit_sec,
78
+ ))
79
+ return self._c.post("astroapi-4", "/western-api/v1/transit/basic", payload)
80
+
81
+ def daily(self, **kw: Any) -> Dict[str, Any]:
82
+ """Transit Daily."""
83
+ return self._post4("/western-api/v1/transit/daily", **kw)
84
+
85
+ def weekly(self, transit_planet: str, **kw: Any) -> Dict[str, Any]:
86
+ """Transit Weekly. Requires the transiting planet (e.g. 'moon')."""
87
+ kw["transit_planet"] = transit_planet
88
+ return self._post4("/western-api/v1/transit/weekly", **kw)
89
+
90
+ def house(
91
+ self,
92
+ transit_day: int, transit_month: int, transit_year: int,
93
+ transit_lat: float, transit_lon: float,
94
+ transit_tzone: float, transit_place: str,
95
+ transit_hour: int = 0, transit_min: int = 0, transit_sec: int = 0,
96
+ **kw: Any,
97
+ ) -> Dict[str, Any]:
98
+ """Transit House. Requires transit date + location; time defaults to 00:00:00."""
99
+ payload = self._birth(**kw)
100
+ payload.update(self._transit_dt(
101
+ transit_day, transit_month, transit_year,
102
+ transit_hour, transit_min, transit_sec,
103
+ ))
104
+ payload.update(self._transit_loc(
105
+ transit_lat, transit_lon, transit_tzone, transit_place,
106
+ ))
107
+ return self._c.post("astroapi-4", "/western-api/v1/transit/house", payload)
108
+
109
+ # ------------------------------------------------------------------ #
110
+ # astroapi-8 transit endpoints
111
+ # ------------------------------------------------------------------ #
112
+
113
+ def monthly(
114
+ self,
115
+ transit_planet: str, transit_month: int, transit_year: int,
116
+ transit_lat: float, transit_lon: float,
117
+ transit_tzone: float, transit_place: str,
118
+ **kw: Any,
119
+ ) -> Dict[str, Any]:
120
+ """Transit Monthly. Requires the transiting planet, month, year and location."""
121
+ payload = self._birth(**kw)
122
+ payload.update({
123
+ "transit_planet": transit_planet,
124
+ "transit_month": transit_month, "transit_year": transit_year,
125
+ })
126
+ payload.update(self._transit_loc(
127
+ transit_lat, transit_lon, transit_tzone, transit_place,
128
+ ))
129
+ return self._c.post("astroapi-8", "/western-api/v2/transit/monthly", payload)
130
+
131
+ def full(
132
+ self,
133
+ transit_planet: str,
134
+ transit_day: int, transit_month: int, transit_year: int,
135
+ transit_lat: float, transit_lon: float,
136
+ transit_tzone: float, transit_place: str,
137
+ transit_hour: int = 0, transit_min: int = 0, transit_sec: int = 0,
138
+ **kw: Any,
139
+ ) -> Dict[str, Any]:
140
+ """Full Transit. Requires the transiting planet, transit date and location."""
141
+ payload = self._birth(**kw)
142
+ payload["transit_planet"] = transit_planet
143
+ payload.update(self._transit_dt(
144
+ transit_day, transit_month, transit_year,
145
+ transit_hour, transit_min, transit_sec,
146
+ ))
147
+ payload.update(self._transit_loc(
148
+ transit_lat, transit_lon, transit_tzone, transit_place,
149
+ ))
150
+ return self._c.post("astroapi-8", "/western-api/v1/full-transit", payload)
151
+
152
+ def planet_retrograde(
153
+ self, planet: str, month: int, year: int,
154
+ place: str, lat: float, lon: float, tzone: float,
155
+ ) -> Dict[str, Any]:
156
+ """Planet Retrograde Transit."""
157
+ return self._c.post("astroapi-8", "/western-api/v1/planet-retrograde-transit", {
158
+ "planet": planet, "month": month, "year": year,
159
+ "place": place, "lat": lat, "lon": lon, "tzone": tzone,
160
+ })
161
+
162
+ def planetary_ingress(
163
+ self, planet: str, month: int, year: int,
164
+ place: str, lat: float, lon: float, tzone: float,
165
+ ) -> Dict[str, Any]:
166
+ """Planetary Ingress."""
167
+ return self._c.post("astroapi-8", "/western-api/v1/planetary-ingress", {
168
+ "planet": planet, "month": month, "year": year,
169
+ "place": place, "lat": lat, "lon": lon, "tzone": tzone,
170
+ })
171
+
172
+ def planet_combustion(
173
+ self, planet: str, month: int, year: int,
174
+ place: str, lat: float, lon: float, tzone: float,
175
+ ) -> Dict[str, Any]:
176
+ """Planet Combustion Transit."""
177
+ return self._c.post("astroapi-8", "/western-api/v1/planet-combustion-transit", {
178
+ "planet": planet, "month": month, "year": year,
179
+ "place": place, "lat": lat, "lon": lon, "tzone": tzone,
180
+ })
181
+
182
+ def wheel_chart(
183
+ self,
184
+ transit_day: int, transit_month: int, transit_year: int,
185
+ transit_lat: float, transit_lon: float,
186
+ transit_tzone: float, transit_place: str,
187
+ transit_hour: int = 0, transit_min: int = 0, transit_sec: int = 0,
188
+ **kw: Any,
189
+ ) -> Dict[str, Any]:
190
+ """Transit Wheel Chart. Requires transit date + location."""
191
+ payload = self._birth(**kw)
192
+ payload.update(self._transit_dt(
193
+ transit_day, transit_month, transit_year,
194
+ transit_hour, transit_min, transit_sec,
195
+ ))
196
+ payload.update(self._transit_loc(
197
+ transit_lat, transit_lon, transit_tzone, transit_place,
198
+ ))
199
+ return self._c.post("astroapi-8", "/western-api/v1/transit/wheel-chart", payload)
200
+
201
+ def planetary_positions(
202
+ self,
203
+ transit_day: int, transit_month: int, transit_year: int,
204
+ transit_lat: float, transit_lon: float,
205
+ transit_tzone: float, transit_place: str,
206
+ transit_hour: int = 0, transit_min: int = 0, transit_sec: int = 0,
207
+ **kw: Any,
208
+ ) -> Dict[str, Any]:
209
+ """Transit Planetary Positions. Requires transit date + location."""
210
+ payload = self._birth(**kw)
211
+ payload.update(self._transit_dt(
212
+ transit_day, transit_month, transit_year,
213
+ transit_hour, transit_min, transit_sec,
214
+ ))
215
+ payload.update(self._transit_loc(
216
+ transit_lat, transit_lon, transit_tzone, transit_place,
217
+ ))
218
+ return self._c.post("astroapi-8", "/western-api/v1/transit/planetary-positions", payload)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: divineapi
3
- Version: 1.2.0
3
+ Version: 1.4.0
4
4
  Summary: Official Python SDK for the Divine API - Astrology, Numerology, Tarot & more
5
5
  Author-email: Divine API <support@divineapi.com>
6
6
  License: MIT
@@ -51,9 +51,9 @@ from divineapi import DivineApi
51
51
 
52
52
  client = DivineApi(api_key="your-api-key", auth_token="your-bearer-token")
53
53
 
54
- # Daily Horoscope
54
+ # Daily Horoscope (h_day = today, tomorrow, or yesterday)
55
55
  result = client.horoscope.daily(
56
- sign="aries", day=10, month=3, year=2024, tzone=5.5
56
+ sign="aries", h_day="today", tzone=5.5
57
57
  )
58
58
  print(result)
59
59
  ```
@@ -64,10 +64,11 @@ print(result)
64
64
 
65
65
  ```python
66
66
  # Daily / Weekly / Monthly / Yearly Horoscope
67
- client.horoscope.daily(sign="aries", day=10, month=3, year=2024, tzone=5.5)
68
- client.horoscope.weekly(sign="leo", week="thisweek", tzone=5.5)
69
- client.horoscope.monthly(sign="cancer", month=3, tzone=5.5)
70
- client.horoscope.yearly(sign="virgo", year=2024, tzone=5.5)
67
+ # daily h_day = today / tomorrow / yesterday; weekly/monthly/yearly = current / prev / next
68
+ client.horoscope.daily(sign="aries", h_day="today", tzone=5.5)
69
+ client.horoscope.weekly(sign="leo", week="current", tzone=5.5)
70
+ client.horoscope.monthly(sign="cancer", month="current", tzone=5.5)
71
+ client.horoscope.yearly(sign="virgo", year="current", tzone=5.5)
71
72
 
72
73
  # Chinese & Numerology Horoscope
73
74
  client.horoscope.chinese(sign="rat", h_day="today", tzone=5.5)
@@ -140,6 +141,14 @@ client.indian.kundli.lal_kitab_varshphal_chart(2026, **birth)
140
141
  # Lal Kitab dasha content (no birth data needed)
141
142
  client.indian.kundli.lal_kitab_mahadasha_content(maha_dasha="saturn")
142
143
  client.indian.kundli.lal_kitab_antardasha_content(maha_dasha="saturn", antar_dasha="mercury")
144
+
145
+ # Additional kundli analysis
146
+ client.indian.kundli.vargottama_planets(**birth)
147
+ client.indian.kundli.bhav_bala(**birth)
148
+ client.indian.kundli.shani_ashtam_shani(**birth)
149
+ client.indian.kundli.bhava_analysis(**birth)
150
+ client.indian.kundli.bhava_group_predictions(**birth)
151
+ client.indian.kundli.planet_remedies("sun", **birth) # analysis_planet + birth
143
152
  ```
144
153
 
145
154
  ### Indian Astrology - Match Making
@@ -164,8 +173,9 @@ client.indian.festival.chaitra_festivals(
164
173
  client.indian.festival.english_calendar(
165
174
  year=2024, month=3, place="Delhi", lat=28.6, lon=77.2, tzone=5.5
166
175
  )
176
+ # festival is a lowercase snake_case slug from the Divine festival list
167
177
  client.indian.festival.find_festival(
168
- festival="diwali", year=2024,
178
+ festival="maha_shivratri", year=2024,
169
179
  place="Delhi", lat=28.6, lon=77.2, tzone=5.5
170
180
  )
171
181
  client.indian.festival.malayalam_festivals(
@@ -193,7 +203,7 @@ client.western.natal.house_cusps(**birth_w)
193
203
  client.western.natal.aspect_table(**birth_w)
194
204
  client.western.natal.natal_wheel_chart(**birth_w)
195
205
  client.western.natal.natal_insights(**birth_w)
196
- client.western.natal.dominants(method="traditional", **birth_w)
206
+ client.western.natal.dominants(method="TRADITIONAL", **birth_w)
197
207
  ```
198
208
 
199
209
  ### Western Astrology - Synastry
@@ -252,7 +262,7 @@ client.lifestyle.astro_chic_picks(sign="virgo", h_day="today", tzone=5.5)
252
262
  ### Calculators
253
263
 
254
264
  ```python
255
- client.calculators.flames(full_name="John", partner_name="Jane")
265
+ client.calculators.flames(your_name="John", partner_name="Jane")
256
266
  client.calculators.love_calculator(
257
267
  your_name="John", partner_name="Jane",
258
268
  your_gender="male", partner_gender="female",
@@ -262,11 +272,15 @@ client.calculators.love_calculator(
262
272
  ### PDF Reports
263
273
 
264
274
  ```python
275
+ # The six branding fields (company_url, logo_url, footer_text, company_name,
276
+ # company_email, company_bio) are REQUIRED by the PDF backend.
265
277
  client.pdf.kundali_sampoorna(
266
278
  full_name="John", day=1, month=1, year=1990,
267
279
  hour=10, min=30, sec=0, gender="male",
268
280
  place="Mumbai", lat=19.07, lon=72.87, tzone=5.5,
269
281
  company_name="AstroCo", company_url="https://example.com",
282
+ company_email="hello@example.com", company_bio="We do astrology.",
283
+ logo_url="https://example.com/logo.png", footer_text="(c) AstroCo",
270
284
  )
271
285
  ```
272
286
 
@@ -278,7 +292,7 @@ from divineapi import DivineApi, AuthenticationError, ValidationError, RateLimit
278
292
  client = DivineApi(api_key="...", auth_token="...")
279
293
 
280
294
  try:
281
- result = client.horoscope.daily(sign="aries", day=10, month=3, year=2024, tzone=5.5)
295
+ result = client.horoscope.daily(sign="aries", h_day="today", tzone=5.5)
282
296
  except AuthenticationError as e:
283
297
  print(f"Auth failed: {e}")
284
298
  except ValidationError as e:
@@ -291,7 +305,7 @@ except RateLimitError as e:
291
305
 
292
306
  ```python
293
307
  with DivineApi(api_key="...", auth_token="...") as client:
294
- result = client.horoscope.daily(sign="aries", day=10, month=3, year=2024, tzone=5.5)
308
+ result = client.horoscope.daily(sign="aries", h_day="today", tzone=5.5)
295
309
  ```
296
310
 
297
311
  ## Configuration
@@ -22,6 +22,7 @@ divineapi/indian/kundli.py
22
22
  divineapi/indian/match_making.py
23
23
  divineapi/indian/panchang.py
24
24
  divineapi/western/__init__.py
25
+ divineapi/western/_house_system.py
25
26
  divineapi/western/composite.py
26
27
  divineapi/western/natal.py
27
28
  divineapi/western/planet_returns.py
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "divineapi"
7
- version = "1.2.0"
7
+ version = "1.4.0"
8
8
  description = "Official Python SDK for the Divine API - Astrology, Numerology, Tarot & more"
9
9
  readme = "README.md"
10
10
  license = {text = "MIT"}
@@ -1,110 +0,0 @@
1
- """Western API - Transit endpoints (astroapi-4 and astroapi-8)."""
2
-
3
- from typing import Any, Dict
4
-
5
- from ..client import BaseClient
6
-
7
-
8
- class TransitApi:
9
- """Transit endpoints for Western astrology."""
10
-
11
- def __init__(self, client: BaseClient) -> None:
12
- self._c = client
13
-
14
- # ------------------------------------------------------------------ #
15
- # Birth-data helper
16
- # ------------------------------------------------------------------ #
17
-
18
- @staticmethod
19
- def _birth(
20
- full_name: str, day: int, month: int, year: int,
21
- hour: int, min: int, sec: int, gender: str,
22
- place: str, lat: float, lon: float, tzone: float,
23
- lan: str = "en", house_system: str = "placidus",
24
- **extra: Any,
25
- ) -> Dict[str, Any]:
26
- d: Dict[str, Any] = {
27
- "full_name": full_name, "day": day, "month": month, "year": year,
28
- "hour": hour, "min": min, "sec": sec, "gender": gender,
29
- "place": place, "lat": lat, "lon": lon, "tzone": tzone,
30
- "lan": lan, "house_system": house_system,
31
- }
32
- d.update(extra)
33
- return d
34
-
35
- def _post4(self, path: str, **kw: Any) -> Dict[str, Any]:
36
- return self._c.post("astroapi-4", path, self._birth(**kw))
37
-
38
- def _post8(self, path: str, **kw: Any) -> Dict[str, Any]:
39
- return self._c.post("astroapi-8", path, self._birth(**kw))
40
-
41
- # ------------------------------------------------------------------ #
42
- # astroapi-4 transit endpoints
43
- # ------------------------------------------------------------------ #
44
-
45
- def basic(self, **kw: Any) -> Dict[str, Any]:
46
- """Transit Basic (pass transit_day/month/year/hour/min/sec via **kw)."""
47
- return self._post4("/western-api/v1/transit/basic", **kw)
48
-
49
- def daily(self, **kw: Any) -> Dict[str, Any]:
50
- """Transit Daily."""
51
- return self._post4("/western-api/v1/transit/daily", **kw)
52
-
53
- def weekly(self, transit_planet: str, **kw: Any) -> Dict[str, Any]:
54
- """Transit Weekly."""
55
- kw["transit_planet"] = transit_planet
56
- return self._post4("/western-api/v1/transit/weekly", **kw)
57
-
58
- def house(self, **kw: Any) -> Dict[str, Any]:
59
- """Transit House (pass full transit params via **kw)."""
60
- return self._post4("/western-api/v1/transit/house", **kw)
61
-
62
- # ------------------------------------------------------------------ #
63
- # astroapi-8 transit endpoints
64
- # ------------------------------------------------------------------ #
65
-
66
- def monthly(self, **kw: Any) -> Dict[str, Any]:
67
- """Transit Monthly (pass extensive transit params via **kw)."""
68
- return self._post8("/western-api/v2/transit/monthly", **kw)
69
-
70
- def full(self, **kw: Any) -> Dict[str, Any]:
71
- """Full Transit (pass extensive transit params via **kw)."""
72
- return self._post8("/western-api/v1/full-transit", **kw)
73
-
74
- def planet_retrograde(
75
- self, planet: str, month: int, year: int,
76
- place: str, lat: float, lon: float, tzone: float,
77
- ) -> Dict[str, Any]:
78
- """Planet Retrograde Transit."""
79
- return self._c.post("astroapi-8", "/western-api/v1/planet-retrograde-transit", {
80
- "planet": planet, "month": month, "year": year,
81
- "place": place, "lat": lat, "lon": lon, "tzone": tzone,
82
- })
83
-
84
- def planetary_ingress(
85
- self, planet: str, month: int, year: int,
86
- place: str, lat: float, lon: float, tzone: float,
87
- ) -> Dict[str, Any]:
88
- """Planetary Ingress."""
89
- return self._c.post("astroapi-8", "/western-api/v1/planetary-ingress", {
90
- "planet": planet, "month": month, "year": year,
91
- "place": place, "lat": lat, "lon": lon, "tzone": tzone,
92
- })
93
-
94
- def planet_combustion(
95
- self, planet: str, month: int, year: int,
96
- place: str, lat: float, lon: float, tzone: float,
97
- ) -> Dict[str, Any]:
98
- """Planet Combustion Transit."""
99
- return self._c.post("astroapi-8", "/western-api/v1/planet-combustion-transit", {
100
- "planet": planet, "month": month, "year": year,
101
- "place": place, "lat": lat, "lon": lon, "tzone": tzone,
102
- })
103
-
104
- def wheel_chart(self, **kw: Any) -> Dict[str, Any]:
105
- """Transit Wheel Chart."""
106
- return self._post8("/western-api/v1/transit/wheel-chart", **kw)
107
-
108
- def planetary_positions(self, **kw: Any) -> Dict[str, Any]:
109
- """Transit Planetary Positions."""
110
- return self._post8("/western-api/v1/transit/planetary-positions", **kw)
File without changes
File without changes
File without changes
File without changes
File without changes