getanyapi 0.43.0__tar.gz → 0.45.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 (102) hide show
  1. {getanyapi-0.43.0 → getanyapi-0.45.0}/PKG-INFO +1 -1
  2. {getanyapi-0.43.0 → getanyapi-0.45.0}/pyproject.toml +1 -1
  3. {getanyapi-0.43.0 → getanyapi-0.45.0}/src/getanyapi/__init__.py +1 -1
  4. {getanyapi-0.43.0 → getanyapi-0.45.0}/src/getanyapi/platforms/amazon.py +35 -4
  5. {getanyapi-0.43.0 → getanyapi-0.45.0}/src/getanyapi/platforms/google.py +1 -5
  6. {getanyapi-0.43.0 → getanyapi-0.45.0}/src/getanyapi/platforms/instagram.py +108 -35
  7. {getanyapi-0.43.0 → getanyapi-0.45.0}/src/getanyapi/platforms/linkedin.py +5 -2
  8. {getanyapi-0.43.0 → getanyapi-0.45.0}/src/getanyapi/platforms/reddit.py +4 -1
  9. {getanyapi-0.43.0 → getanyapi-0.45.0}/src/getanyapi/platforms/twitter.py +9 -6
  10. {getanyapi-0.43.0 → getanyapi-0.45.0}/src/getanyapi/platforms/youtube.py +17 -3
  11. {getanyapi-0.43.0 → getanyapi-0.45.0}/src/getanyapi/platforms/zillow.py +29 -1
  12. {getanyapi-0.43.0 → getanyapi-0.45.0}/.gitignore +0 -0
  13. {getanyapi-0.43.0 → getanyapi-0.45.0}/README.md +0 -0
  14. {getanyapi-0.43.0 → getanyapi-0.45.0}/src/getanyapi/_account.py +0 -0
  15. {getanyapi-0.43.0 → getanyapi-0.45.0}/src/getanyapi/_async_client.py +0 -0
  16. {getanyapi-0.43.0 → getanyapi-0.45.0}/src/getanyapi/_client.py +0 -0
  17. {getanyapi-0.43.0 → getanyapi-0.45.0}/src/getanyapi/_errors.py +0 -0
  18. {getanyapi-0.43.0 → getanyapi-0.45.0}/src/getanyapi/_idempotency.py +0 -0
  19. {getanyapi-0.43.0 → getanyapi-0.45.0}/src/getanyapi/_pagination.py +0 -0
  20. {getanyapi-0.43.0 → getanyapi-0.45.0}/src/getanyapi/_transport.py +0 -0
  21. {getanyapi-0.43.0 → getanyapi-0.45.0}/src/getanyapi/discovery_types.py +0 -0
  22. {getanyapi-0.43.0 → getanyapi-0.45.0}/src/getanyapi/platforms/__init__.py +0 -0
  23. {getanyapi-0.43.0 → getanyapi-0.45.0}/src/getanyapi/platforms/ahrefs.py +0 -0
  24. {getanyapi-0.43.0 → getanyapi-0.45.0}/src/getanyapi/platforms/airbnb.py +0 -0
  25. {getanyapi-0.43.0 → getanyapi-0.45.0}/src/getanyapi/platforms/alibaba.py +0 -0
  26. {getanyapi-0.43.0 → getanyapi-0.45.0}/src/getanyapi/platforms/apollo.py +0 -0
  27. {getanyapi-0.43.0 → getanyapi-0.45.0}/src/getanyapi/platforms/appstore.py +0 -0
  28. {getanyapi-0.43.0 → getanyapi-0.45.0}/src/getanyapi/platforms/bluesky.py +0 -0
  29. {getanyapi-0.43.0 → getanyapi-0.45.0}/src/getanyapi/platforms/booking.py +0 -0
  30. {getanyapi-0.43.0 → getanyapi-0.45.0}/src/getanyapi/platforms/capterra.py +0 -0
  31. {getanyapi-0.43.0 → getanyapi-0.45.0}/src/getanyapi/platforms/chatgpt.py +0 -0
  32. {getanyapi-0.43.0 → getanyapi-0.45.0}/src/getanyapi/platforms/coinmarketcap.py +0 -0
  33. {getanyapi-0.43.0 → getanyapi-0.45.0}/src/getanyapi/platforms/company.py +0 -0
  34. {getanyapi-0.43.0 → getanyapi-0.45.0}/src/getanyapi/platforms/company_enrichment.py +0 -0
  35. {getanyapi-0.43.0 → getanyapi-0.45.0}/src/getanyapi/platforms/company_search.py +0 -0
  36. {getanyapi-0.43.0 → getanyapi-0.45.0}/src/getanyapi/platforms/congress.py +0 -0
  37. {getanyapi-0.43.0 → getanyapi-0.45.0}/src/getanyapi/platforms/dexscreener.py +0 -0
  38. {getanyapi-0.43.0 → getanyapi-0.45.0}/src/getanyapi/platforms/douyin.py +0 -0
  39. {getanyapi-0.43.0 → getanyapi-0.45.0}/src/getanyapi/platforms/ebay.py +0 -0
  40. {getanyapi-0.43.0 → getanyapi-0.45.0}/src/getanyapi/platforms/email.py +0 -0
  41. {getanyapi-0.43.0 → getanyapi-0.45.0}/src/getanyapi/platforms/email_finding.py +0 -0
  42. {getanyapi-0.43.0 → getanyapi-0.45.0}/src/getanyapi/platforms/email_verification.py +0 -0
  43. {getanyapi-0.43.0 → getanyapi-0.45.0}/src/getanyapi/platforms/facebook.py +0 -0
  44. {getanyapi-0.43.0 → getanyapi-0.45.0}/src/getanyapi/platforms/fiverr.py +0 -0
  45. {getanyapi-0.43.0 → getanyapi-0.45.0}/src/getanyapi/platforms/g2.py +0 -0
  46. {getanyapi-0.43.0 → getanyapi-0.45.0}/src/getanyapi/platforms/gemini.py +0 -0
  47. {getanyapi-0.43.0 → getanyapi-0.45.0}/src/getanyapi/platforms/github.py +0 -0
  48. {getanyapi-0.43.0 → getanyapi-0.45.0}/src/getanyapi/platforms/glassdoor.py +0 -0
  49. {getanyapi-0.43.0 → getanyapi-0.45.0}/src/getanyapi/platforms/google_ads.py +0 -0
  50. {getanyapi-0.43.0 → getanyapi-0.45.0}/src/getanyapi/platforms/google_finance.py +0 -0
  51. {getanyapi-0.43.0 → getanyapi-0.45.0}/src/getanyapi/platforms/google_shopping.py +0 -0
  52. {getanyapi-0.43.0 → getanyapi-0.45.0}/src/getanyapi/platforms/hackernews.py +0 -0
  53. {getanyapi-0.43.0 → getanyapi-0.45.0}/src/getanyapi/platforms/indeed.py +0 -0
  54. {getanyapi-0.43.0 → getanyapi-0.45.0}/src/getanyapi/platforms/job_search.py +0 -0
  55. {getanyapi-0.43.0 → getanyapi-0.45.0}/src/getanyapi/platforms/maps.py +0 -0
  56. {getanyapi-0.43.0 → getanyapi-0.45.0}/src/getanyapi/platforms/mobile_phone.py +0 -0
  57. {getanyapi-0.43.0 → getanyapi-0.45.0}/src/getanyapi/platforms/naver.py +0 -0
  58. {getanyapi-0.43.0 → getanyapi-0.45.0}/src/getanyapi/platforms/nextdoor.py +0 -0
  59. {getanyapi-0.43.0 → getanyapi-0.45.0}/src/getanyapi/platforms/people_search.py +0 -0
  60. {getanyapi-0.43.0 → getanyapi-0.45.0}/src/getanyapi/platforms/perplexity.py +0 -0
  61. {getanyapi-0.43.0 → getanyapi-0.45.0}/src/getanyapi/platforms/person.py +0 -0
  62. {getanyapi-0.43.0 → getanyapi-0.45.0}/src/getanyapi/platforms/person_enrichment.py +0 -0
  63. {getanyapi-0.43.0 → getanyapi-0.45.0}/src/getanyapi/platforms/pinterest.py +0 -0
  64. {getanyapi-0.43.0 → getanyapi-0.45.0}/src/getanyapi/platforms/playstore.py +0 -0
  65. {getanyapi-0.43.0 → getanyapi-0.45.0}/src/getanyapi/platforms/polymarket.py +0 -0
  66. {getanyapi-0.43.0 → getanyapi-0.45.0}/src/getanyapi/platforms/realtor.py +0 -0
  67. {getanyapi-0.43.0 → getanyapi-0.45.0}/src/getanyapi/platforms/redfin.py +0 -0
  68. {getanyapi-0.43.0 → getanyapi-0.45.0}/src/getanyapi/platforms/rednote.py +0 -0
  69. {getanyapi-0.43.0 → getanyapi-0.45.0}/src/getanyapi/platforms/sec.py +0 -0
  70. {getanyapi-0.43.0 → getanyapi-0.45.0}/src/getanyapi/platforms/semrush.py +0 -0
  71. {getanyapi-0.43.0 → getanyapi-0.45.0}/src/getanyapi/platforms/seo.py +0 -0
  72. {getanyapi-0.43.0 → getanyapi-0.45.0}/src/getanyapi/platforms/snapchat.py +0 -0
  73. {getanyapi-0.43.0 → getanyapi-0.45.0}/src/getanyapi/platforms/social.py +0 -0
  74. {getanyapi-0.43.0 → getanyapi-0.45.0}/src/getanyapi/platforms/spotify.py +0 -0
  75. {getanyapi-0.43.0 → getanyapi-0.45.0}/src/getanyapi/platforms/substack.py +0 -0
  76. {getanyapi-0.43.0 → getanyapi-0.45.0}/src/getanyapi/platforms/technographics.py +0 -0
  77. {getanyapi-0.43.0 → getanyapi-0.45.0}/src/getanyapi/platforms/threads.py +0 -0
  78. {getanyapi-0.43.0 → getanyapi-0.45.0}/src/getanyapi/platforms/tiktok.py +0 -0
  79. {getanyapi-0.43.0 → getanyapi-0.45.0}/src/getanyapi/platforms/tiktok_shop.py +0 -0
  80. {getanyapi-0.43.0 → getanyapi-0.45.0}/src/getanyapi/platforms/tripadvisor.py +0 -0
  81. {getanyapi-0.43.0 → getanyapi-0.45.0}/src/getanyapi/platforms/trustpilot.py +0 -0
  82. {getanyapi-0.43.0 → getanyapi-0.45.0}/src/getanyapi/platforms/truthsocial.py +0 -0
  83. {getanyapi-0.43.0 → getanyapi-0.45.0}/src/getanyapi/platforms/upwork.py +0 -0
  84. {getanyapi-0.43.0 → getanyapi-0.45.0}/src/getanyapi/platforms/walmart.py +0 -0
  85. {getanyapi-0.43.0 → getanyapi-0.45.0}/src/getanyapi/platforms/web.py +0 -0
  86. {getanyapi-0.43.0 → getanyapi-0.45.0}/src/getanyapi/platforms/weibo.py +0 -0
  87. {getanyapi-0.43.0 → getanyapi-0.45.0}/src/getanyapi/platforms/yahoo_finance.py +0 -0
  88. {getanyapi-0.43.0 → getanyapi-0.45.0}/src/getanyapi/platforms/yelp.py +0 -0
  89. {getanyapi-0.43.0 → getanyapi-0.45.0}/src/getanyapi/platforms/zhihu.py +0 -0
  90. {getanyapi-0.43.0 → getanyapi-0.45.0}/src/getanyapi/py.typed +0 -0
  91. {getanyapi-0.43.0 → getanyapi-0.45.0}/src/getanyapi/types.py +0 -0
  92. {getanyapi-0.43.0 → getanyapi-0.45.0}/tests/conftest.py +0 -0
  93. {getanyapi-0.43.0 → getanyapi-0.45.0}/tests/test_account.py +0 -0
  94. {getanyapi-0.43.0 → getanyapi-0.45.0}/tests/test_client.py +0 -0
  95. {getanyapi-0.43.0 → getanyapi-0.45.0}/tests/test_durable_requests.py +0 -0
  96. {getanyapi-0.43.0 → getanyapi-0.45.0}/tests/test_envelope.py +0 -0
  97. {getanyapi-0.43.0 → getanyapi-0.45.0}/tests/test_errors.py +0 -0
  98. {getanyapi-0.43.0 → getanyapi-0.45.0}/tests/test_fixture_roundtrip.py +0 -0
  99. {getanyapi-0.43.0 → getanyapi-0.45.0}/tests/test_fixture_sweep.py +0 -0
  100. {getanyapi-0.43.0 → getanyapi-0.45.0}/tests/test_pagination.py +0 -0
  101. {getanyapi-0.43.0 → getanyapi-0.45.0}/tests/test_transport.py +0 -0
  102. {getanyapi-0.43.0 → getanyapi-0.45.0}/uv.lock +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: getanyapi
