polymarket-apis 0.3.0__py3-none-any.whl → 0.3.9__py3-none-any.whl

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.

Potentially problematic release.


This version of polymarket-apis might be problematic. Click here for more details.

Files changed (32) hide show
  1. polymarket_apis/__init__.py +42 -0
  2. polymarket_apis/clients/__init__.py +23 -0
  3. polymarket_apis/clients/clob_client.py +224 -117
  4. polymarket_apis/clients/data_client.py +220 -67
  5. polymarket_apis/clients/gamma_client.py +589 -101
  6. polymarket_apis/clients/graphql_client.py +28 -11
  7. polymarket_apis/clients/web3_client.py +538 -131
  8. polymarket_apis/clients/websockets_client.py +24 -7
  9. polymarket_apis/types/__init__.py +167 -0
  10. polymarket_apis/types/clob_types.py +35 -14
  11. polymarket_apis/types/common.py +105 -35
  12. polymarket_apis/types/data_types.py +48 -3
  13. polymarket_apis/types/gamma_types.py +529 -257
  14. polymarket_apis/types/web3_types.py +45 -0
  15. polymarket_apis/types/websockets_types.py +92 -41
  16. polymarket_apis/utilities/config.py +1 -0
  17. polymarket_apis/utilities/constants.py +5 -4
  18. polymarket_apis/utilities/exceptions.py +9 -0
  19. polymarket_apis/utilities/order_builder/builder.py +38 -22
  20. polymarket_apis/utilities/order_builder/helpers.py +0 -1
  21. polymarket_apis/utilities/signing/hmac.py +5 -1
  22. polymarket_apis/utilities/signing/signer.py +2 -2
  23. polymarket_apis/utilities/web3/abis/Safe.json +1138 -0
  24. polymarket_apis/utilities/web3/abis/SafeProxyFactory.json +224 -0
  25. polymarket_apis/utilities/web3/abis/custom_contract_errors.py +1 -1
  26. polymarket_apis/utilities/web3/helpers.py +235 -0
  27. {polymarket_apis-0.3.0.dist-info → polymarket_apis-0.3.9.dist-info}/METADATA +48 -8
  28. polymarket_apis-0.3.9.dist-info/RECORD +44 -0
  29. polymarket_apis/utilities/schemas/activity-subgraph.graphql +0 -86
  30. polymarket_apis/utilities/schemas/open-interest.graphql +0 -30
  31. polymarket_apis-0.3.0.dist-info/RECORD +0 -43
  32. {polymarket_apis-0.3.0.dist-info → polymarket_apis-0.3.9.dist-info}/WHEEL +0 -0
@@ -7,7 +7,18 @@ from urllib.parse import urljoin
7
7
 
8
8
  import httpx
9
9
 
10
- from ..types.gamma_types import Event, EventList, GammaMarket
10
+ from ..types.common import EthAddress
11
+ from ..types.gamma_types import (
12
+ Comment,
13
+ Event,
14
+ GammaMarket,
15
+ SearchResult,
16
+ Series,
17
+ Sport,
18
+ Tag,
19
+ TagRelation,
20
+ Team,
21
+ )
11
22
 
12
23
 
13
24
  def generate_random_id(length=16):
@@ -24,31 +35,98 @@ class PolymarketGammaClient:
24
35
  def _build_url(self, endpoint: str) -> str:
25
36
  return urljoin(self.base_url, endpoint)
26
37
 
