getanyapi 0.35.3__tar.gz → 0.37.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.
- {getanyapi-0.35.3 → getanyapi-0.37.0}/PKG-INFO +1 -1
- {getanyapi-0.35.3 → getanyapi-0.37.0}/pyproject.toml +1 -1
- {getanyapi-0.35.3 → getanyapi-0.37.0}/src/getanyapi/__init__.py +1 -1
- {getanyapi-0.35.3 → getanyapi-0.37.0}/src/getanyapi/_account.py +16 -3
- {getanyapi-0.35.3 → getanyapi-0.37.0}/src/getanyapi/_async_client.py +1 -1
- {getanyapi-0.35.3 → getanyapi-0.37.0}/src/getanyapi/_client.py +1 -1
- {getanyapi-0.35.3 → getanyapi-0.37.0}/src/getanyapi/platforms/company_enrichment.py +1 -1
- {getanyapi-0.35.3 → getanyapi-0.37.0}/src/getanyapi/platforms/google.py +2 -2
- {getanyapi-0.35.3 → getanyapi-0.37.0}/src/getanyapi/platforms/instagram.py +11 -7
- {getanyapi-0.35.3 → getanyapi-0.37.0}/src/getanyapi/platforms/maps.py +6 -6
- {getanyapi-0.35.3 → getanyapi-0.37.0}/src/getanyapi/platforms/perplexity.py +2 -2
- {getanyapi-0.35.3 → getanyapi-0.37.0}/src/getanyapi/platforms/reddit.py +4 -4
- {getanyapi-0.35.3 → getanyapi-0.37.0}/src/getanyapi/platforms/threads.py +5 -1
- {getanyapi-0.35.3 → getanyapi-0.37.0}/src/getanyapi/platforms/tiktok.py +4 -2
- {getanyapi-0.35.3 → getanyapi-0.37.0}/src/getanyapi/platforms/tripadvisor.py +4 -4
- {getanyapi-0.35.3 → getanyapi-0.37.0}/src/getanyapi/platforms/trustpilot.py +5 -5
- {getanyapi-0.35.3 → getanyapi-0.37.0}/src/getanyapi/platforms/twitter.py +0 -2
- {getanyapi-0.35.3 → getanyapi-0.37.0}/src/getanyapi/platforms/yelp.py +3 -3
- {getanyapi-0.35.3 → getanyapi-0.37.0}/src/getanyapi/platforms/youtube.py +170 -26
- {getanyapi-0.35.3 → getanyapi-0.37.0}/tests/test_account.py +35 -0
- {getanyapi-0.35.3 → getanyapi-0.37.0}/.gitignore +0 -0
- {getanyapi-0.35.3 → getanyapi-0.37.0}/README.md +0 -0
- {getanyapi-0.35.3 → getanyapi-0.37.0}/src/getanyapi/_errors.py +0 -0
- {getanyapi-0.35.3 → getanyapi-0.37.0}/src/getanyapi/_idempotency.py +0 -0
- {getanyapi-0.35.3 → getanyapi-0.37.0}/src/getanyapi/_pagination.py +0 -0
- {getanyapi-0.35.3 → getanyapi-0.37.0}/src/getanyapi/_transport.py +0 -0
- {getanyapi-0.35.3 → getanyapi-0.37.0}/src/getanyapi/discovery_types.py +0 -0
- {getanyapi-0.35.3 → getanyapi-0.37.0}/src/getanyapi/platforms/__init__.py +0 -0
- {getanyapi-0.35.3 → getanyapi-0.37.0}/src/getanyapi/platforms/ahrefs.py +0 -0
- {getanyapi-0.35.3 → getanyapi-0.37.0}/src/getanyapi/platforms/airbnb.py +0 -0
- {getanyapi-0.35.3 → getanyapi-0.37.0}/src/getanyapi/platforms/alibaba.py +0 -0
- {getanyapi-0.35.3 → getanyapi-0.37.0}/src/getanyapi/platforms/amazon.py +0 -0
- {getanyapi-0.35.3 → getanyapi-0.37.0}/src/getanyapi/platforms/apollo.py +0 -0
- {getanyapi-0.35.3 → getanyapi-0.37.0}/src/getanyapi/platforms/appstore.py +0 -0
- {getanyapi-0.35.3 → getanyapi-0.37.0}/src/getanyapi/platforms/bluesky.py +0 -0
- {getanyapi-0.35.3 → getanyapi-0.37.0}/src/getanyapi/platforms/booking.py +0 -0
- {getanyapi-0.35.3 → getanyapi-0.37.0}/src/getanyapi/platforms/capterra.py +0 -0
- {getanyapi-0.35.3 → getanyapi-0.37.0}/src/getanyapi/platforms/chatgpt.py +0 -0
- {getanyapi-0.35.3 → getanyapi-0.37.0}/src/getanyapi/platforms/coinmarketcap.py +0 -0
- {getanyapi-0.35.3 → getanyapi-0.37.0}/src/getanyapi/platforms/company.py +0 -0
- {getanyapi-0.35.3 → getanyapi-0.37.0}/src/getanyapi/platforms/company_search.py +0 -0
- {getanyapi-0.35.3 → getanyapi-0.37.0}/src/getanyapi/platforms/congress.py +0 -0
- {getanyapi-0.35.3 → getanyapi-0.37.0}/src/getanyapi/platforms/dexscreener.py +0 -0
- {getanyapi-0.35.3 → getanyapi-0.37.0}/src/getanyapi/platforms/douyin.py +0 -0
- {getanyapi-0.35.3 → getanyapi-0.37.0}/src/getanyapi/platforms/ebay.py +0 -0
- {getanyapi-0.35.3 → getanyapi-0.37.0}/src/getanyapi/platforms/email.py +0 -0
- {getanyapi-0.35.3 → getanyapi-0.37.0}/src/getanyapi/platforms/email_finding.py +0 -0
- {getanyapi-0.35.3 → getanyapi-0.37.0}/src/getanyapi/platforms/email_verification.py +0 -0
- {getanyapi-0.35.3 → getanyapi-0.37.0}/src/getanyapi/platforms/facebook.py +0 -0
- {getanyapi-0.35.3 → getanyapi-0.37.0}/src/getanyapi/platforms/fiverr.py +0 -0
- {getanyapi-0.35.3 → getanyapi-0.37.0}/src/getanyapi/platforms/g2.py +0 -0
- {getanyapi-0.35.3 → getanyapi-0.37.0}/src/getanyapi/platforms/gemini.py +0 -0
- {getanyapi-0.35.3 → getanyapi-0.37.0}/src/getanyapi/platforms/github.py +0 -0
- {getanyapi-0.35.3 → getanyapi-0.37.0}/src/getanyapi/platforms/glassdoor.py +0 -0
- {getanyapi-0.35.3 → getanyapi-0.37.0}/src/getanyapi/platforms/google_ads.py +0 -0
- {getanyapi-0.35.3 → getanyapi-0.37.0}/src/getanyapi/platforms/google_finance.py +0 -0
- {getanyapi-0.35.3 → getanyapi-0.37.0}/src/getanyapi/platforms/google_shopping.py +0 -0
- {getanyapi-0.35.3 → getanyapi-0.37.0}/src/getanyapi/platforms/hackernews.py +0 -0
- {getanyapi-0.35.3 → getanyapi-0.37.0}/src/getanyapi/platforms/indeed.py +0 -0
- {getanyapi-0.35.3 → getanyapi-0.37.0}/src/getanyapi/platforms/job_search.py +0 -0
- {getanyapi-0.35.3 → getanyapi-0.37.0}/src/getanyapi/platforms/linkedin.py +0 -0
- {getanyapi-0.35.3 → getanyapi-0.37.0}/src/getanyapi/platforms/mobile_phone.py +0 -0
- {getanyapi-0.35.3 → getanyapi-0.37.0}/src/getanyapi/platforms/naver.py +0 -0
- {getanyapi-0.35.3 → getanyapi-0.37.0}/src/getanyapi/platforms/pandaexpress.py +0 -0
- {getanyapi-0.35.3 → getanyapi-0.37.0}/src/getanyapi/platforms/people_search.py +0 -0
- {getanyapi-0.35.3 → getanyapi-0.37.0}/src/getanyapi/platforms/person.py +0 -0
- {getanyapi-0.35.3 → getanyapi-0.37.0}/src/getanyapi/platforms/person_enrichment.py +0 -0
- {getanyapi-0.35.3 → getanyapi-0.37.0}/src/getanyapi/platforms/pinterest.py +0 -0
- {getanyapi-0.35.3 → getanyapi-0.37.0}/src/getanyapi/platforms/playstore.py +0 -0
- {getanyapi-0.35.3 → getanyapi-0.37.0}/src/getanyapi/platforms/polymarket.py +0 -0
- {getanyapi-0.35.3 → getanyapi-0.37.0}/src/getanyapi/platforms/realtor.py +0 -0
- {getanyapi-0.35.3 → getanyapi-0.37.0}/src/getanyapi/platforms/redfin.py +0 -0
- {getanyapi-0.35.3 → getanyapi-0.37.0}/src/getanyapi/platforms/rednote.py +0 -0
- {getanyapi-0.35.3 → getanyapi-0.37.0}/src/getanyapi/platforms/sec.py +0 -0
- {getanyapi-0.35.3 → getanyapi-0.37.0}/src/getanyapi/platforms/semrush.py +0 -0
- {getanyapi-0.35.3 → getanyapi-0.37.0}/src/getanyapi/platforms/seo.py +0 -0
- {getanyapi-0.35.3 → getanyapi-0.37.0}/src/getanyapi/platforms/snapchat.py +0 -0
- {getanyapi-0.35.3 → getanyapi-0.37.0}/src/getanyapi/platforms/social.py +0 -0
- {getanyapi-0.35.3 → getanyapi-0.37.0}/src/getanyapi/platforms/spotify.py +0 -0
- {getanyapi-0.35.3 → getanyapi-0.37.0}/src/getanyapi/platforms/substack.py +0 -0
- {getanyapi-0.35.3 → getanyapi-0.37.0}/src/getanyapi/platforms/technographics.py +0 -0
- {getanyapi-0.35.3 → getanyapi-0.37.0}/src/getanyapi/platforms/tiktok_shop.py +0 -0
- {getanyapi-0.35.3 → getanyapi-0.37.0}/src/getanyapi/platforms/truthsocial.py +0 -0
- {getanyapi-0.35.3 → getanyapi-0.37.0}/src/getanyapi/platforms/upwork.py +0 -0
- {getanyapi-0.35.3 → getanyapi-0.37.0}/src/getanyapi/platforms/walmart.py +0 -0
- {getanyapi-0.35.3 → getanyapi-0.37.0}/src/getanyapi/platforms/web.py +0 -0
- {getanyapi-0.35.3 → getanyapi-0.37.0}/src/getanyapi/platforms/weibo.py +0 -0
- {getanyapi-0.35.3 → getanyapi-0.37.0}/src/getanyapi/platforms/yahoo_finance.py +0 -0
- {getanyapi-0.35.3 → getanyapi-0.37.0}/src/getanyapi/platforms/zhihu.py +0 -0
- {getanyapi-0.35.3 → getanyapi-0.37.0}/src/getanyapi/platforms/zillow.py +0 -0
- {getanyapi-0.35.3 → getanyapi-0.37.0}/src/getanyapi/py.typed +0 -0
- {getanyapi-0.35.3 → getanyapi-0.37.0}/src/getanyapi/types.py +0 -0
- {getanyapi-0.35.3 → getanyapi-0.37.0}/tests/conftest.py +0 -0
- {getanyapi-0.35.3 → getanyapi-0.37.0}/tests/test_client.py +0 -0
- {getanyapi-0.35.3 → getanyapi-0.37.0}/tests/test_durable_requests.py +0 -0
- {getanyapi-0.35.3 → getanyapi-0.37.0}/tests/test_envelope.py +0 -0
- {getanyapi-0.35.3 → getanyapi-0.37.0}/tests/test_errors.py +0 -0
- {getanyapi-0.35.3 → getanyapi-0.37.0}/tests/test_fixture_roundtrip.py +0 -0
- {getanyapi-0.35.3 → getanyapi-0.37.0}/tests/test_fixture_sweep.py +0 -0
- {getanyapi-0.35.3 → getanyapi-0.37.0}/tests/test_pagination.py +0 -0
- {getanyapi-0.35.3 → getanyapi-0.37.0}/tests/test_transport.py +0 -0
- {getanyapi-0.35.3 → getanyapi-0.37.0}/uv.lock +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: getanyapi
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.37.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
|
|
@@ -49,13 +49,26 @@ def catalog_request(category: str | None) -> tuple[str, dict[str, str]]:
|
|
|
49
49
|
|
|
50
50
|
|
|
51
51
|
def search_request(
|
|
52
|
-
query: str,
|
|
52
|
+
query: str | None,
|
|
53
53
|
category: str | None,
|
|
54
54
|
platform: str | None,
|
|
55
55
|
limit: int | None,
|
|
56
56
|
) -> tuple[str, dict[str, str]]:
|
|
57
|
-
"""Path and query params for dedicated ranked discovery search.
|
|
58
|
-
|
|
57
|
+
"""Path and query params for dedicated ranked discovery search.
|
|
58
|
+
|
|
59
|
+
The gateway needs at least one of query, category or platform, in any
|
|
60
|
+
combination, so a scope with no query at all is a valid search. An absent or
|
|
61
|
+
empty query omits ``q`` entirely, because an empty ``q`` is a different
|
|
62
|
+
request.
|
|
63
|
+
"""
|
|
64
|
+
if not query and category is None and platform is None:
|
|
65
|
+
raise AnyAPIError(
|
|
66
|
+
"search needs at least one of query, category, or platform",
|
|
67
|
+
status=0,
|
|
68
|
+
)
|
|
69
|
+
params: dict[str, str] = {}
|
|
70
|
+
if query:
|
|
71
|
+
params["q"] = query
|
|
59
72
|
if category is not None:
|
|
60
73
|
params["category"] = category
|
|
61
74
|
if platform is not None:
|
|
@@ -275,7 +275,7 @@ class CompanyEnrichmentCrustdataV3Data(BaseModel):
|
|
|
275
275
|
naics: CompanyEnrichmentCrustdataV3Naic | None = Field(
|
|
276
276
|
default=None, description="Primary NAICS industry classification."
|
|
277
277
|
)
|
|
278
|
-
name: str = Field(description="Company name.")
|
|
278
|
+
name: str | None = Field(default=None, description="Company name.")
|
|
279
279
|
region_metrics: CompanyEnrichmentCrustdataV3RegionMetric | None = Field(
|
|
280
280
|
default=None,
|
|
281
281
|
alias="regionMetrics",
|
|
@@ -705,7 +705,7 @@ class GoogleNamespace:
|
|
|
705
705
|
as cursor to walk further, or use google.search_100 for up to 100 ranked
|
|
706
706
|
results in one call, which is cheaper past roughly 20 results.
|
|
707
707
|
|
|
708
|
-
Price: $0.
|
|
708
|
+
Price: $0.0009 per request.
|
|
709
709
|
|
|
710
710
|
Example:
|
|
711
711
|
res = client.google.search(gl="us", hl="en", limit=10, query="best coffee maker")
|
|
@@ -970,7 +970,7 @@ class AsyncGoogleNamespace:
|
|
|
970
970
|
as cursor to walk further, or use google.search_100 for up to 100 ranked
|
|
971
971
|
results in one call, which is cheaper past roughly 20 results.
|
|
972
972
|
|
|
973
|
-
Price: $0.
|
|
973
|
+
Price: $0.0009 per request.
|
|
974
974
|
|
|
975
975
|
Example:
|
|
976
976
|
res = client.google.search(gl="us", hl="en", limit=10, query="best coffee maker")
|
|
@@ -156,7 +156,7 @@ class InstagramPostInput(TypedDict, total=False):
|
|
|
156
156
|
requirePlayCount: NotRequired[bool]
|
|
157
157
|
"""Set true to be served only by a source that reports a reel's play count. The default cheapest source does not carry play counts, so `plays` is absent from its responses; opting in guarantees the field when Instagram exposes it, at a higher price per request."""
|
|
158
158
|
url: Required[str]
|
|
159
|
-
"""Full Instagram post or reel URL."""
|
|
159
|
+
"""Full Instagram post or reel URL, carrying the media shortcode: /p/, /reel/, /reels/, or /tv/. A profile URL such as https://www.instagram.com/username names no post, so it is rejected instead of charged for an empty result."""
|
|
160
160
|
|
|
161
161
|
|
|
162
162
|
class InstagramPostCommentsInput(TypedDict, total=False):
|
|
@@ -1876,7 +1876,7 @@ class InstagramNamespace:
|
|
|
1876
1876
|
Fetch a single Instagram post or reel by URL (media URLs, like count, owner,
|
|
1877
1877
|
type) as normalized JSON.
|
|
1878
1878
|
|
|
1879
|
-
Price: $0.
|
|
1879
|
+
Price: $0.0015 per request.
|
|
1880
1880
|
|
|
1881
1881
|
Example:
|
|
1882
1882
|
res = client.instagram.post(url="https://www.instagram.com/reel/DWzrfE2kaY8/")
|
|
@@ -1897,7 +1897,7 @@ class InstagramNamespace:
|
|
|
1897
1897
|
List the comments on an Instagram post or reel by URL with cursor pagination
|
|
1898
1898
|
(text, author, likes).
|
|
1899
1899
|
|
|
1900
|
-
Price: $0.
|
|
1900
|
+
Price: $0.00144 per request.
|
|
1901
1901
|
|
|
1902
1902
|
Example:
|
|
1903
1903
|
res = client.instagram.post_comments(url="https://www.instagram.com/reel/DWzrfE2kaY8/")
|
|
@@ -2050,7 +2050,9 @@ class InstagramNamespace:
|
|
|
2050
2050
|
than Instagram's own hashtag feed. That is what lets it filter by date and
|
|
2051
2051
|
media type and return reels whose like counts have settled, and it is also
|
|
2052
2052
|
why results skew older (median around three months) and stop at roughly 110
|
|
2053
|
-
per hashtag.
|
|
2053
|
+
per hashtag. If that web search index is unavailable, a first-page request
|
|
2054
|
+
that sets no date and no media type is served from Instagram's own live top
|
|
2055
|
+
feed instead. For Instagram's own live ranking of a tag use
|
|
2054
2056
|
instagram.hashtag_top_posts, and for the chronological feed use
|
|
2055
2057
|
instagram.hashtag_recent_posts.
|
|
2056
2058
|
|
|
@@ -2741,7 +2743,7 @@ class AsyncInstagramNamespace:
|
|
|
2741
2743
|
Fetch a single Instagram post or reel by URL (media URLs, like count, owner,
|
|
2742
2744
|
type) as normalized JSON.
|
|
2743
2745
|
|
|
2744
|
-
Price: $0.
|
|
2746
|
+
Price: $0.0015 per request.
|
|
2745
2747
|
|
|
2746
2748
|
Example:
|
|
2747
2749
|
res = client.instagram.post(url="https://www.instagram.com/reel/DWzrfE2kaY8/")
|
|
@@ -2762,7 +2764,7 @@ class AsyncInstagramNamespace:
|
|
|
2762
2764
|
List the comments on an Instagram post or reel by URL with cursor pagination
|
|
2763
2765
|
(text, author, likes).
|
|
2764
2766
|
|
|
2765
|
-
Price: $0.
|
|
2767
|
+
Price: $0.00144 per request.
|
|
2766
2768
|
|
|
2767
2769
|
Example:
|
|
2768
2770
|
res = client.instagram.post_comments(url="https://www.instagram.com/reel/DWzrfE2kaY8/")
|
|
@@ -2915,7 +2917,9 @@ class AsyncInstagramNamespace:
|
|
|
2915
2917
|
than Instagram's own hashtag feed. That is what lets it filter by date and
|
|
2916
2918
|
media type and return reels whose like counts have settled, and it is also
|
|
2917
2919
|
why results skew older (median around three months) and stop at roughly 110
|
|
2918
|
-
per hashtag.
|
|
2920
|
+
per hashtag. If that web search index is unavailable, a first-page request
|
|
2921
|
+
that sets no date and no media type is served from Instagram's own live top
|
|
2922
|
+
feed instead. For Instagram's own live ranking of a tag use
|
|
2919
2923
|
instagram.hashtag_top_posts, and for the chronological feed use
|
|
2920
2924
|
instagram.hashtag_recent_posts.
|
|
2921
2925
|
|
|
@@ -65,7 +65,7 @@ class MapsReviewsInput(TypedDict, total=False):
|
|
|
65
65
|
language: NotRequired[str]
|
|
66
66
|
"""Two-letter language code for the review details (e.g. en). Default: en."""
|
|
67
67
|
limit: NotRequired[int]
|
|
68
|
-
"""Maximum number of results to return (1-100, default 100).
|
|
68
|
+
"""Maximum number of results to return (1-100, default 100). Range: 1 to 100."""
|
|
69
69
|
placeId: Required[str]
|
|
70
70
|
"""The Google Maps place ID to fetch reviews for (e.g. ChIJj61dQgK6j4AR4GeTYWZsKWw)."""
|
|
71
71
|
postedLimit: NotRequired[Literal["24h", "week", "month", "year"]]
|
|
@@ -308,7 +308,7 @@ class MapsReviewsItem(BaseModel):
|
|
|
308
308
|
place_id: str | None = Field(
|
|
309
309
|
default=None,
|
|
310
310
|
alias="placeId",
|
|
311
|
-
description="Google Maps place id the review belongs to.
|
|
311
|
+
description="Google Maps place id the review belongs to. Echoes the requested placeId; a lane that does not repeat it per review omits it.",
|
|
312
312
|
)
|
|
313
313
|
published_ago: str | None = Field(
|
|
314
314
|
default=None,
|
|
@@ -468,10 +468,10 @@ class MapsNamespace:
|
|
|
468
468
|
Fetch up to 100 Google Maps reviews for a place by place ID, sorted the way
|
|
469
469
|
you need, in one normalized response.
|
|
470
470
|
|
|
471
|
-
Price: $0.
|
|
471
|
+
Price: $0.0035 per request.
|
|
472
472
|
|
|
473
473
|
Example:
|
|
474
|
-
res = client.maps.reviews(limit=3, placeId="ChIJN1t_tDeuEmsRUsoyG83frY4"
|
|
474
|
+
res = client.maps.reviews(limit=3, placeId="ChIJN1t_tDeuEmsRUsoyG83frY4")
|
|
475
475
|
"""
|
|
476
476
|
raw = self._client._run_raw( # pyright: ignore[reportPrivateUsage]
|
|
477
477
|
"maps.reviews", dict(input), options
|
|
@@ -556,10 +556,10 @@ class AsyncMapsNamespace:
|
|
|
556
556
|
Fetch up to 100 Google Maps reviews for a place by place ID, sorted the way
|
|
557
557
|
you need, in one normalized response.
|
|
558
558
|
|
|
559
|
-
Price: $0.
|
|
559
|
+
Price: $0.0035 per request.
|
|
560
560
|
|
|
561
561
|
Example:
|
|
562
|
-
res = client.maps.reviews(limit=3, placeId="ChIJN1t_tDeuEmsRUsoyG83frY4"
|
|
562
|
+
res = client.maps.reviews(limit=3, placeId="ChIJN1t_tDeuEmsRUsoyG83frY4")
|
|
563
563
|
"""
|
|
564
564
|
raw = await self._client._arun_raw( # pyright: ignore[reportPrivateUsage]
|
|
565
565
|
"maps.reviews", dict(input), options
|
|
@@ -66,7 +66,7 @@ class PerplexityNamespace:
|
|
|
66
66
|
Ask Perplexity a web-grounded question and receive an answer with source
|
|
67
67
|
citations.
|
|
68
68
|
|
|
69
|
-
Price: $0.
|
|
69
|
+
Price: $0.0036 per request.
|
|
70
70
|
|
|
71
71
|
Example:
|
|
72
72
|
res = client.perplexity.search(prompt="What is AnyAPI at getanyapi.com, and what does it offer?")
|
|
@@ -94,7 +94,7 @@ class AsyncPerplexityNamespace:
|
|
|
94
94
|
Ask Perplexity a web-grounded question and receive an answer with source
|
|
95
95
|
citations.
|
|
96
96
|
|
|
97
|
-
Price: $0.
|
|
97
|
+
Price: $0.0036 per request.
|
|
98
98
|
|
|
99
99
|
Example:
|
|
100
100
|
res = client.perplexity.search(prompt="What is AnyAPI at getanyapi.com, and what does it offer?")
|
|
@@ -710,7 +710,7 @@ class RedditNamespace:
|
|
|
710
710
|
Fetch a single Reddit post by URL, including its full body text, score,
|
|
711
711
|
comment count, upvote ratio, and subreddit, as normalized JSON.
|
|
712
712
|
|
|
713
|
-
Price: $0.
|
|
713
|
+
Price: $0.0012 per request.
|
|
714
714
|
|
|
715
715
|
Example:
|
|
716
716
|
res = client.reddit.post(url="https://www.reddit.com/r/IAmA/comments/z1c9z/i_am_barack_obama_president_of_the_united_states/")
|
|
@@ -815,7 +815,7 @@ class RedditNamespace:
|
|
|
815
815
|
|
|
816
816
|
Search Reddit posts across all subreddits by query.
|
|
817
817
|
|
|
818
|
-
Price: $0.
|
|
818
|
+
Price: $0.0012 per request.
|
|
819
819
|
|
|
820
820
|
Example:
|
|
821
821
|
res = client.reddit.search(query="mechanical keyboard")
|
|
@@ -1083,7 +1083,7 @@ class AsyncRedditNamespace:
|
|
|
1083
1083
|
Fetch a single Reddit post by URL, including its full body text, score,
|
|
1084
1084
|
comment count, upvote ratio, and subreddit, as normalized JSON.
|
|
1085
1085
|
|
|
1086
|
-
Price: $0.
|
|
1086
|
+
Price: $0.0012 per request.
|
|
1087
1087
|
|
|
1088
1088
|
Example:
|
|
1089
1089
|
res = client.reddit.post(url="https://www.reddit.com/r/IAmA/comments/z1c9z/i_am_barack_obama_president_of_the_united_states/")
|
|
@@ -1188,7 +1188,7 @@ class AsyncRedditNamespace:
|
|
|
1188
1188
|
|
|
1189
1189
|
Search Reddit posts across all subreddits by query.
|
|
1190
1190
|
|
|
1191
|
-
Price: $0.
|
|
1191
|
+
Price: $0.0012 per request.
|
|
1192
1192
|
|
|
1193
1193
|
Example:
|
|
1194
1194
|
res = client.reddit.search(query="mechanical keyboard")
|
|
@@ -36,10 +36,14 @@ class ThreadsProfileInput(TypedDict, total=False):
|
|
|
36
36
|
class ThreadsSearchInput(TypedDict, total=False):
|
|
37
37
|
"""Input for Threads Search."""
|
|
38
38
|
|
|
39
|
+
endDate: NotRequired[str]
|
|
40
|
+
"""Only return posts published on or before this date, format YYYY-MM-DD (e.g. 2026-08-07). Threads pages backwards in time from here, so narrowing this is how you walk older results."""
|
|
39
41
|
preferLatencyUnderMs: NotRequired[int]
|
|
40
42
|
"""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."""
|
|
41
43
|
query: Required[str]
|
|
42
|
-
"""Keyword or hashtag to search public Threads posts for; the # prefix is optional (e.g. AI agents)."""
|
|
44
|
+
"""Keyword or hashtag to search public Threads posts for; the # prefix is optional (e.g. AI agents). Threads matches text in posts, so search a brand or topic rather than a URL - a query that is only a domain is searched by its name (wander.com is searched as wander)."""
|
|
45
|
+
startDate: NotRequired[str]
|
|
46
|
+
"""Only return posts published on or after this date, format YYYY-MM-DD (e.g. 2026-08-01)."""
|
|
43
47
|
|
|
44
48
|
|
|
45
49
|
class ThreadsSearchUsersInput(TypedDict, total=False):
|
|
@@ -465,9 +465,11 @@ class TiktokTrendingHashtagsInput(TypedDict, total=False):
|
|
|
465
465
|
class TiktokVideoInput(TypedDict, total=False):
|
|
466
466
|
"""Input for TikTok Video."""
|
|
467
467
|
|
|
468
|
+
id: NotRequired[str]
|
|
469
|
+
"""TikTok video ID, the numeric run at the end of a video URL. Use it when a listing SKU handed you an id and no URL."""
|
|
468
470
|
preferLatencyUnderMs: NotRequired[int]
|
|
469
471
|
"""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."""
|
|
470
|
-
url:
|
|
472
|
+
url: NotRequired[str]
|
|
471
473
|
"""Full TikTok video URL."""
|
|
472
474
|
|
|
473
475
|
|
|
@@ -1588,7 +1590,7 @@ class TiktokVideoTranscriptFullWord(BaseModel):
|
|
|
1588
1590
|
model_config = ConfigDict(extra="allow", populate_by_name=True)
|
|
1589
1591
|
|
|
1590
1592
|
confidence: float = Field(
|
|
1591
|
-
description="Recognizer
|
|
1593
|
+
description="Recognizer score for this word, exactly as the recognizer reported it. It is normally an alignment probability between 0 and 1, but on audio the recognizer could not align it reports a negative log-scale score instead, so read the sign before treating the number as a probability. Either way, lower means less certain, and low values are common on names, jargon, and music."
|
|
1592
1594
|
)
|
|
1593
1595
|
end_seconds: float | None = Field(
|
|
1594
1596
|
default=None,
|
|
@@ -178,7 +178,7 @@ class TripadvisorNamespace:
|
|
|
178
178
|
attraction by its page URL: rating, text, date, and trip details as
|
|
179
179
|
normalized JSON.
|
|
180
180
|
|
|
181
|
-
Price: $0.
|
|
181
|
+
Price: $0.003 per request.
|
|
182
182
|
|
|
183
183
|
Example:
|
|
184
184
|
res = client.tripadvisor.reviews(limit=3, url="https://www.tripadvisor.com/Hotel_Review-g60763-d93450-Reviews-The_Plaza-New_York_City_New_York.html")
|
|
@@ -200,7 +200,7 @@ class TripadvisorNamespace:
|
|
|
200
200
|
destination and get rich place records (ratings, review counts, contact
|
|
201
201
|
details, pricing) as normalized JSON.
|
|
202
202
|
|
|
203
|
-
Price: $0.
|
|
203
|
+
Price: $0.003 per request.
|
|
204
204
|
|
|
205
205
|
Example:
|
|
206
206
|
res = client.tripadvisor.search(limit=3, query="Paris")
|
|
@@ -229,7 +229,7 @@ class AsyncTripadvisorNamespace:
|
|
|
229
229
|
attraction by its page URL: rating, text, date, and trip details as
|
|
230
230
|
normalized JSON.
|
|
231
231
|
|
|
232
|
-
Price: $0.
|
|
232
|
+
Price: $0.003 per request.
|
|
233
233
|
|
|
234
234
|
Example:
|
|
235
235
|
res = client.tripadvisor.reviews(limit=3, url="https://www.tripadvisor.com/Hotel_Review-g60763-d93450-Reviews-The_Plaza-New_York_City_New_York.html")
|
|
@@ -251,7 +251,7 @@ class AsyncTripadvisorNamespace:
|
|
|
251
251
|
destination and get rich place records (ratings, review counts, contact
|
|
252
252
|
details, pricing) as normalized JSON.
|
|
253
253
|
|
|
254
|
-
Price: $0.
|
|
254
|
+
Price: $0.003 per request.
|
|
255
255
|
|
|
256
256
|
Example:
|
|
257
257
|
res = client.tripadvisor.search(limit=3, query="Paris")
|
|
@@ -21,9 +21,9 @@ class TrustpilotReviewsInput(TypedDict, total=False):
|
|
|
21
21
|
company: Required[str]
|
|
22
22
|
"""Brand name or Trustpilot review-page URL to fetch reviews for (e.g. nike or https://www.trustpilot.com/review/nike.com)."""
|
|
23
23
|
countries: NotRequired[list[str]]
|
|
24
|
-
"""Only return reviews from reviewers in these ISO 3166-1 alpha-2 countries (e.g. ["US", "GB"])
|
|
24
|
+
"""Only return reviews from reviewers in these ISO 3166-1 alpha-2 countries (e.g. ["US", "GB"]). Omit this field for all countries and to stay on the cheapest price; a country filter routes to the dearest source."""
|
|
25
25
|
languages: NotRequired[list[str]]
|
|
26
|
-
"""Only return reviews in these ISO 639-1 languages (e.g. ["en", "de"])
|
|
26
|
+
"""Only return reviews in these ISO 639-1 languages (e.g. ["en", "de"]). Omit this field for all languages and to stay on the cheapest price; a language filter routes to the dearest source."""
|
|
27
27
|
limit: NotRequired[int]
|
|
28
28
|
"""Maximum number of results to return (1-200, default 200). Trustpilot serves at most 200 reviews per company. Range: 1 to 200. Default: 200."""
|
|
29
29
|
preferLatencyUnderMs: NotRequired[int]
|
|
@@ -31,11 +31,11 @@ class TrustpilotReviewsInput(TypedDict, total=False):
|
|
|
31
31
|
sortBy: NotRequired[str]
|
|
32
32
|
"""Review ordering: auto, relevancy, or recent (e.g. recent). Default: auto."""
|
|
33
33
|
stars: NotRequired[str]
|
|
34
|
-
"""Limit reviews to a single star rating from 1 to 5 (e.g. 5);
|
|
34
|
+
"""Limit reviews to a single star rating from 1 to 5 (e.g. 5). Omit this field to stay on the cheapest price; a star filter routes to a dearer source."""
|
|
35
35
|
startDate: NotRequired[str]
|
|
36
|
-
"""Only return reviews on or after this date, inclusive, in YYYY-MM-DD format (e.g. 2026-01-01)."""
|
|
36
|
+
"""Only return reviews on or after this date, inclusive, in YYYY-MM-DD format (e.g. 2026-01-01). Omit this field to stay on the cheapest price; a date floor routes to the dearest source."""
|
|
37
37
|
verifiedOnly: NotRequired[bool]
|
|
38
|
-
"""Set true to return only verified reviews (e.g. true). Default: false."""
|
|
38
|
+
"""Set true to return only verified reviews (e.g. true). Omit this field, or send false, to stay on the cheapest price; true routes to a dearer source. Default: false."""
|
|
39
39
|
|
|
40
40
|
|
|
41
41
|
class TrustpilotReviewsData(BaseModel):
|
|
@@ -103,8 +103,6 @@ class TwitterSearchInput(TypedDict, total=False):
|
|
|
103
103
|
|
|
104
104
|
cursor: NotRequired[str]
|
|
105
105
|
"""Opaque pagination cursor from a previous response's nextCursor. Omit for the first page; pass it to fetch the next page of search results."""
|
|
106
|
-
lang: NotRequired[str]
|
|
107
|
-
"""Optional ISO 639-1 language code to restrict tweets to (e.g. en)."""
|
|
108
106
|
limit: NotRequired[int]
|
|
109
107
|
"""Per-page maximum number of results to return (1-50, default 20). A provider may return a smaller native page; follow nextCursor for more. Range: 1 to 50. Default: 20."""
|
|
110
108
|
preferLatencyUnderMs: NotRequired[int]
|
|
@@ -19,7 +19,7 @@ class YelpSearchInput(TypedDict, total=False):
|
|
|
19
19
|
"""Input for Yelp Search."""
|
|
20
20
|
|
|
21
21
|
limit: NotRequired[int]
|
|
22
|
-
"""Maximum number of results to return (1 to 20, default 20). Range: 1 to 20."""
|
|
22
|
+
"""Maximum number of results to return (1 to 20, default 20). Range: 1 to 20. Default: 20."""
|
|
23
23
|
location: Required[str]
|
|
24
24
|
"""City and state defining the search area (e.g. San Francisco, CA)."""
|
|
25
25
|
preferLatencyUnderMs: NotRequired[int]
|
|
@@ -113,7 +113,7 @@ class YelpNamespace:
|
|
|
113
113
|
Search Yelp for businesses by keyword and location: up to 20 listings with
|
|
114
114
|
ratings, categories, and core business info per request.
|
|
115
115
|
|
|
116
|
-
Price: $0.
|
|
116
|
+
Price: $0.0035 per request.
|
|
117
117
|
|
|
118
118
|
Example:
|
|
119
119
|
res = client.yelp.search(limit=5, location="Chicago, IL", query="pizza")
|
|
@@ -138,7 +138,7 @@ class AsyncYelpNamespace:
|
|
|
138
138
|
Search Yelp for businesses by keyword and location: up to 20 listings with
|
|
139
139
|
ratings, categories, and core business info per request.
|
|
140
140
|
|
|
141
|
-
Price: $0.
|
|
141
|
+
Price: $0.0035 per request.
|
|
142
142
|
|
|
143
143
|
Example:
|
|
144
144
|
res = client.yelp.search(limit=5, location="Chicago, IL", query="pizza")
|
|
@@ -165,8 +165,23 @@ class YoutubeSearchHashtagInput(TypedDict, total=False):
|
|
|
165
165
|
"""Hashtag to search for (without the leading #)."""
|
|
166
166
|
preferLatencyUnderMs: NotRequired[int]
|
|
167
167
|
"""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."""
|
|
168
|
-
type: NotRequired[Literal["all"
|
|
169
|
-
"""Content filter."""
|
|
168
|
+
type: NotRequired[Literal["all"]]
|
|
169
|
+
"""Content filter. Only "all" is served: no source we buy returns a Shorts row with the channel and publish time this endpoint requires."""
|
|
170
|
+
|
|
171
|
+
|
|
172
|
+
class YoutubeSearchShortsInput(TypedDict, total=False):
|
|
173
|
+
"""Input for YouTube Shorts Search."""
|
|
174
|
+
|
|
175
|
+
cursor: NotRequired[str]
|
|
176
|
+
"""Continuation token from a previous response for pagination."""
|
|
177
|
+
preferLatencyUnderMs: NotRequired[int]
|
|
178
|
+
"""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."""
|
|
179
|
+
query: Required[str]
|
|
180
|
+
"""The keyword to search Shorts for."""
|
|
181
|
+
sortBy: NotRequired[Literal["relevance", "popular"]]
|
|
182
|
+
"""Sort order: "relevance" (default) or "popular" (most-viewed). Default: relevance."""
|
|
183
|
+
uploadDate: NotRequired[Literal["today", "this_week", "this_month", "this_year"]]
|
|
184
|
+
"""Filter by upload recency. Omit for any time."""
|
|
170
185
|
|
|
171
186
|
|
|
172
187
|
class YoutubeTrendingShortsInput(TypedDict, total=False):
|
|
@@ -180,11 +195,11 @@ class YoutubeVideoInput(TypedDict, total=False):
|
|
|
180
195
|
"""Input for YouTube Video."""
|
|
181
196
|
|
|
182
197
|
id: NotRequired[str]
|
|
183
|
-
"""YouTube video ID."""
|
|
198
|
+
"""YouTube video ID. A Short uses the same ID as any other video."""
|
|
184
199
|
preferLatencyUnderMs: NotRequired[int]
|
|
185
200
|
"""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."""
|
|
186
201
|
url: NotRequired[str]
|
|
187
|
-
"""Full YouTube video URL."""
|
|
202
|
+
"""Full YouTube video URL. Shorts (youtube.com/shorts/...), youtu.be, live, and embed URLs all work."""
|
|
188
203
|
|
|
189
204
|
|
|
190
205
|
class YoutubeVideoCommentsInput(TypedDict, total=False):
|
|
@@ -197,7 +212,7 @@ class YoutubeVideoCommentsInput(TypedDict, total=False):
|
|
|
197
212
|
preferLatencyUnderMs: NotRequired[int]
|
|
198
213
|
"""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."""
|
|
199
214
|
url: Required[str]
|
|
200
|
-
"""Full YouTube video URL."""
|
|
215
|
+
"""Full YouTube video URL. Shorts (youtube.com/shorts/...) and youtu.be URLs also work."""
|
|
201
216
|
|
|
202
217
|
|
|
203
218
|
class YoutubeVideoSponsorsInput(TypedDict, total=False):
|
|
@@ -215,11 +230,11 @@ class YoutubeVideoTranscriptInput(TypedDict, total=False):
|
|
|
215
230
|
"""Input for YouTube Video Transcript."""
|
|
216
231
|
|
|
217
232
|
id: NotRequired[str]
|
|
218
|
-
"""YouTube video ID."""
|
|
233
|
+
"""YouTube video ID. A Short uses the same ID as any other video."""
|
|
219
234
|
preferLatencyUnderMs: NotRequired[int]
|
|
220
235
|
"""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."""
|
|
221
236
|
url: NotRequired[str]
|
|
222
|
-
"""Full YouTube video URL."""
|
|
237
|
+
"""Full YouTube video URL. Shorts (youtube.com/shorts/...), youtu.be, live, and embed URLs all work."""
|
|
223
238
|
|
|
224
239
|
|
|
225
240
|
class YoutubeVideoTranscriptFullInput(TypedDict, total=False):
|
|
@@ -232,7 +247,7 @@ class YoutubeVideoTranscriptFullInput(TypedDict, total=False):
|
|
|
232
247
|
preferLatencyUnderMs: NotRequired[int]
|
|
233
248
|
"""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."""
|
|
234
249
|
url: Required[str]
|
|
235
|
-
"""YouTube video URL (e.g. "https://www.youtube.com/watch?v=dQw4w9WgXcQ")."""
|
|
250
|
+
"""YouTube video or Short URL (e.g. "https://www.youtube.com/watch?v=dQw4w9WgXcQ" or "https://www.youtube.com/shorts/Fir1x9cw2vg")."""
|
|
236
251
|
|
|
237
252
|
|
|
238
253
|
class YoutubeChannelData(BaseModel):
|
|
@@ -388,8 +403,9 @@ class YoutubeChannelShortsShort(BaseModel):
|
|
|
388
403
|
likes: int = Field(
|
|
389
404
|
description="Public like count when supplied by the upstream response. Minimum: 0."
|
|
390
405
|
)
|
|
391
|
-
title: str = Field(
|
|
392
|
-
|
|
406
|
+
title: str | None = Field(
|
|
407
|
+
default=None,
|
|
408
|
+
description="Public title or caption for the Short. Populated whenever the provider has data for the entity. Present whenever the upstream returns this record.",
|
|
393
409
|
)
|
|
394
410
|
url: str = Field(
|
|
395
411
|
description="Public YouTube URL for the Short. Populated whenever the provider has data for the entity."
|
|
@@ -597,6 +613,44 @@ class YoutubeSearchHashtagVideo(BaseModel):
|
|
|
597
613
|
views: int
|
|
598
614
|
|
|
599
615
|
|
|
616
|
+
class YoutubeSearchShortsData(BaseModel):
|
|
617
|
+
model_config = ConfigDict(populate_by_name=True)
|
|
618
|
+
|
|
619
|
+
next_cursor: str | None = Field(
|
|
620
|
+
default=None,
|
|
621
|
+
alias="nextCursor",
|
|
622
|
+
description="Opaque cursor for the next page of results, or null when there are no more. Pass it back as cursor to continue.",
|
|
623
|
+
)
|
|
624
|
+
shorts: list[YoutubeSearchShortsShort] = Field(
|
|
625
|
+
description="Populated whenever the provider has data for the entity."
|
|
626
|
+
)
|
|
627
|
+
|
|
628
|
+
|
|
629
|
+
class YoutubeSearchShortsShort(BaseModel):
|
|
630
|
+
model_config = ConfigDict(extra="allow", populate_by_name=True)
|
|
631
|
+
|
|
632
|
+
content_type: str = Field(
|
|
633
|
+
alias="contentType",
|
|
634
|
+
description='Always "short": every row here comes from YouTube\'s Shorts shelf.',
|
|
635
|
+
)
|
|
636
|
+
id: str = Field(
|
|
637
|
+
description="YouTube video id of the Short. Populated whenever the provider has data for the entity."
|
|
638
|
+
)
|
|
639
|
+
title: str = Field(
|
|
640
|
+
description="Title of the Short. Populated whenever the provider has data for the entity."
|
|
641
|
+
)
|
|
642
|
+
url: str = Field(
|
|
643
|
+
description="Watch URL for the Short. Populated whenever the provider has data for the entity."
|
|
644
|
+
)
|
|
645
|
+
views: int = Field(
|
|
646
|
+
description="View count, or 0 when the source did not publish one. Read viewsAvailable before trusting a 0."
|
|
647
|
+
)
|
|
648
|
+
views_available: bool = Field(
|
|
649
|
+
alias="viewsAvailable",
|
|
650
|
+
description="False when the source published no view count, so views is a placeholder rather than a measured zero.",
|
|
651
|
+
)
|
|
652
|
+
|
|
653
|
+
|
|
600
654
|
class YoutubeTrendingShortsData(BaseModel):
|
|
601
655
|
shorts: list[YoutubeTrendingShortsShort] = Field(
|
|
602
656
|
description="Populated whenever the provider has data for the entity."
|
|
@@ -1203,6 +1257,50 @@ class YoutubeNamespace:
|
|
|
1203
1257
|
options=options,
|
|
1204
1258
|
)
|
|
1205
1259
|
|
|
1260
|
+
def search_shorts(
|
|
1261
|
+
self,
|
|
1262
|
+
*,
|
|
1263
|
+
options: RequestOptions | None = None,
|
|
1264
|
+
**input: Unpack[YoutubeSearchShortsInput],
|
|
1265
|
+
) -> RunResult[YoutubeSearchShortsData]:
|
|
1266
|
+
"""YouTube Shorts Search
|
|
1267
|
+
|
|
1268
|
+
Search YouTube Shorts by keyword and get matching Shorts (title, views, URL)
|
|
1269
|
+
with cursor pagination as normalized JSON.
|
|
1270
|
+
|
|
1271
|
+
Price: $0.002 per request.
|
|
1272
|
+
|
|
1273
|
+
Example:
|
|
1274
|
+
res = client.youtube.search_shorts(query="cats")
|
|
1275
|
+
"""
|
|
1276
|
+
raw = self._client._run_raw( # pyright: ignore[reportPrivateUsage]
|
|
1277
|
+
"youtube.search_shorts", dict(input), options
|
|
1278
|
+
)
|
|
1279
|
+
return RunResult[YoutubeSearchShortsData].model_validate(raw)
|
|
1280
|
+
|
|
1281
|
+
def iter_search_shorts(
|
|
1282
|
+
self,
|
|
1283
|
+
*,
|
|
1284
|
+
options: RequestOptions | None = None,
|
|
1285
|
+
**input: Unpack[YoutubeSearchShortsInput],
|
|
1286
|
+
) -> Paginator[YoutubeSearchShortsShort, YoutubeSearchShortsData]:
|
|
1287
|
+
"""Iterate YouTube Shorts Search results, following pagination cursors.
|
|
1288
|
+
|
|
1289
|
+
Yields validated `YoutubeSearchShortsShort` items from the `shorts` field of
|
|
1290
|
+
each page. Use `.pages()` on the returned paginator to walk whole
|
|
1291
|
+
`RunResult` pages.
|
|
1292
|
+
"""
|
|
1293
|
+
return paginate(
|
|
1294
|
+
self._client,
|
|
1295
|
+
"youtube.search_shorts",
|
|
1296
|
+
dict(input),
|
|
1297
|
+
"shorts",
|
|
1298
|
+
item_model=YoutubeSearchShortsShort,
|
|
1299
|
+
data_model=YoutubeSearchShortsData,
|
|
1300
|
+
bare=False,
|
|
1301
|
+
options=options,
|
|
1302
|
+
)
|
|
1303
|
+
|
|
1206
1304
|
def trending_shorts(
|
|
1207
1305
|
self,
|
|
1208
1306
|
*,
|
|
@@ -1232,8 +1330,8 @@ class YoutubeNamespace:
|
|
|
1232
1330
|
) -> RunResult[YoutubeVideoData]:
|
|
1233
1331
|
"""YouTube Video
|
|
1234
1332
|
|
|
1235
|
-
Fetch a YouTube video's metadata (title, channel, views, likes,
|
|
1236
|
-
publish date) by URL or ID.
|
|
1333
|
+
Fetch a YouTube video or Short's metadata (title, channel, views, likes,
|
|
1334
|
+
duration, publish date) by URL or ID.
|
|
1237
1335
|
|
|
1238
1336
|
Price: $0.00125 per request.
|
|
1239
1337
|
|
|
@@ -1253,8 +1351,8 @@ class YoutubeNamespace:
|
|
|
1253
1351
|
) -> RunResult[YoutubeVideoCommentsData]:
|
|
1254
1352
|
"""YouTube Video Comments
|
|
1255
1353
|
|
|
1256
|
-
List the comments on a YouTube video by URL with cursor pagination
|
|
1257
|
-
author, likes, reply count).
|
|
1354
|
+
List the comments on a YouTube video or Short by URL with cursor pagination
|
|
1355
|
+
(text, author, likes, reply count).
|
|
1258
1356
|
|
|
1259
1357
|
Price: $0.002 per request.
|
|
1260
1358
|
|
|
@@ -1318,7 +1416,7 @@ class YoutubeNamespace:
|
|
|
1318
1416
|
) -> RunResult[YoutubeVideoTranscriptData]:
|
|
1319
1417
|
"""YouTube Video Transcript
|
|
1320
1418
|
|
|
1321
|
-
Fetch the transcript/captions of a YouTube video by URL or ID.
|
|
1419
|
+
Fetch the transcript/captions of a YouTube video or Short by URL or ID.
|
|
1322
1420
|
|
|
1323
1421
|
Price: $0.00125 per request.
|
|
1324
1422
|
|
|
@@ -1338,10 +1436,11 @@ class YoutubeNamespace:
|
|
|
1338
1436
|
) -> RunResult[YoutubeVideoTranscriptFullData]:
|
|
1339
1437
|
"""YouTube Video Transcript (Provenance)
|
|
1340
1438
|
|
|
1341
|
-
Fetch a YouTube transcript with timed segments and its
|
|
1342
|
-
the words are creator-written captions or machine speech
|
|
1439
|
+
Fetch a YouTube video or Short transcript with timed segments and its
|
|
1440
|
+
provenance: whether the words are creator-written captions or machine speech
|
|
1441
|
+
recognition.
|
|
1343
1442
|
|
|
1344
|
-
Price: $0.
|
|
1443
|
+
Price: $0.001 per request.
|
|
1345
1444
|
|
|
1346
1445
|
Example:
|
|
1347
1446
|
res = client.youtube.video_transcript_full(url="https://www.youtube.com/watch?v=dQw4w9WgXcQ")
|
|
@@ -1775,6 +1874,50 @@ class AsyncYoutubeNamespace:
|
|
|
1775
1874
|
options=options,
|
|
1776
1875
|
)
|
|
1777
1876
|
|
|
1877
|
+
async def search_shorts(
|
|
1878
|
+
self,
|
|
1879
|
+
*,
|
|
1880
|
+
options: RequestOptions | None = None,
|
|
1881
|
+
**input: Unpack[YoutubeSearchShortsInput],
|
|
1882
|
+
) -> RunResult[YoutubeSearchShortsData]:
|
|
1883
|
+
"""YouTube Shorts Search
|
|
1884
|
+
|
|
1885
|
+
Search YouTube Shorts by keyword and get matching Shorts (title, views, URL)
|
|
1886
|
+
with cursor pagination as normalized JSON.
|
|
1887
|
+
|
|
1888
|
+
Price: $0.002 per request.
|
|
1889
|
+
|
|
1890
|
+
Example:
|
|
1891
|
+
res = client.youtube.search_shorts(query="cats")
|
|
1892
|
+
"""
|
|
1893
|
+
raw = await self._client._arun_raw( # pyright: ignore[reportPrivateUsage]
|
|
1894
|
+
"youtube.search_shorts", dict(input), options
|
|
1895
|
+
)
|
|
1896
|
+
return RunResult[YoutubeSearchShortsData].model_validate(raw)
|
|
1897
|
+
|
|
1898
|
+
def iter_search_shorts(
|
|
1899
|
+
self,
|
|
1900
|
+
*,
|
|
1901
|
+
options: RequestOptions | None = None,
|
|
1902
|
+
**input: Unpack[YoutubeSearchShortsInput],
|
|
1903
|
+
) -> AsyncPaginator[YoutubeSearchShortsShort, YoutubeSearchShortsData]:
|
|
1904
|
+
"""Iterate YouTube Shorts Search results, following pagination cursors.
|
|
1905
|
+
|
|
1906
|
+
Yields validated `YoutubeSearchShortsShort` items from the `shorts` field of
|
|
1907
|
+
each page. Use `.pages()` on the returned paginator to walk whole
|
|
1908
|
+
`RunResult` pages.
|
|
1909
|
+
"""
|
|
1910
|
+
return apaginate(
|
|
1911
|
+
self._client,
|
|
1912
|
+
"youtube.search_shorts",
|
|
1913
|
+
dict(input),
|
|
1914
|
+
"shorts",
|
|
1915
|
+
item_model=YoutubeSearchShortsShort,
|
|
1916
|
+
data_model=YoutubeSearchShortsData,
|
|
1917
|
+
bare=False,
|
|
1918
|
+
options=options,
|
|
1919
|
+
)
|
|
1920
|
+
|
|
1778
1921
|
async def trending_shorts(
|
|
1779
1922
|
self,
|
|
1780
1923
|
*,
|
|
@@ -1804,8 +1947,8 @@ class AsyncYoutubeNamespace:
|
|
|
1804
1947
|
) -> RunResult[YoutubeVideoData]:
|
|
1805
1948
|
"""YouTube Video
|
|
1806
1949
|
|
|
1807
|
-
Fetch a YouTube video's metadata (title, channel, views, likes,
|
|
1808
|
-
publish date) by URL or ID.
|
|
1950
|
+
Fetch a YouTube video or Short's metadata (title, channel, views, likes,
|
|
1951
|
+
duration, publish date) by URL or ID.
|
|
1809
1952
|
|
|
1810
1953
|
Price: $0.00125 per request.
|
|
1811
1954
|
|
|
@@ -1825,8 +1968,8 @@ class AsyncYoutubeNamespace:
|
|
|
1825
1968
|
) -> RunResult[YoutubeVideoCommentsData]:
|
|
1826
1969
|
"""YouTube Video Comments
|
|
1827
1970
|
|
|
1828
|
-
List the comments on a YouTube video by URL with cursor pagination
|
|
1829
|
-
author, likes, reply count).
|
|
1971
|
+
List the comments on a YouTube video or Short by URL with cursor pagination
|
|
1972
|
+
(text, author, likes, reply count).
|
|
1830
1973
|
|
|
1831
1974
|
Price: $0.002 per request.
|
|
1832
1975
|
|
|
@@ -1890,7 +2033,7 @@ class AsyncYoutubeNamespace:
|
|
|
1890
2033
|
) -> RunResult[YoutubeVideoTranscriptData]:
|
|
1891
2034
|
"""YouTube Video Transcript
|
|
1892
2035
|
|
|
1893
|
-
Fetch the transcript/captions of a YouTube video by URL or ID.
|
|
2036
|
+
Fetch the transcript/captions of a YouTube video or Short by URL or ID.
|
|
1894
2037
|
|
|
1895
2038
|
Price: $0.00125 per request.
|
|
1896
2039
|
|
|
@@ -1910,10 +2053,11 @@ class AsyncYoutubeNamespace:
|
|
|
1910
2053
|
) -> RunResult[YoutubeVideoTranscriptFullData]:
|
|
1911
2054
|
"""YouTube Video Transcript (Provenance)
|
|
1912
2055
|
|
|
1913
|
-
Fetch a YouTube transcript with timed segments and its
|
|
1914
|
-
the words are creator-written captions or machine speech
|
|
2056
|
+
Fetch a YouTube video or Short transcript with timed segments and its
|
|
2057
|
+
provenance: whether the words are creator-written captions or machine speech
|
|
2058
|
+
recognition.
|
|
1915
2059
|
|
|
1916
|
-
Price: $0.
|
|
2060
|
+
Price: $0.001 per request.
|
|
1917
2061
|
|
|
1918
2062
|
Example:
|
|
1919
2063
|
res = client.youtube.video_transcript_full(url="https://www.youtube.com/watch?v=dQw4w9WgXcQ")
|
|
@@ -12,6 +12,7 @@ import pytest
|
|
|
12
12
|
|
|
13
13
|
from conftest import json_response, make_async_client, make_sync_client
|
|
14
14
|
from getanyapi import (
|
|
15
|
+
AnyAPIError,
|
|
15
16
|
DiscoveryPricing,
|
|
16
17
|
FlatPricingOffer,
|
|
17
18
|
LinearPricingOffer,
|
|
@@ -331,6 +332,40 @@ def test_search_reads_every_shared_ranked_field_and_forwards_filters() -> None:
|
|
|
331
332
|
assert found.model_dump(by_alias=True, exclude_defaults=True) == body
|
|
332
333
|
|
|
333
334
|
|
|
335
|
+
def test_search_scopes_without_a_query_and_omits_q_entirely() -> None:
|
|
336
|
+
body = discovery_search()
|
|
337
|
+
|
|
338
|
+
def respond(req: httpx.Request) -> httpx.Response:
|
|
339
|
+
assert req.url.path == "/catalog/search"
|
|
340
|
+
assert dict(req.url.params) == {"platform": "reddit"}
|
|
341
|
+
return json_response(200, body)
|
|
342
|
+
|
|
343
|
+
client, _ = make_sync_client(respond)
|
|
344
|
+
assert client.search(platform="reddit").results
|
|
345
|
+
|
|
346
|
+
|
|
347
|
+
def test_search_omits_an_empty_query_rather_than_sending_it_empty() -> None:
|
|
348
|
+
body = discovery_search()
|
|
349
|
+
|
|
350
|
+
def respond(req: httpx.Request) -> httpx.Response:
|
|
351
|
+
assert "q" not in dict(req.url.params)
|
|
352
|
+
return json_response(200, body)
|
|
353
|
+
|
|
354
|
+
client, _ = make_sync_client(respond)
|
|
355
|
+
assert client.search(query="", category="social").results
|
|
356
|
+
|
|
357
|
+
|
|
358
|
+
def test_search_without_query_category_or_platform_never_calls_the_gateway() -> None:
|
|
359
|
+
client, recorder = make_sync_client(
|
|
360
|
+
lambda _req: json_response(200, discovery_search())
|
|
361
|
+
)
|
|
362
|
+
with pytest.raises(
|
|
363
|
+
AnyAPIError, match="at least one of query, category, or platform"
|
|
364
|
+
):
|
|
365
|
+
client.search(limit=5)
|
|
366
|
+
assert recorder.requests == []
|
|
367
|
+
|
|
368
|
+
|
|
334
369
|
def test_search_drops_safe_additive_result_and_envelope_fields() -> None:
|
|
335
370
|
body = discovery_search()
|
|
336
371
|
results = cast("list[dict[str, object]]", body["results"])
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|