3
- Version: 0.43.0
3
+ Version: 0.45.0
4
4
  Summary: Official typed Python SDK for AnyAPI: any API, one wallet, USD, no subscriptions.
5
5
  Project-URL: Homepage, https://getanyapi.com
6
6
  Project-URL: Documentation, https://getanyapi.com/docs
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "getanyapi"
7
- version = "0.43.0"
7
+ version = "0.45.0"
8
8
  description = "Official typed Python SDK for AnyAPI: any API, one wallet, USD, no subscriptions."
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.10"
@@ -54,7 +54,7 @@ from .types import (
54
54
  unwrap,
55
55
  )
56
56
 
57
- __version__ = "0.43.0"
57
+ __version__ = "0.45.0"
58
58
 
59
59
  __all__ = [
60
60
  # clients + top-level functions
@@ -35,6 +35,21 @@ class AmazonBestsellersInput(TypedDict, total=False):
35
35
  """Maximum number of results to return (1-20, default 20). Range: 1 to 20. Default: 20."""
36
36
  preferLatencyUnderMs: NotRequired[int]
37
37
  """Optional; omit it and routing is unchanged, with the cheapest source serving. Prefer sources whose typical response time (median over the trailing 30 days, as published on this endpoint's lane health) is under this many milliseconds; among those, the cheapest serves. This can raise your price: when the cheapest source misses the target, a faster and dearer one serves, and you are quoted and charged its price. If no source is that fast the request is still served, by whichever source offers the best speed for its price - it is never refused for being slow. Sources we have not timed are tried last. This is a preference, not a guarantee: the median describes past requests and is not a ceiling on this one, and it excludes any wait this request itself asks for. On a paginated walk it applies to the first page only: later pages stay with the source that page chose, at the price it was quoted. Minimum: 1."""
38
+ requireFields: NotRequired[
39
+ list[
40
+ Literal[
41
+ "categoryName",
42
+ "currency",
43
+ "image",
44
+ "offersCount",
45
+ "price",
46
+ "rank",
47
+ "rating",
48
+ "reviewsCount",
49
+ ]
50
+ ]
51
+ ]
52
+ """Optional; omit it and routing is unchanged, with the cheapest source serving. Name the output fields this request must be able to return, for example `offersCount` or `categoryName`, and it is served only by a source that returns every one of them. Fields you do not name are still returned whenever the serving source has them. This can raise your price: when the cheapest source cannot return a named field, a dearer source serves, and you are quoted and charged its price. A named field can still be absent on a product that genuinely lacks it. Naming a combination that no single source returns together is refused as invalid input, with no charge."""
38
53
  url: Required[str]
39
54
  """Amazon Best Sellers category URL (e.g. https://www.amazon.com/Best-Sellers-Electronics/zgbs/electronics)."""
40
55
 
@@ -137,6 +152,22 @@ class AmazonSearchInput(TypedDict, total=False):
137
152
  """Maximum number of results to return (1-20, default 20). Range: 1 to 20. Default: 20."""
138
153
  preferLatencyUnderMs: NotRequired[int]
139
154
  """Optional; omit it and routing is unchanged, with the cheapest source serving. Prefer sources whose typical response time (median over the trailing 30 days, as published on this endpoint's lane health) is under this many milliseconds; among those, the cheapest serves. This can raise your price: when the cheapest source misses the target, a faster and dearer one serves, and you are quoted and charged its price. If no source is that fast the request is still served, by whichever source offers the best speed for its price - it is never refused for being slow. Sources we have not timed are tried last. This is a preference, not a guarantee: the median describes past requests and is not a ceiling on this one, and it excludes any wait this request itself asks for. On a paginated walk it applies to the first page only: later pages stay with the source that page chose, at the price it was quoted. Minimum: 1."""
155
+ requireFields: NotRequired[
156
+ list[
157
+ Literal[
158
+ "currency",
159
+ "image",
160
+ "isSponsored",
161
+ "listPrice",
162
+ "offersCount",
163
+ "position",
164
+ "price",
165
+ "rating",
166
+ "reviewsCount",
167
+ ]
168
+ ]
169
+ ]
170
+ """Optional; omit it and routing is unchanged, with the cheapest source serving. Name the output fields this request must be able to return, for example `offersCount`, and it is served only by a source that returns every one of them. Fields you do not name are still returned whenever the serving source has them. This can raise your price: when the cheapest source cannot return a named field, a dearer source serves, and you are quoted and charged its price. A named field can still be absent on a product that genuinely lacks it. Naming a combination that no single source returns together is refused as invalid input, with no charge."""
140
171
  url: Required[str]
141
172
  """Amazon search or category URL to pull results from (e.g. https://www.amazon.com/s?k=gaming+mouse)."""
142
173
 
@@ -193,7 +224,7 @@ class AmazonBestsellersItem(BaseModel):
193
224
  category_name: str | None = Field(
194
225
  default=None,
195
226
  alias="categoryName",
196
- description="Best Sellers category name the product ranks in.",
227
+ description="Best Sellers category name the product ranks in, or null when the serving source does not publish it.",
197
228
  )
198
229
  currency: str | None = Field(
199
230
  default=None, description='Price currency symbol or code, e.g. "$".'
@@ -204,7 +235,7 @@ class AmazonBestsellersItem(BaseModel):
204
235
  offers_count: int | None = Field(
205
236
  default=None,
206
237
  alias="offersCount",
207
- description="Number of available offers; 0 when unknown.",
238
+ description="Number of available offers, or null when the serving source does not publish it.",
208
239
  )
209
240
  price: float | None = Field(
210
241
  default=None, description="Listed price; 0 when no offer is available."
@@ -367,12 +398,12 @@ class AmazonSearchItem(BaseModel):
367
398
  list_price: float | None = Field(
368
399
  default=None,
369
400
  alias="listPrice",
370
- description="Pre-discount list price when on sale; 0 when not discounted.",
401
+ description="Pre-discount list price when on sale, 0 when not discounted, or null when the serving source does not publish it.",
371
402
  )
372
403
  offers_count: int | None = Field(
373
404
  default=None,
374
405
  alias="offersCount",
375
- description="Number of available offers; 0 when unknown.",
406
+ description="Number of available offers, or null when the serving source does not publish it.",
376
407
  )
377
408
  position: int | None = Field(
378
409
  default=None, description="1-based position of the result on the search page."
@@ -3,7 +3,7 @@
3
3
 
4
4
  from __future__ import annotations
5
5
 
6
- from typing import Literal, TYPE_CHECKING
6
+ from typing import TYPE_CHECKING
7
7
 
8
8
  from pydantic import BaseModel, ConfigDict, Field
9
9
  from typing_extensions import NotRequired, Required, TypedDict, Unpack
@@ -37,10 +37,6 @@ class GoogleAiOverviewInput(TypedDict, total=False):
37
37
  """Optional; omit it and routing is unchanged, with the cheapest source serving. Prefer sources whose typical response time (median over the trailing 30 days, as published on this endpoint's lane health) is under this many milliseconds; among those, the cheapest serves. This can raise your price: when the cheapest source misses the target, a faster and dearer one serves, and you are quoted and charged its price. If no source is that fast the request is still served, by whichever source offers the best speed for its price - it is never refused for being slow. Sources we have not timed are tried last. This is a preference, not a guarantee: the median describes past requests and is not a ceiling on this one, and it excludes any wait this request itself asks for. On a paginated walk it applies to the first page only: later pages stay with the source that page chose, at the price it was quoted. Minimum: 1."""
38
38
  prompt: Required[str]
39
39
  """The question or prompt to answer with a Google AI Overview."""
40
- requireFields: NotRequired[
41
- list[Literal["citations", "index", "scrapedAt", "title", "url"]]
42
- ]
43
- """Optional; omit it and routing is unchanged, with the cheapest source serving. Name the output fields this request must be able to return, for example `citations` or `index`, and it is served only by a source that returns every one of them. Fields you do not name are still returned whenever the serving source has them. This can raise your price: when the cheapest source cannot return a named field, a dearer source serves, and you are quoted and charged its price. A named field can still be absent on a result that genuinely lacks it. Naming a combination that no single source returns together is refused as invalid input, with no charge."""
44
40
 
45
41
 
46
42
  class GoogleAutocompleteInput(TypedDict, total=False):
@@ -160,6 +160,8 @@ class InstagramMediaTranscriptInput(TypedDict, total=False):
160
160
  class InstagramPostInput(TypedDict, total=False):
161
161
  """Input for Instagram Post."""
162
162
 
163
+ hostVideo: NotRequired[bool]
164
+ """Set true to also get the post's video on a hosted MP4 link that plays without an Instagram session. The file is downloaded and stored for you, and the response adds `hostedUrl`, `expiresUtc` and `bytes`. It is charged as an extra on top of the price, and it never changes which source serves you: every source offers it at the same price. A post with no video, or a post that does not exist, is refused with no charge rather than billed for a file that cannot exist. Omit it and nothing is downloaded, nothing is stored, and nothing extra is charged. Default: false."""
163
165
  preferLatencyUnderMs: NotRequired[int]
164
166
  """Optional; omit it and routing is unchanged, with the cheapest source serving. Prefer sources whose typical response time (median over the trailing 30 days, as published on this endpoint's lane health) is under this many milliseconds; among those, the cheapest serves. This can raise your price: when the cheapest source misses the target, a faster and dearer one serves, and you are quoted and charged its price. If no source is that fast the request is still served, by whichever source offers the best speed for its price - it is never refused for being slow. Sources we have not timed are tried last. This is a preference, not a guarantee: the median describes past requests and is not a ceiling on this one, and it excludes any wait this request itself asks for. On a paginated walk it applies to the first page only: later pages stay with the source that page chose, at the price it was quoted. Minimum: 1."""
165
167
  requireFields: NotRequired[
@@ -213,10 +215,12 @@ class InstagramProfileContactInput(TypedDict, total=False):
213
215
  class InstagramReelTranscriptInput(TypedDict, total=False):
214
216
  """Input for Instagram Reel Transcript."""
215
217
 
218
+ hostVideo: NotRequired[bool]
219
+ """Set true to also get the reel's MP4 on a hosted link you can play without an Instagram session. Charged as an extra on top of the transcript (e.g. true). Default: false."""
216
220
  preferLatencyUnderMs: NotRequired[int]
217
221
  """Optional; omit it and routing is unchanged, with the cheapest source serving. Prefer sources whose typical response time (median over the trailing 30 days, as published on this endpoint's lane health) is under this many milliseconds; among those, the cheapest serves. This can raise your price: when the cheapest source misses the target, a faster and dearer one serves, and you are quoted and charged its price. If no source is that fast the request is still served, by whichever source offers the best speed for its price - it is never refused for being slow. Sources we have not timed are tried last. This is a preference, not a guarantee: the median describes past requests and is not a ceiling on this one, and it excludes any wait this request itself asks for. On a paginated walk it applies to the first page only: later pages stay with the source that page chose, at the price it was quoted. Minimum: 1."""
218
222
  url: Required[str]
219
- """The URL of a public Instagram reel or video post with spoken audio (e.g. https://www.instagram.com/reel/C8yKXdRxKqK/)."""
223
+ """The URL of a public Instagram reel or video post (e.g. https://www.instagram.com/reel/C8yKXdRxKqK/), or an Instagram CDN media URL you already hold."""
220
224
  wordTimestamps: NotRequired[bool]
221
225
  """Set true to include a precise timestamp for every word in the transcript (e.g. true). Default: false."""
222
226
 
@@ -973,10 +977,24 @@ class InstagramMediaTranscriptTranscript(BaseModel):
973
977
  class InstagramPostData(BaseModel):
974
978
  model_config = ConfigDict(extra="allow", populate_by_name=True)
975
979
 
980
+ bytes: int | None = Field(
981
+ default=None,
982
+ description="Size of the hosted MP4 in bytes. Present only when the request set `hostVideo` to true.",
983
+ )
976
984
  display_url: str = Field(
977
985
  alias="displayUrl",
978
986
  description="Populated whenever the provider has data for the entity.",
979
987
  )
988
+ expires_utc: int | None = Field(
989
+ default=None,
990
+ alias="expiresUtc",
991
+ description="When `hostedUrl` stops working, as a UTC epoch timestamp in seconds (Unix time). Multiply by 1000 for a JS Date in milliseconds. Present only when the request set `hostVideo` to true.",
992
+ )
993
+ hosted_url: str | None = Field(
994
+ default=None,
995
+ alias="hostedUrl",
996
+ description="AnyAPI-hosted MP4 of this post's video, playable without an Instagram session. Present only when the request set `hostVideo` to true.",
997
+ )
980
998
  id: str = Field(
981
999
  description="Populated whenever the provider has data for the entity."
982
1000
  )
@@ -1177,57 +1195,72 @@ class InstagramProfileContactData(BaseModel):
1177
1195
 
1178
1196
  class InstagramReelTranscriptData(BaseModel):
1179
1197
  items: list[InstagramReelTranscriptItem] = Field(
1180
- description="Transcript record for the requested reel (one item), with the full transcript text, timed segments, and source video metadata. Populated whenever the provider has data for the entity."
1198
+ description="Record for the requested reel (one item), with the full transcript text, timed segments, source video metadata, and the hosted video link when hostVideo was asked for. Populated whenever the provider has data for the entity."
1181
1199
  )
1182
1200
 
1183
1201
 
1184
1202
  class InstagramReelTranscriptItem(BaseModel):
1185
1203
  model_config = ConfigDict(extra="allow", populate_by_name=True)
1186
1204
 
1187
- caption: str | None = Field(
1205
+ bytes: int | None = Field(
1188
1206
  default=None,
1189
- description="The reel's caption text. Empty when the reel has no caption.",
1207
+ description="Size of the hosted MP4 in bytes. Present only when hostVideo was true.",
1190
1208
  )
1191
- comment_count: int | None = Field(
1209
+ duration_seconds: float | None = Field(
1192
1210
  default=None,
1193
- alias="commentCount",
1194
- description="Number of comments on the reel.",
1211
+ alias="durationSeconds",
1212
+ description="Video duration in seconds. Absent when there is no transcript, which is what measures it.",
1195
1213
  )
1196
- created_utc: float | None = Field(
1214
+ expires_utc: float | None = Field(
1197
1215
  default=None,
1198
- alias="createdUtc",
1199
- description="UTC epoch timestamp in seconds (Unix time). Multiply by 1000 for a JS Date in milliseconds.",
1216
+ alias="expiresUtc",
1217
+ description="UTC epoch timestamp in seconds (Unix time). Multiply by 1000 for a JS Date in milliseconds. After this moment the hosted MP4 is deleted. Present only when hostVideo was true.",
1200
1218
  )
1201
- duration_seconds: float | None = Field(
1202
- default=None, alias="durationSeconds", description="Video duration in seconds."
1219
+ hosted_url: str | None = Field(
1220
+ default=None,
1221
+ alias="hostedUrl",
1222
+ description="A direct link to the downloaded MP4, hosted by AnyAPI and playable without an Instagram session. Present only when hostVideo was true.",
1203
1223
  )
1204
1224
  id: str = Field(
1205
- description="The reel's numeric Instagram media ID, as a string. Populated whenever the provider has data for the entity."
1225
+ description="The reel's numeric Instagram media ID, as a string. Empty when the request supplied a CDN media URL, which carries no post record. Populated whenever the provider has data for the entity."
1206
1226
  )
1207
1227
  language: str | None = Field(
1208
1228
  default=None,
1209
- description='Detected spoken language (ISO 639-1 code, e.g. "en"). Empty when the upstream omits it.',
1229
+ description='Detected spoken language (ISO 639-1 code, e.g. "en"). Absent when there is no transcript.',
1210
1230
  )
1211
1231
  like_count: int | None = Field(
1212
- default=None, alias="likeCount", description="Number of likes on the reel."
1232
+ default=None,
1233
+ alias="likeCount",
1234
+ description="Number of likes on the reel. Absent when the lane that served the lookup does not carry a like count.",
1235
+ )
1236
+ media_type: str | None = Field(
1237
+ default=None,
1238
+ alias="mediaType",
1239
+ description="What kind of media this is, as Instagram labels it (for example a video or an image post).",
1213
1240
  )
1214
1241
  owner_username: str | None = Field(
1215
1242
  default=None,
1216
1243
  alias="ownerUsername",
1217
- description="Username of the reel's owner, without the @ prefix. Empty when the upstream omits it.",
1244
+ description="Username of the reel's owner, without the @ prefix. Empty when the request supplied a CDN media URL.",
1218
1245
  )
1219
1246
  segments: list[InstagramReelTranscriptSegment] | None = Field(
1220
1247
  default=None,
1221
- description="Time-aligned transcript segments, each with its text and start/end offsets in seconds.",
1248
+ description="Time-aligned transcript segments, each with its text, speaker label, and start/end offsets in seconds. Empty when the reel has no detectable spoken audio.",
1249
+ )
1250
+ shortcode: str | None = Field(
1251
+ default=None,
1252
+ description="The reel's short code, the part of its instagram.com URL after /reel/. Empty when the request supplied a CDN media URL.",
1222
1253
  )
1223
1254
  text: str = Field(
1224
1255
  description="The full speech transcript. Empty when the reel has no detectable spoken audio. Populated whenever the provider has data for the entity."
1225
1256
  )
1226
- url: str = Field(
1227
- description="Canonical URL of the reel, with tracking query params stripped. Populated whenever the provider has data for the entity."
1257
+ thumbnail_url: str | None = Field(
1258
+ default=None,
1259
+ alias="thumbnailUrl",
1260
+ description="A link to the reel's cover image on Instagram. This link is signed by Instagram and stops working after a short time.",
1228
1261
  )
1229
- view_count: int | None = Field(
1230
- default=None, alias="viewCount", description="Number of video views."
1262
+ url: str = Field(
1263
+ description="The reel URL the request asked for, returned as sent. Populated whenever the provider has data for the entity."
1231
1264
  )
1232
1265
 
1233
1266
 
@@ -1238,6 +1271,10 @@ class InstagramReelTranscriptSegment(BaseModel):
1238
1271
  default=None,
1239
1272
  description="Segment end offset in seconds from the start of the video.",
1240
1273
  )
1274
+ speaker: str | None = Field(
1275
+ default=None,
1276
+ description='Which speaker said this segment, as a stable label within this transcript (e.g. "0", "1").',
1277
+ )
1241
1278
  start: float | None = Field(
1242
1279
  default=None,
1243
1280
  description="Segment start offset in seconds from the start of the video.",
@@ -1245,6 +1282,24 @@ class InstagramReelTranscriptSegment(BaseModel):
1245
1282
  text: str | None = Field(
1246
1283
  default=None, description="The segment's transcribed text."
1247
1284
  )
1285
+ words: list[InstagramReelTranscriptWord] | None = Field(
1286
+ default=None,
1287
+ description="Every word in the segment with its own timing. Present only when wordTimestamps was true.",
1288
+ )
1289
+
1290
+
1291
+ class InstagramReelTranscriptWord(BaseModel):
1292
+ model_config = ConfigDict(extra="allow")
1293
+
1294
+ end: float | None = Field(
1295
+ default=None,
1296
+ description="Word end offset in seconds from the start of the video.",
1297
+ )
1298
+ start: float | None = Field(
1299
+ default=None,
1300
+ description="Word start offset in seconds from the start of the video.",
1301
+ )
1302
+ text: str | None = Field(default=None, description="The word as spoken.")
1248
1303
 
1249
1304
 
1250
1305
  class InstagramReelsSearchData(BaseModel):
@@ -2421,9 +2476,12 @@ class InstagramNamespace:
2421
2476
  """Instagram Post
2422
2477
 
2423
2478
  Fetch a single Instagram post or reel by URL (media URLs, like count, owner,
2424
- type) as normalized JSON.
2479
+ type) as normalized JSON. Turn on hostVideo to also get the post's video on
2480
+ a hosted MP4 link that plays without an Instagram session, charged as an
2481
+ extra on top of the price. If you want the spoken words as well,
2482
+ instagram.reel_transcript transcribes the same reel.
2425
2483
 
2426
- Price: $0.0012 per request.
2484
+ Price: $0.0005 per request.
2427
2485
 
2428
2486
  Example:
2429
2487
  res = client.instagram.post(url="https://www.instagram.com/reel/DWzrfE2kaY8/")
@@ -2509,7 +2567,7 @@ class InstagramNamespace:
2509
2567
  Fetch an Instagram account's public profile (followers, posts, bio,
2510
2568
  verification) by handle.
2511
2569
 
2512
- Price: $0.0005 per request.
2570
+ Price: $0.0012 per request.
2513
2571
 
2514
2572
  Example:
2515
2573
  res = client.instagram.profile(handle="nasa")
@@ -2549,13 +2607,19 @@ class InstagramNamespace:
2549
2607
  ) -> RunResult[InstagramReelTranscriptData]:
2550
2608
  """Instagram Reel Transcript
2551
2609
 
2552
- Turn any public Instagram reel or video post into a full speech transcript,
2553
- with optional word-level timestamps.
2610
+ Transcribe any public Instagram reel or video post: the full speech
2611
+ transcript, speaker labels, and word-level timestamps, from a reel URL or an
2612
+ Instagram CDN media URL you already hold. Transcription runs on
2613
+ MAI-Transcribe-2, chosen for its accuracy and its speaker labels. Turn on
2614
+ hostVideo to also get the MP4 on a hosted link that plays without an
2615
+ Instagram session. If you only want the text, instagram.media_transcript is
2616
+ the cheaper transcript-only option; if you only want the file,
2617
+ instagram.post is where you go.
2554
2618
 
2555
- Price: $0.0055 per request plus $0.0253 per result (maximum $0.0308).
2619
+ Price: $0.0015 per request plus $0.006 per audio minute (maximum $0.095).
2556
2620
 
2557
2621
  Example:
2558
- res = client.instagram.reel_transcript(url="https://www.instagram.com/reel/DWzrfE2kaY8/", wordTimestamps=False)
2622
+ res = client.instagram.reel_transcript(hostVideo=True, url="https://www.instagram.com/reel/CfY6jCIgH-P/", wordTimestamps=False)
2559
2623
  """
2560
2624
  raw = self._client._run_raw( # pyright: ignore[reportPrivateUsage]
2561
2625
  "instagram.reel_transcript", dict(input), options
@@ -3439,9 +3503,12 @@ class AsyncInstagramNamespace:
3439
3503
  """Instagram Post
3440
3504
 
3441
3505
  Fetch a single Instagram post or reel by URL (media URLs, like count, owner,
3442
- type) as normalized JSON.
3506
+ type) as normalized JSON. Turn on hostVideo to also get the post's video on
3507
+ a hosted MP4 link that plays without an Instagram session, charged as an
3508
+ extra on top of the price. If you want the spoken words as well,
3509
+ instagram.reel_transcript transcribes the same reel.
3443
3510
 
3444
- Price: $0.0012 per request.
3511
+ Price: $0.0005 per request.
3445
3512
 
3446
3513
  Example:
3447
3514
  res = client.instagram.post(url="https://www.instagram.com/reel/DWzrfE2kaY8/")
@@ -3527,7 +3594,7 @@ class AsyncInstagramNamespace:
3527
3594
  Fetch an Instagram account's public profile (followers, posts, bio,
3528
3595
  verification) by handle.
3529
3596
 
3530
- Price: $0.0005 per request.
3597
+ Price: $0.0012 per request.
3531
3598
 
3532
3599
  Example:
3533
3600
  res = client.instagram.profile(handle="nasa")
@@ -3567,13 +3634,19 @@ class AsyncInstagramNamespace:
3567
3634
  ) -> RunResult[InstagramReelTranscriptData]:
3568
3635
  """Instagram Reel Transcript
3569
3636
 
3570
- Turn any public Instagram reel or video post into a full speech transcript,
3571
- with optional word-level timestamps.
3637
+ Transcribe any public Instagram reel or video post: the full speech
3638
+ transcript, speaker labels, and word-level timestamps, from a reel URL or an
3639
+ Instagram CDN media URL you already hold. Transcription runs on
3640
+ MAI-Transcribe-2, chosen for its accuracy and its speaker labels. Turn on
3641
+ hostVideo to also get the MP4 on a hosted link that plays without an
3642
+ Instagram session. If you only want the text, instagram.media_transcript is
3643
+ the cheaper transcript-only option; if you only want the file,
3644
+ instagram.post is where you go.
3572
3645
 
3573
- Price: $0.0055 per request plus $0.0253 per result (maximum $0.0308).
3646
+ Price: $0.0015 per request plus $0.006 per audio minute (maximum $0.095).
3574
3647
 
3575
3648
  Example:
3576
- res = client.instagram.reel_transcript(url="https://www.instagram.com/reel/DWzrfE2kaY8/", wordTimestamps=False)
3649
+ res = client.instagram.reel_transcript(hostVideo=True, url="https://www.instagram.com/reel/CfY6jCIgH-P/", wordTimestamps=False)
3577
3650
  """
3578
3651
  raw = await self._client._arun_raw( # pyright: ignore[reportPrivateUsage]
3579
3652
  "instagram.reel_transcript", dict(input), options
@@ -2817,7 +2817,8 @@ class LinkedinProfileThinData(BaseModel):
2817
2817
  default=None, description="About/summary text of the profile."
2818
2818
  )
2819
2819
  articles: list[LinkedinProfileThinArticle] | None = Field(
2820
- default=None, description="The profile's published articles."
2820
+ default=None,
2821
+ description="The profile's published articles, or null when the serving source does not publish them.",
2821
2822
  )
2822
2823
  avatar_url: str | None = Field(
2823
2824
  default=None, alias="avatarUrl", description="URL of the profile avatar image."
@@ -2835,7 +2836,9 @@ class LinkedinProfileThinData(BaseModel):
2835
2836
  )
2836
2837
  name: str = Field(description="Full name of the profile owner.")
2837
2838
  recent_posts: list[LinkedinProfileThinRecentPost] | None = Field(
2838
- default=None, alias="recentPosts", description="The profile's recent posts."
2839
+ default=None,
2840
+ alias="recentPosts",
2841
+ description="The profile's recent posts, or null when the serving source does not publish them.",
2839
2842
  )
2840
2843
 
2841
2844
 
@@ -826,7 +826,10 @@ class RedditUserPostsMedia(BaseModel):
826
826
  model_config = ConfigDict(extra="allow", populate_by_name=True)
827
827
 
828
828
  height: int | None = None
829
- type_: str = Field(alias="type", description="One of photo, video, or gif.")
829
+ type_: str | None = Field(
830
+ alias="type",
831
+ description="One of photo, video, or gif, or null when the serving source wraps a thumbnail it cannot classify.",
832
+ )
830
833
  url: str = Field(
831
834
  description="Image URL. For a video or GIF this is the poster/thumbnail frame."
832
835
  )
@@ -249,7 +249,7 @@ class TwitterUserPostsInput(TypedDict, total=False):
249
249
  ]
250
250
  ]
251
251
  ]
252
- """Optional; omit it and routing is unchanged, with the cheapest source serving. Name the output fields this request must be able to return, for example `isReply`, and it is served only by a source that returns every one of them. Fields you do not name are still returned whenever the serving source has them. This can raise your price: when the cheapest source cannot return a named field, a dearer source serves, and you are quoted and charged its price. A named field can still be absent on a tweet that genuinely lacks it. Naming a combination that no single source returns together is refused as invalid input, with no charge. On a paginated walk it applies to the first page only; later pages stay with the source that page chose, at the price it was quoted."""
252
+ """Optional; omit it and routing is unchanged, with the cheapest source serving. Name the output fields this request must be able to return, for example `isPinned` or `isReply`, and it is served only by a source that returns every one of them. Fields you do not name are still returned whenever the serving source has them. This can raise your price: when the cheapest source cannot return a named field, a dearer source serves, and you are quoted and charged its price. A named field can still be absent on a tweet that genuinely lacks it. Naming a combination that no single source returns together is refused as invalid input, with no charge. On a paginated walk it applies to the first page only; later pages stay with the source that page chose, at the price it was quoted."""
253
253
 
254
254
 
255
255
  class TwitterUserTweetsInput(TypedDict, total=False):
@@ -260,7 +260,7 @@ class TwitterUserTweetsInput(TypedDict, total=False):
260
260
  handle: Required[str]
261
261
  """Twitter/X handle without the leading @."""
262
262
  limit: NotRequired[int]
263
- """Maximum number of authored tweets and replies to return in the current bulk call (1-1000). The provider may return fewer results. Range: 1 to 1000. Default: 20."""
263
+ """Maximum number of authored tweets and replies to return in THIS page (1-100). Sources return fewer - most cap at 20 - so read `nextCursor` and pass it back as `cursor` to walk further rather than asking for one large page. Range: 1 to 100. Default: 20."""
264
264
  preferLatencyUnderMs: NotRequired[int]
265
265
  """Optional; omit it and routing is unchanged, with the cheapest source serving. Prefer sources whose typical response time (median over the trailing 30 days, as published on this endpoint's lane health) is under this many milliseconds; among those, the cheapest serves. This can raise your price: when the cheapest source misses the target, a faster and dearer one serves, and you are quoted and charged its price. If no source is that fast the request is still served, by whichever source offers the best speed for its price - it is never refused for being slow. Sources we have not timed are tried last. This is a preference, not a guarantee: the median describes past requests and is not a ceiling on this one, and it excludes any wait this request itself asks for. On a paginated walk it applies to the first page only: later pages stay with the source that page chose, at the price it was quoted. Minimum: 1."""
266
266
  requireFields: NotRequired[
@@ -280,7 +280,7 @@ class TwitterUserTweetsInput(TypedDict, total=False):
280
280
  ]
281
281
  ]
282
282
  ]
283
- """Optional; omit it and routing is unchanged, with the cheapest source serving. Name the output fields this request must be able to return, for example `nextCursor` or `media`, and it is served only by a source that returns every one of them. Fields you do not name are still returned whenever the serving source has them. This can raise your price: when the cheapest source cannot return a named field, a dearer source serves, and you are quoted and charged its price. A named field can still be absent on a post that genuinely lacks it. Naming a combination that no single source returns together is refused as invalid input, with no charge. On a paginated walk it applies to the first page only; later pages stay with the source that page chose, at the price it was quoted."""
283
+ """Optional; omit it and routing is unchanged, with the cheapest source serving. Name the output fields this request must be able to return, for example `isPinned` or `nextCursor`, and it is served only by a source that returns every one of them. Fields you do not name are still returned whenever the serving source has them. This can raise your price: when the cheapest source cannot return a named field, a dearer source serves, and you are quoted and charged its price. A named field can still be absent on a post that genuinely lacks it. Naming a combination that no single source returns together is refused as invalid input, with no charge. On a paginated walk it applies to the first page only; later pages stay with the source that page chose, at the price it was quoted."""
284
284
  requireSinglePage: NotRequired[bool]
285
285
  """Require a lane that can return the requested limit in one response."""
286
286
 
@@ -1030,9 +1030,9 @@ class TwitterUserPostsTweet(BaseModel):
1030
1030
  id: str = Field(
1031
1031
  description="The post's numeric tweet ID, represented as a string. Populated whenever the provider has data for the entity."
1032
1032
  )
1033
- is_pinned: bool = Field(
1033
+ is_pinned: bool | None = Field(
1034
1034
  alias="isPinned",
1035
- description="Whether X marks the post as pinned on the profile.",
1035
+ description="Whether X marks the post as pinned on the profile, or null when the serving source does not publish it.",
1036
1036
  )
1037
1037
  is_reply: bool | None = Field(
1038
1038
  default=None,
@@ -1099,7 +1099,10 @@ class TwitterUserTweetsTweet(BaseModel):
1099
1099
  id: str = Field(
1100
1100
  description="Populated whenever the provider has data for the entity."
1101
1101
  )
1102
- is_pinned: bool = Field(alias="isPinned")
1102
+ is_pinned: bool | None = Field(
1103
+ alias="isPinned",
1104
+ description="Whether X marks the post as pinned on the profile, or null when the serving source does not publish it.",
1105
+ )
1103
1106
  is_reply: bool | None = Field(default=None, alias="isReply")
1104
1107
  lang: str | None = None
1105
1108
  likes: int
@@ -248,6 +248,20 @@ class YoutubeVideoTranscriptFullInput(TypedDict, total=False):
248
248
  """Preferred caption language code (e.g. "en", "es"). Defaults to English."""
249
249
  preferLatencyUnderMs: NotRequired[int]
250
250
  """Optional; omit it and routing is unchanged, with the cheapest source serving. Prefer sources whose typical response time (median over the trailing 30 days, as published on this endpoint's lane health) is under this many milliseconds; among those, the cheapest serves. This can raise your price: when the cheapest source misses the target, a faster and dearer one serves, and you are quoted and charged its price. If no source is that fast the request is still served, by whichever source offers the best speed for its price - it is never refused for being slow. Sources we have not timed are tried last. This is a preference, not a guarantee: the median describes past requests and is not a ceiling on this one, and it excludes any wait this request itself asks for. On a paginated walk it applies to the first page only: later pages stay with the source that page chose, at the price it was quoted. Minimum: 1."""
251
+ requireFields: NotRequired[
252
+ list[
253
+ Literal[
254
+ "channel",
255
+ "durationSeconds",
256
+ "endSeconds",
257
+ "isAiGenerated",
258
+ "startSeconds",
259
+ "text",
260
+ "title",
261
+ ]
262
+ ]
263
+ ]
264
+ """Optional; omit it and routing is unchanged, with the cheapest source serving. Name the output fields this request must be able to return, for example `isAiGenerated`, and it is served only by a source that returns every one of them. Fields you do not name are still returned whenever the serving source has them. This can raise your price: when the cheapest source cannot return a named field, a dearer source serves, and you are quoted and charged its price. A named field can still be absent on a transcript that genuinely lacks it. Naming a combination that no single source returns together is refused as invalid input, with no charge."""
251
265
  url: Required[str]
252
266
  """YouTube video or Short URL (e.g. "https://www.youtube.com/watch?v=dQw4w9WgXcQ" or "https://www.youtube.com/shorts/Fir1x9cw2vg")."""
253
267
 
@@ -807,7 +821,7 @@ class YoutubeVideoTranscriptFullData(BaseModel):
807
821
  is_ai_generated: bool | None = Field(
808
822
  default=None,
809
823
  alias="isAiGenerated",
810
- description="True when the words were recognized from the audio by the serving lane rather than read from any YouTube caption track.",
824
+ description="True when the words were recognized from the audio by the serving lane rather than read from any YouTube caption track, or null when the serving source does not say.",
811
825
  )
812
826
  is_auto_generated: bool = Field(
813
827
  alias="isAutoGenerated",
@@ -855,7 +869,7 @@ class YoutubeNamespace:
855
869
  Fetch a YouTube channel's stats (subscribers, video count, total views,
856
870
  description) by handle or channel ID.
857
871
 
858
- Price: $0.0012 per request.
872
+ Price: $0.0005 per request.
859
873
 
860
874
  Example:
861
875
  res = client.youtube.channel(handle="@mkbhd")
@@ -1470,7 +1484,7 @@ class AsyncYoutubeNamespace:
1470
1484
  Fetch a YouTube channel's stats (subscribers, video count, total views,
1471
1485
  description) by handle or channel ID.
1472
1486
 
1473
- Price: $0.0012 per request.
1487
+ Price: $0.0005 per request.
1474
1488
 
1475
1489
  Example:
1476
1490
  res = client.youtube.channel(handle="@mkbhd")
@@ -96,6 +96,30 @@ class ZillowSearchInput(TypedDict, total=False):
96
96
  """Listing type: buy (for sale), rent, or sold. Default: buy."""
97
97
  preferLatencyUnderMs: NotRequired[int]
98
98
  """Optional; omit it and routing is unchanged, with the cheapest source serving. Prefer sources whose typical response time (median over the trailing 30 days, as published on this endpoint's lane health) is under this many milliseconds; among those, the cheapest serves. This can raise your price: when the cheapest source misses the target, a faster and dearer one serves, and you are quoted and charged its price. If no source is that fast the request is still served, by whichever source offers the best speed for its price - it is never refused for being slow. Sources we have not timed are tried last. This is a preference, not a guarantee: the median describes past requests and is not a ceiling on this one, and it excludes any wait this request itself asks for. On a paginated walk it applies to the first page only: later pages stay with the source that page chose, at the price it was quoted. Minimum: 1."""
99
+ requireFields: NotRequired[
100
+ list[
101
+ Literal[
102
+ "baths",
103
+ "beds",
104
+ "city",
105
+ "currency",
106
+ "daysOnZillow",
107
+ "latitude",
108
+ "livingArea",
109
+ "longitude",
110
+ "lotSize",
111
+ "price",
112
+ "propertyType",
113
+ "rentZestimate",
114
+ "state",
115
+ "status",
116
+ "yearBuilt",
117
+ "zestimate",
118
+ "zipcode",
119
+ ]
120
+ ]
121
+ ]
122
+ """Optional; omit it and routing is unchanged, with the cheapest source serving. Name the output fields this request must be able to return, for example `yearBuilt`, and it is served only by a source that returns every one of them. Fields you do not name are still returned whenever the serving source has them. This can raise your price: when the cheapest source cannot return a named field, a dearer source serves, and you are quoted and charged its price. A named field can still be absent on a listing that genuinely lacks it. Naming a combination that no single source returns together is refused as invalid input, with no charge."""
99
123
  showOnlyPriceReductions: NotRequired[bool]
100
124
  """Only show listings with a price reduction. Buy searches only; ignored for rentals (e.g. true)."""
101
125
  sortBy: NotRequired[
@@ -242,7 +266,11 @@ class ZillowSearchItem(BaseModel):
242
266
  url: str = Field(
243
267
  description="Absolute Zillow listing URL. Populated whenever the provider has data for the entity."
244
268
  )
245
- year_built: int | None = Field(default=None, alias="yearBuilt")
269
+ year_built: int | None = Field(
270
+ default=None,
271
+ alias="yearBuilt",
272
+ description="Year the home was built, or null when the serving source does not publish it.",
273
+ )
246
274
  zestimate: float | None = Field(
247
275
  default=None, description="Zillow estimated market value."
248
276
  )
File without changes
File without changes
File without changes
File without changes