38
+ def search(
39
+ self,
40
+ query: str,
41
+ cache: Optional[bool] = None,
42
+ status: Optional[Literal["active", "resolved"]] = None,
43
+ limit_per_type: Optional[int] = None, # max is 50
44
+ page: Optional[int] = None,
45
+ tags: Optional[list[str]] = None,
46
+ keep_closed_markets: Optional[bool] = None,
47
+ sort: Optional[
48
+ Literal[
49
+ "volume",
50
+ "volume_24hr",
51
+ "liquidity",
52
+ "start_date",
53
+ "end_date",
54
+ "competitive",
55
+ ]
56
+ ] = None,
57
+ ascending: Optional[bool] = None,
58
+ search_tags: Optional[bool] = None,
59
+ search_profiles: Optional[bool] = None,
60
+ recurrence: Optional[
61
+ Literal["hourly", "daily", "weekly", "monthly", "annual"]
62
+ ] = None,
63
+ exclude_tag_ids: Optional[list[int]] = None,
64
+ optimized: Optional[bool] = None,
65
+ ) -> SearchResult:
66
+ params: dict[str, str | list[str] | int | bool] = {
67
+ "q": query,
68
+ }
69
+ if cache is not None:
70
+ params["cache"] = str(cache).lower()
71
+ if status:
72
+ params["events_status"] = status
73
+ if limit_per_type:
74
+ params["limit_per_type"] = limit_per_type
75
+ if page:
76
+ params["page"] = page
77
+ if tags:
78
+ params["events_tag"] = json.dumps([json.dumps(item) for item in tags])
79
+ if keep_closed_markets is not None:
80
+ params["keep_closed_markets"] = keep_closed_markets
81
+ if sort:
82
+ params["sort"] = sort
83
+ if ascending is not None:
84
+ params["ascending"] = str(ascending).lower()
85
+ if search_tags is not None:
86
+ params["search_tags"] = str(search_tags).lower()
87
+ if search_profiles is not None:
88
+ params["search_profiles"] = str(search_profiles).lower()
89
+ if recurrence:
90
+ params["recurrence"] = recurrence
91
+ if exclude_tag_ids:
92
+ params["exclude_tag_id"] = [str(i) for i in exclude_tag_ids]
93
+ if optimized is not None:
94
+ params["optimized"] = str(optimized).lower()
95
+ response = self.client.get(self._build_url("/public-search"), params=params)
96
+ response.raise_for_status()
97
+ return SearchResult(**response.json())
98
+
99
+ def get_market(self, market_id: str) -> GammaMarket:
100
+ """Get a GammaMarket by market_id."""
101
+ response = self.client.get(self._build_url(f"/markets/{market_id}"))
102
+ response.raise_for_status()
103
+ return GammaMarket(**response.json())
104
+
27
105
  def get_markets(
28
- self,
29
- limit: Optional[int] = None,
30
- offset: Optional[int] = None,
31
- order: Optional[str] = None,
32
- ascending: bool = True,
33
- archived: Optional[bool] = None,
34
- active: Optional[bool] = None,
35
- closed: Optional[bool] = None,
36
- slugs: Optional[list[str]] = None,
37
- market_ids: Optional[list[int]] = None,
38
- token_ids: Optional[list[str]] = None,
39
- condition_ids: Optional[list[str]] = None,
40
- tag_id: Optional[int] = None,
41
- related_tags: Optional[bool] = False,
42
- liquidity_num_min: Optional[float] = None,
43
- liquidity_num_max: Optional[float] = None,
44
- volume_num_min: Optional[float] = None,
45
- volume_num_max: Optional[float] = None,
46
- start_date_min: Optional[datetime] = None,
47
- start_date_max: Optional[datetime] = None,
48
- end_date_min: Optional[datetime] = None,
49
- end_date_max: Optional[datetime] = None,
106
+ self,
107
+ limit: int | None = None,
108
+ offset: int | None = None,
109
+ order: str | None = None,
110
+ ascending: bool = True,
111
+ archived: bool | None = None,
112
+ active: bool | None = None,
113
+ closed: bool | None = None,
114
+ slugs: list[str] | None = None,
115
+ market_ids: list[int] | None = None,
116
+ token_ids: list[str] | None = None,
117
+ condition_ids: list[str] | None = None,
118
+ tag_id: int | None = None,
119
+ related_tags: bool | None = False,
120
+ liquidity_num_min: float | None = None,
121
+ liquidity_num_max: float | None = None,
122
+ volume_num_min: float | None = None,
123
+ volume_num_max: float | None = None,
124
+ start_date_min: datetime | None = None,
125
+ start_date_max: datetime | None = None,
126
+ end_date_min: datetime | None = None,
127
+ end_date_max: datetime | None = None,
50
128
  ) -> list[GammaMarket]:
51
- params = {}
129
+ params: dict[str, float | int | list[int] | str | list[str] | bool] = {}
52
130
  if limit:
53
131
  params["limit"] = limit
54
132
  if offset:
@@ -95,41 +173,63 @@ class PolymarketGammaClient:
95
173
  response.raise_for_status()
96
174
  return [GammaMarket(**market) for market in response.json()]
97
175
 
98
- def get_market(self, market_id: str) -> GammaMarket:
99
- """Get a GammaMarket by market_id."""
100
- response = self.client.get(self._build_url(f"/markets/{market_id}"))
176
+ def get_market_by_id(
177
+ self, market_id: str, include_tag: Optional[bool] = None
178
+ ) -> GammaMarket:
179
+ params = {}
180
+ if include_tag:
181
+ params["include_tag"] = include_tag
182
+ response = self.client.get(
183
+ self._build_url(f"/markets/{market_id}"), params=params
184
+ )
185
+ response.raise_for_status()
186
+ return GammaMarket(**response.json())
187
+
188
+ def get_market_tags(self, market_id: str) -> list[Tag]:
189
+ response = self.client.get(self._build_url(f"/markets/{market_id}/tags"))
190
+ response.raise_for_status()
191
+ return [Tag(**tag) for tag in response.json()]
192
+
193
+ def get_market_by_slug(
194
+ self, slug: str, include_tag: Optional[bool] = None
195
+ ) -> GammaMarket:
196
+ params = {}
197
+ if include_tag:
198
+ params["include_tag"] = include_tag
199
+ response = self.client.get(
200
+ self._build_url(f"/markets/slug/{slug}"), params=params
201
+ )
101
202
  response.raise_for_status()
102
203
  return GammaMarket(**response.json())
103
204
 
104
205
  def get_events(
105
- self,
106
- limit: Optional[int] = None,
107
- offset: Optional[int] = None,
108
- order: Optional[str] = None,
109
- ascending: bool = True,
110
- event_ids: Optional[Union[str, list[str]]] = None,
111
- slugs: Optional[list[str]] = None,
112
- archived: Optional[bool] = None,
113
- active: Optional[bool] = None,
114
- closed: Optional[bool] = None,
115
- liquidity_min: Optional[float] = None,
116
- liquidity_max: Optional[float] = None,
117
- volume_min: Optional[float] = None,
118
- volume_max: Optional[float] = None,
119
- start_date_min: Optional[datetime] = None,
120
- start_date_max: Optional[datetime] = None,
121
- end_date_min: Optional[datetime] = None,
122
- end_date_max: Optional[datetime] = None,
123
- tag: Optional[str] = None,
124
- tag_id: Optional[int] = None,
125
- tag_slug: Optional[str] = None,
126
- related_tags: bool = False,
206
+ self,
207
+ limit: int = 500,
208
+ offset: int = 0,
209
+ order: Optional[str] = None,
210
+ ascending: bool = True,
211
+ event_ids: Optional[Union[str, list[str]]] = None,
212
+ slugs: Optional[list[str]] = None,
213
+ archived: Optional[bool] = None,
214
+ active: Optional[bool] = None,
215
+ closed: Optional[bool] = None,
216
+ liquidity_min: Optional[float] = None,
217
+ liquidity_max: Optional[float] = None,
218
+ volume_min: Optional[float] = None,
219
+ volume_max: Optional[float] = None,
220
+ start_date_min: Optional[datetime] = None,
221
+ start_date_max: Optional[datetime] = None,
222
+ end_date_min: Optional[datetime] = None,
223
+ end_date_max: Optional[datetime] = None,
224
+ tag: Optional[str] = None,
225
+ tag_id: Optional[int] = None,
226
+ tag_slug: Optional[str] = None,
227
+ related_tags: bool = False,
127
228
  ) -> list[Event]:
128
- params = {}
129
- if limit:
130
- params["limit"] = limit
131
- if offset:
132
- params["offset"] = offset
229
+ params: dict[str, int | str | list[str] | float] = {
230
+ "limit": limit,
231
+ "offset": offset,
232
+ }
133
233
  if order:
134
234
  params["order"] = order
135
235
  params["ascending"] = ascending
@@ -172,39 +272,33 @@ class PolymarketGammaClient:
172
272
  response.raise_for_status()
173
273
  return [Event(**event) for event in response.json()]
174
274
 
175
- def get_event(self, event_id: int) -> Event:
176
- response = self.client.get(self._build_url(f"/events/{event_id}"))
177
- response.raise_for_status()
178
- return Event(**response.json())
179
-
180
275
  def get_all_events(
181
- self,
182
- order: Optional[str] = None,
183
- ascending: bool = True,
184
- event_ids: Optional[Union[str, list[str]]] = None,
185
- slugs: Optional[list[str]] = None,
186
- archived: Optional[bool] = None,
187
- active: Optional[bool] = None,
188
- closed: Optional[bool] = None,
189
- liquidity_min: Optional[float] = None,
190
- liquidity_max: Optional[float] = None,
191
- volume_min: Optional[float] = None,
192
- volume_max: Optional[float] = None,
193
- start_date_min: Optional[datetime] = None,
194
- start_date_max: Optional[datetime] = None,
195
- end_date_min: Optional[datetime] = None,
196
- end_date_max: Optional[datetime] = None,
197
- tag: Optional[str] = None,
198
- tag_id: Optional[int] = None,
199
- tag_slug: Optional[str] = None,
200
- related_tags: bool = False,
276
+ self,
277
+ order: Optional[str] = None,
278
+ ascending: bool = True,
279
+ event_ids: Optional[Union[str, list[str]]] = None,
280
+ slugs: Optional[list[str]] = None,
281
+ archived: Optional[bool] = None,
282
+ active: Optional[bool] = None,
283
+ closed: Optional[bool] = None,
284
+ liquidity_min: Optional[float] = None,
285
+ liquidity_max: Optional[float] = None,
286
+ volume_min: Optional[float] = None,
287
+ volume_max: Optional[float] = None,
288
+ start_date_min: Optional[datetime] = None,
289
+ start_date_max: Optional[datetime] = None,
290
+ end_date_min: Optional[datetime] = None,
291
+ end_date_max: Optional[datetime] = None,
292
+ tag: Optional[str] = None,
293
+ tag_id: Optional[int] = None,
294
+ tag_slug: Optional[str] = None,
295
+ related_tags: bool = False,
201
296
  ) -> list[Event]:
202
297
  offset = 0
203
298
  events = []
204
299
 
205
300
  while True:
206
301
  part = self.get_events(
207
- limit=500,
208
302
  offset=offset,
209
303
  order=order,
210
304
  ascending=ascending,
@@ -235,27 +329,419 @@ class PolymarketGammaClient:
235
329
 
236
330
  return events
237
331
 
238
- def search_events(
239
- self,
240
- query: str,
241
- active: bool = True,
242
- status: Optional[Literal["active", "resolved"]] = "active",
243
- sort: Literal["volume", "volume_24hr", "liquidity", "start_date", "end_date", "competitive"] = "volume_24hr",
244
- page: int = 1,
245
- limit_per_type: int = 50, # max is 50
246
- presets: Optional[Literal["EventsHybrid", "EventsTitle"] | list[Literal["EventsHybrid", "EventsTitle"]]] = None,
247
- ) -> EventList:
248
- """Search for events by query. Should emulate the website search function."""
249
- params = {"q": query,"page": page, "limit_per_type": limit_per_type, "events_status": status, "active": active}
250
- if sort:
251
- params["sort"] = sort
252
- if sort == "end_date":
253
- params["ascending"] = "true"
254
- if presets:
255
- params["presets"] = presets
256
- response = self.client.get(self._build_url("/public-search"), params=params)
332
+ def get_event_by_id(
333
+ self,
334
+ event_id: int,
335
+ include_chat: Optional[bool] = None,
336
+ include_template: Optional[bool] = None,
337
+ ) -> Event:
338
+ params = {}
339
+ if include_chat:
340
+ params["include_chat"] = include_chat
341
+ if include_template:
342
+ params["include_template"] = include_template
343
+ response = self.client.get(
344
+ self._build_url(f"/events/{event_id}"), params=params
345
+ )
346
+ response.raise_for_status()
347
+ return Event(**response.json())
348
+
349
+ def get_event_by_slug(
350
+ self,
351
+ slug: str,
352
+ include_chat: Optional[bool] = None,
353
+ include_template: Optional[bool] = None,
354
+ ) -> Event:
355
+ params = {}
356
+ if include_chat:
357
+ params["include_chat"] = include_chat
358
+ if include_template:
359
+ params["include_template"] = include_template
360
+ response = self.client.get(
361
+ self._build_url(f"/events/slug/{slug}"), params=params
362
+ )
363
+ response.raise_for_status()
364
+ return Event(**response.json())
365
+
366
+ def get_event_tags(self, event_id: int) -> list[Tag]:
367
+ response = self.client.get(self._build_url(f"/events/{event_id}/tags"))
368
+ response.raise_for_status()
369
+ return [Tag(**tag) for tag in response.json()]
370
+
371
+ def get_teams(
372
+ self,
373
+ limit: int = 500,
374
+ offset: int = 0,
375
+ order: Optional[
376
+ Literal[
377
+ "id",
378
+ "name",
379
+ "league",
380
+ "record",
381
+ "logo",
382
+ "abbreviation",
383
+ "alias",
384
+ "createdAt",
385
+ "updatedAt",
386
+ ]
387
+ ] = None,
388
+ ascending: bool = True,
389
+ league: Optional[str] = None,
390
+ name: Optional[str] = None,
391
+ abbreviation: Optional[str] = None,
392
+ ) -> list[Team]:
393
+ params: dict[str, int | str] = {
394
+ "limit": limit,
395
+ "offset": offset,
396
+ }
397
+ if order:
398
+ params["order"] = order
399
+ params["ascending"] = str(ascending).lower()
400
+ if league:
401
+ params["league"] = league.lower()
402
+ if name:
403
+ params["name"] = name
404
+ if abbreviation:
405
+ params["abbreviation"] = abbreviation.lower()
406
+ response = self.client.get(self._build_url("/teams"), params=params)
407
+ response.raise_for_status()
408
+ return [Team(**team) for team in response.json()]
409
+
410
+ def get_all_teams(
411
+ self,
412
+ order: Optional[
413
+ Literal[
414
+ "id",
415
+ "name",
416
+ "league",
417
+ "record",
418
+ "logo",
419
+ "abbreviation",
420
+ "alias",
421
+ "createdAt",
422
+ "updatedAt",
423
+ ]
424
+ ] = None,
425
+ ascending: bool = True,
426
+ league: Optional[str] = None,
427
+ name: Optional[str] = None,
428
+ abbreviation: Optional[str] = None,
429
+ ) -> list[Team]:
430
+ offset = 0
431
+ teams = []
432
+
433
+ while True:
434
+ part = self.get_teams(
435
+ offset=offset,
436
+ order=order,
437
+ ascending=ascending,
438
+ league=league,
439
+ name=name,
440
+ abbreviation=abbreviation,
441
+ )
442
+ teams.extend(part)
443
+
444
+ if len(part) < 500:
445
+ break
446
+
447
+ offset += 500
448
+
449
+ return teams
450
+
451
+ def get_sports_metadata(
452
+ self,
453
+ ) -> list[Sport]:
454
+ response = self.client.get(self._build_url("/sports"))
455
+ response.raise_for_status()
456
+ return [Sport(**sport) for sport in response.json()]
457
+
458
+ def get_tags(
459
+ self,
460
+ limit: int = 300,
461
+ offset: int = 0,
462
+ order: Optional[
463
+ Literal[
464
+ "id",
465
+ "label",
466
+ "slug",
467
+ "forceShow",
468
+ "forceHide",
469
+ "isCarousel",
470
+ "createdAt",
471
+ "updatedAt",
472
+ "createdBy",
473
+ "updatedBy",
474
+ ]
475
+ ] = None,
476
+ ascending: bool = True,
477
+ include_templates: Optional[bool] = None,
478
+ is_carousel: Optional[bool] = None,
479
+ ) -> list[Tag]:
480
+ params: dict[str, int | str] = {
481
+ "limit": limit,
482
+ "offset": offset,
483
+ }
484
+ if order:
485
+ params["order"] = order
486
+ params["ascending"] = str(ascending).lower()
487
+ if include_templates is not None:
488
+ params["include_templates"] = str(include_templates).lower()
489
+ if is_carousel is not None:
490
+ params["is_carousel"] = str(is_carousel).lower()
491
+ response = self.client.get(self._build_url("/tags"), params=params)
492
+ response.raise_for_status()
493
+ return [Tag(**tag) for tag in response.json()]
494
+
495
+ def get_all_tags(
496
+ self,
497
+ order: Optional[
498
+ Literal[
499
+ "id",
500
+ "label",
501
+ "slug",
502
+ "forceShow",
503
+ "forceHide",
504
+ "isCarousel",
505
+ "createdAt",
506
+ "updatedAt",
507
+ "createdBy",
508
+ "updatedBy",
509
+ ]
510
+ ] = None,
511
+ ascending: bool = True,
512
+ include_templates: Optional[bool] = None,
513
+ is_carousel: Optional[bool] = None,
514
+ ) -> list[Tag]:
515
+ offset = 0
516
+ tags = []
517
+
518
+ while True:
519
+ part = self.get_tags(
520
+ offset=offset,
521
+ order=order,
522
+ ascending=ascending,
523
+ include_templates=include_templates,
524
+ is_carousel=is_carousel,
525
+ )
526
+ tags.extend(part)
527
+
528
+ if len(part) < 300:
529
+ break
530
+
531
+ offset += 300
532
+
533
+ return tags
534
+
535
+ def get_tag(self, tag_id: str, include_template: Optional[bool] = None) -> Tag:
536
+ params = {}
537
+ if include_template is not None:
538
+ params = {"include_template": str(include_template).lower()}
539
+ response = self.client.get(self._build_url(f"/tags/{tag_id}"), params=params)
540
+ response.raise_for_status()
541
+ return Tag(**response.json())
542
+
543
+ def get_related_tag_ids_by_tag_id(
544
+ self,
545
+ tag_id: int,
546
+ omit_empty: Optional[bool] = None,
547
+ status: Optional[Literal["active", "closed", "all"]] = None,
548
+ ) -> list[TagRelation]:
549
+ params = {}
550
+ if omit_empty is not None:
551
+ params["omit_empty"] = str(omit_empty).lower()
552
+ if status:
553
+ params["status"] = status
554
+ response = self.client.get(
555
+ self._build_url(f"/tags/{tag_id}/related-tags"), params=params
556
+ )
557
+ response.raise_for_status()
558
+ return [TagRelation(**tag) for tag in response.json()]
559
+
560
+ def get_related_tag_ids_by_slug(
561
+ self,
562
+ slug: str,
563
+ omit_empty: Optional[bool] = None,
564
+ status: Optional[Literal["active", "closed", "all"]] = None,
565
+ ) -> list[TagRelation]:
566
+ params = {}
567
+ if omit_empty is not None:
568
+ params["omit_empty"] = str(omit_empty).lower()
569
+ if status:
570
+ params["status"] = status
571
+ response = self.client.get(
572
+ self._build_url(f"/tags/slug/{slug}/related-tags"), params=params
573
+ )
574
+ response.raise_for_status()
575
+ return [TagRelation(**tag) for tag in response.json()]
576
+
577
+ def get_related_tags_by_tag_id(
578
+ self,
579
+ tag_id: int,
580
+ omit_empty: Optional[bool] = None,
581
+ status: Optional[Literal["active", "closed", "all"]] = None,
582
+ ) -> list[Tag]:
583
+ params = {}
584
+ if omit_empty is not None:
585
+ params["omit_empty"] = str(omit_empty).lower()
586
+ if status:
587
+ params["status"] = status
588
+ response = self.client.get(
589
+ self._build_url(f"/tags/{tag_id}/related-tags/tags"), params=params
590
+ )
591
+ response.raise_for_status()
592
+ return [Tag(**tag) for tag in response.json()]
593
+
594
+ def get_related_tags_by_slug(
595
+ self,
596
+ slug: str,
597
+ omit_empty: Optional[bool] = None,
598
+ status: Optional[Literal["active", "closed", "all"]] = None,
599
+ ) -> list[Tag]:
600
+ params = {}
601
+ if omit_empty is not None:
602
+ params["omit_empty"] = str(omit_empty).lower()
603
+ if status:
604
+ params["status"] = status
605
+ response = self.client.get(
606
+ self._build_url(f"/tags/slug/{slug}/related-tags/tags"), params=params
607
+ )
608
+ response.raise_for_status()
609
+ return [Tag(**tag) for tag in response.json()]
610
+
611
+ def get_series(
612
+ self,
613
+ limit: int = 300,
614
+ offset: int = 0,
615
+ order: Optional[str] = None,
616
+ ascending: bool = True,
617
+ slug: Optional[str] = None,
618
+ closed: Optional[bool] = None,
619
+ include_chat: Optional[bool] = None,
620
+ recurrence: Optional[
621
+ Literal[
622
+ "hourly", "daily", "weekly", "monthly", "annual"
623
+ ] # results also contain "15m" but the server returns a 422 Unprocessable Content
624
+ ] = None,
625
+ ) -> list[Series]:
626
+ params: dict[str, str | int | list[int]] = {
627
+ "limit": limit,
628
+ "offset": offset,
629
+ }
630
+ if order:
631
+ params["order"] = order
632
+ params["ascending"] = str(ascending).lower()
633
+ if slug:
634
+ params["slug"] = slug
635
+ if closed is not None:
636
+ params["closed"] = str(closed).lower()
637
+ if include_chat is not None:
638
+ params["include_chat"] = str(include_chat).lower()
639
+ if recurrence is not None:
640
+ params["recurrence"] = str(recurrence).lower()
641
+
642
+ response = self.client.get(self._build_url("/series"), params=params)
643
+ response.raise_for_status()
644
+ return [Series(**series) for series in response.json()]
645
+
646
+ def get_all_series(
647
+ self,
648
+ order: Optional[str] = None,
649
+ ascending: bool = True,
650
+ slug: Optional[str] = None,
651
+ closed: Optional[bool] = None,
652
+ include_chat: Optional[bool] = None,
653
+ recurrence: Optional[
654
+ Literal["hourly", "daily", "weekly", "monthly", "annual"]
655
+ ] = None,
656
+ ) -> list[Series]:
657
+ offset = 0
658
+ series = []
659
+
660
+ while True:
661
+ part = self.get_series(
662
+ offset=offset,
663
+ order=order,
664
+ ascending=ascending,
665
+ slug=slug,
666
+ closed=closed,
667
+ include_chat=include_chat,
668
+ recurrence=recurrence,
669
+ )
670
+ series.extend(part)
671
+
672
+ if len(part) < 300:
673
+ break
674
+
675
+ offset += 300
676
+ return series
677
+
678
+ def get_series_by_id(self, series_id: str) -> Series:
679
+ response = self.client.get(self._build_url(f"/series/{series_id}"))
680
+ response.raise_for_status()
681
+ return Series(**response.json())
682
+
683
+ def get_comments(
684
+ self,
685
+ parent_entity_type: Literal["Event", "Series", "market"],
686
+ parent_entity_id: int,
687
+ limit=500,
688
+ offset=0,
689
+ order: Optional[str] = None,
690
+ ascending: bool = True,
691
+ get_positions: Optional[bool] = None,
692
+ holders_only: Optional[bool] = None,
693
+ ) -> list[Comment]:
694
+ """Warning, the server doesn't give back the right amount of comments you asked for."""
695
+ params: dict[str, str | int] = {
696
+ "parent_entity_type": parent_entity_type,
697
+ "parent_entity_id": parent_entity_id,
698
+ "limit": limit,
699
+ "offset": offset,
700
+ }
701
+ if order:
702
+ params["order"] = order
703
+ params["ascending"] = str(ascending).lower()
704
+ if get_positions is not None:
705
+ params["get_positions"] = str(get_positions).lower()
706
+ if holders_only is not None:
707
+ params["holders_only"] = str(holders_only).lower()
708
+ response = self.client.get(self._build_url("/comments"), params=params)
709
+ response.raise_for_status()
710
+ return [Comment(**comment) for comment in response.json()]
711
+
712
+ def get_comments_by_id(
713
+ self, comment_id: str, get_positions: Optional[bool] = None
714
+ ) -> list[Comment]:
715
+ """Returns all comments that belong to the comment's thread."""
716
+ params = {}
717
+ if get_positions is not None:
718
+ params["get_positions"] = str(get_positions).lower()
719
+ response = self.client.get(
720
+ self._build_url(f"/comments/{comment_id}"), params=params
721
+ )
722
+ response.raise_for_status()
723
+ return [Comment(**comment) for comment in response.json()]
724
+
725
+ def get_comments_by_user_address(
726
+ self,
727
+ user_address: EthAddress, # warning, this is the base address, not the proxy address
728
+ limit=500,
729
+ offset=0,
730
+ order: Optional[str] = None,
731
+ ascending: bool = True,
732
+ ) -> list[Comment]:
733
+ params: dict[str, str | int] = {
734
+ "limit": limit,
735
+ "offset": offset,
736
+ }
737
+ if order:
738
+ params["order"] = order
739
+ params["ascending"] = str(ascending).lower()
740
+ response = self.client.get(
741
+ self._build_url(f"/comments/user_address/{user_address}"), params=params
742
+ )
257
743
  response.raise_for_status()
258
- return EventList(**response.json())
744
+ return [Comment(**comment) for comment in response.json()]
259
745
 
260
746
  def grok_event_summary(self, event_slug: str):
261
747
  json_payload = {
@@ -302,7 +788,9 @@ class PolymarketGammaClient:
302
788
  for source in citations:
303
789
  print(f"- {source.get('url', 'Unknown URL')}")
304
790
 
305
- def grok_election_market_explanation(self, candidate_name: str, election_title: str):
791
+ def grok_election_market_explanation(
792
+ self, candidate_name: str, election_title: str
793
+ ):
306
794
  text = f"Provide candidate information for {candidate_name} in the {election_title} on Polymarket."
307
795
  json_payload = {
308
796
  "id": generate_random_id(),