scavio 0.8.0 → 0.10.0

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.
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # Scavio
2
2
 
3
- TypeScript SDK for the [Scavio Search API](https://scavio.dev) — real-time Google, Amazon, Walmart, YouTube, Reddit, TikTok, Instagram, X, and LinkedIn data.
3
+ TypeScript SDK for the [Scavio Search API](https://scavio.dev) — real-time Google, Amazon, Walmart, YouTube, Reddit, TikTok, TikTok Shop, Instagram, X, and LinkedIn data.
4
4
 
5
5
  ## Install
6
6
 
@@ -205,29 +205,36 @@ await client.x.trending({ country: "UnitedStates" });
205
205
  ### LinkedIn
206
206
 
207
207
  ```typescript
208
- // Person profile, about, posts, and contact
208
+ // Person profile, about, and posts. A handle or a full LinkedIn URL works
209
+ // anywhere a reference is taken.
209
210
  await client.linkedin.person({ username: "williamhgates" });
210
- await client.linkedin.personAbout({ username: "williamhgates" });
211
+ await client.linkedin.personAbout({ url: "https://www.linkedin.com/in/williamhgates/" });
211
212
  await client.linkedin.personPosts({ username: "williamhgates" });
212
- await client.linkedin.personContact({ username: "williamhgates" });
213
213
 
214
- // Company profile, posts, people, and jobs
214
+ // Company profile and posts
215
215
  await client.linkedin.company({ company: "microsoft" });
216
216
  await client.linkedin.companyPosts({ company: "microsoft" });
217
- await client.linkedin.companyPeople({ company: "microsoft" });
218
- await client.linkedin.companyJobs({ company: "microsoft" });
219
-
220
- // Search people, jobs, and posts
221
- await client.linkedin.searchPeople({ search: "john", title: "engineer" });
222
- await client.linkedin.searchJobs({ search: "software engineer" });
223
- await client.linkedin.searchPosts({ search: "AI agents" });
224
-
225
- // Job, post, and post comments
226
- await client.linkedin.job({ job_id: "3900000000" });
227
- await client.linkedin.post({ post_id: "7486820977411145728" });
228
- await client.linkedin.postComments({ post_id: "7486820977411145728", sort_order: "relevance" });
217
+
218
+ // Jobs: search, then pull detail for one listing
219
+ await client.linkedin.searchJobs({ search: "software engineer", location: "United States" });
220
+ await client.linkedin.job({ job_id: "4415427228" });
221
+
222
+ // A post and its comments (10 per page)
223
+ await client.linkedin.post({ post_id: "7488618410256523265" });
224
+ await client.linkedin.postComments({ post_id: "7488618410256523265", page: 1 });
229
225
  ```
230
226
 
227
+ All LinkedIn endpoints cost 1 credit.
228
+
229
+ > **Retired endpoints.** The upstream provider withdrew the datasets behind
230
+ > `personContact`, `companyPeople`, `companyJobs`, `searchPeople` and
231
+ > `searchPosts`. They remain callable but always return HTTP 410 and are never
232
+ > billed. `company()` returns `featured_employees` (a small sample of staff), and
233
+ > `searchJobs()` with a company name substitutes for `companyJobs()`.
234
+ >
235
+ > `personPosts` and `companyPosts` return up to 50 posts; the provider exposes no
236
+ > further pages, so those endpoints no longer take a cursor.
237
+
231
238
  ### TikTok
232
239
 
233
240
  ```typescript
@@ -265,8 +272,70 @@ await client.tiktok.userFollowers({ sec_user_id: "abc123" });
265
272
  await client.tiktok.userFollowings({ sec_user_id: "abc123" });
266
273
  ```
267
274
 
275
+ ### TikTok Shop
276
+
277
+ Every TikTok Shop endpoint costs 1 credit. Two limits to design around:
278
+
279
+ - `product()` resolves only about 44% of the product ids returned by `search()`.
280
+ Upstream has no detail data for the rest, so an HTTP 404 is a normal outcome, not an
281
+ error — skip the item instead of retrying. Search to product is not a reliable
282
+ pipeline. `product()` **throws** `NotFoundError` on that 404 (there is no `data`
283
+ field in the body to test), so a loop over search ids must catch it:
284
+
285
+ ```typescript
286
+ import { NotFoundError } from "scavio";
287
+
288
+ for (const productId of productIds) {
289
+ try {
290
+ const detail = await client.tiktokShop.product({ product_id: productId });
291
+ } catch (e) {
292
+ if (e instanceof NotFoundError) continue; // no detail upstream; skip
293
+ throw e;
294
+ }
295
+ }
296
+ ```
297
+
298
+ `productReviews()` often works for ids `product()` cannot resolve: of 8 such ids
299
+ tested, 8 returned HTTP 200 and 7 carried at least one review, so it is a useful
300
+ fallback source of product detail.
301
+ - `product()` does not return a price; upstream masks it on the product page. Exact
302
+ prices come from `search()`, `shopProducts()`, and `categoryProducts()`.
303
+
304
+ ```typescript
305
+ // Search products (US catalog, exact prices, cursor pagination)
306
+ await client.tiktokShop.search({ search: "phone case" });
307
+
308
+ // Keyword suggestions (8 regions)
309
+ await client.tiktokShop.searchSuggestions({ search: "wireless", region: "US" });
310
+
311
+ // Product detail (no price; a 404 is normal, see above)
312
+ await client.tiktokShop.product({ product_id: "1732293553906094315" });
313
+
314
+ // Product reviews (up to 200 per call)
315
+ await client.tiktokShop.productReviews({
316
+ product_id: "1732293553906094315",
317
+ page_size: 200,
318
+ sort: "relevant",
319
+ });
320
+
321
+ // Category tree (28 top-level, 240 nodes)
322
+ await client.tiktokShop.categories();
323
+
324
+ // Products in a category (US and GB only)
325
+ await client.tiktokShop.categoryProducts({ category_id: "601450" });
326
+
327
+ // A shop's catalog, 30 per page
328
+ await client.tiktokShop.shopProducts({ shop_id: "7495514739648989419" });
329
+
330
+ // Resolve any TikTok Shop URL or share link to a product_id / shop_id
331
+ await client.tiktokShop.resolve({ url: "https://vt.tiktok.com/ZT2AHoGsE/" });
332
+ ```
333
+
268
334
  ### Instagram
269
335
 
336
+ Credit cost varies by endpoint: `userPosts` costs 2 credits, every other
337
+ Instagram endpoint costs 8.
338
+
270
339
  ```typescript
271
340
  // User profile
272
341
  await client.instagram.profile({ username: "instagram" });
@@ -349,7 +418,7 @@ MIT
349
418
  - [Amazon Product API](https://scavio.dev/amazon-product-api) and [Walmart Product API](https://scavio.dev/walmart-product-api) — product search and details
350
419
  - [YouTube API](https://scavio.dev/youtube-transcript-api), [TikTok API](https://scavio.dev/tiktok-api), and [Instagram API](https://scavio.dev/instagram-api) — video and social media data
351
420
  - [Reddit API](https://scavio.dev/reddit-api) — posts and threaded comments
352
- - [X API](https://scavio.dev/x-api) and [LinkedIn API](https://scavio.dev/linkedin-api) — tweets, profiles, companies, and jobs
421
+ - [X API](https://scavio.dev/docs/x-search) and [LinkedIn API](https://scavio.dev/docs/linkedin-person) — tweets, profiles, companies, and jobs
353
422
 
354
423
  Teams choosing between providers can [compare Scavio vs alternatives](https://scavio.dev/compare) side by side.
355
424
 
package/dist/index.cjs CHANGED
@@ -466,6 +466,114 @@ var TikTokNamespace = class {
466
466
  }
467
467
  };
468
468
 
469
+ // src/namespaces/tiktok-shop.ts
470
+ var TikTokShopNamespace = class {
471
+ constructor(client) {
472
+ this.client = client;
473
+ }
474
+ client;
475
+ /**
476
+ * Search TikTok Shop products by keyword (US catalog), up to 30 per page with
477
+ * exact prices, ratings, and shop details. Paginate with next_cursor and dedupe
478
+ * by product_id across pages.
479
+ *
480
+ * This is one of the three endpoints that return exact prices; tiktokShop.product()
481
+ * does not return a price. A product_id returned here is not guaranteed to resolve
482
+ * on tiktokShop.product() - only about 44% do.
483
+ */
484
+ async search(options) {
485
+ return this.client._post("/api/v1/tiktok-shop/search", options);
486
+ }
487
+ /**
488
+ * Keyword autocomplete and expansion for a partial query, across 8 marketplace
489
+ * regions. Suggestions are not guaranteed prefix matches: a misspelling returns
490
+ * typo corrections, and results can include brand and shop names.
491
+ */
492
+ async searchSuggestions(options) {
493
+ return this.client._post("/api/v1/tiktok-shop/search/suggestions", options);
494
+ }
495
+ /**
496
+ * Full product detail: description, images, variants with stock, shipping, shop
497
+ * profile, category path, and top reviews.
498
+ *
499
+ * Two limits worth knowing before you build on this:
500
+ *
501
+ * 1. It resolves only about 44% of the product ids returned by tiktokShop.search().
502
+ * Upstream has no detail data for the rest, so an HTTP 404 is a normal outcome,
503
+ * not an error. Skip the item rather than retrying - retries do not help and no
504
+ * other region carries it. Search to product is not a reliable pipeline.
505
+ *
506
+ * This method throws `NotFoundError` on that 404 (there is no `data` field in
507
+ * the response body to test), so a loop over search ids must catch it or it
508
+ * dies on the first miss:
509
+ *
510
+ * ```ts
511
+ * import { NotFoundError } from "scavio";
512
+ *
513
+ * for (const productId of productIds) {
514
+ * try {
515
+ * const detail = await client.tiktokShop.product({ product_id: productId });
516
+ * } catch (e) {
517
+ * if (e instanceof NotFoundError) continue; // no detail upstream; skip
518
+ * throw e;
519
+ * }
520
+ * }
521
+ * ```
522
+ *
523
+ * tiktokShop.productReviews() often works for ids product() cannot resolve: of
524
+ * 8 such ids tested, 8 returned HTTP 200 and 7 carried at least one review, so
525
+ * it is a useful fallback source of product detail.
526
+ * 2. It does NOT return a price. Upstream masks the price on the product page.
527
+ * Exact prices come from tiktokShop.search(), tiktokShop.shopProducts(), and
528
+ * tiktokShop.categoryProducts().
529
+ */
530
+ async product(options) {
531
+ return this.client._post("/api/v1/tiktok-shop/product", options);
532
+ }
533
+ /**
534
+ * Paginated product reviews with text, images, star histogram, and
535
+ * verified-purchase flags, up to 200 per call. total_reviews drifts between calls
536
+ * and must not be used to compute a page count; page with has_more instead.
537
+ */
538
+ async productReviews(options) {
539
+ return this.client._post("/api/v1/tiktok-shop/product/reviews", options);
540
+ }
541
+ /**
542
+ * The global TikTok Shop category tree: 28 top-level categories, 240 nodes, two
543
+ * levels deep. Category ids are identical in every region and names are always
544
+ * English.
545
+ */
546
+ async categories() {
547
+ return this.client._post("/api/v1/tiktok-shop/categories", {});
548
+ }
549
+ /**
550
+ * Products listed under a category id from tiktokShop.categories(), with exact
551
+ * prices. Page size is inconsistent upstream (15 to 20 per page), so always
552
+ * paginate with next_cursor rather than assuming a fixed page size. Category
553
+ * listings are shallow: after a few pages the source stops returning new products
554
+ * and has_more turns false, which is the end of the listing rather than an error.
555
+ */
556
+ async categoryProducts(options) {
557
+ return this.client._post("/api/v1/tiktok-shop/category/products", options);
558
+ }
559
+ /**
560
+ * A shop's product catalog, 30 per page, with exact prices. Shop follower count,
561
+ * location, and shop-level rating are not available here; call
562
+ * tiktokShop.product() for the full shop profile.
563
+ */
564
+ async shopProducts(options) {
565
+ return this.client._post("/api/v1/tiktok-shop/shop/products", options);
566
+ }
567
+ /**
568
+ * Resolve any TikTok Shop URL or share link to a product_id or shop_id, ready to
569
+ * pass to the other methods. Accepts canonical product and store pages,
570
+ * tiktok.com/view links, affiliate share links, and vt.tiktok.com short links.
571
+ */
572
+ async resolve(options) {
573
+ return this.client._post("/api/v1/tiktok-shop/resolve", options);
574
+ }
575
+ };
576
+
469
577
  // src/namespaces/instagram.ts
470
578
  var InstagramNamespace = class {
471
579
  constructor(client) {
@@ -648,48 +756,78 @@ var LinkedInNamespace = class {
648
756
  this.client = client;
649
757
  }
650
758
  client;
759
+ /** Full profile: about text, experience, education, honours and links. */
651
760
  async person(options) {
652
761
  return this.client._post("/api/v1/linkedin/person", options);
653
762
  }
763
+ /** The about-only slice of the profile payload. */
654
764
  async personAbout(options) {
655
765
  return this.client._post("/api/v1/linkedin/person/about", options);
656
766
  }
767
+ /** Recent posts, up to 50. Upstream exposes no further pages. */
657
768
  async personPosts(options) {
658
769
  return this.client._post("/api/v1/linkedin/person/posts", options);
659
770
  }
660
- async personContact(options) {
661
- return this.client._post("/api/v1/linkedin/person/contact", options);
662
- }
771
+ /** Company profile, including locations and featured employees. */
663
772
  async company(options) {
664
773
  return this.client._post("/api/v1/linkedin/company", options);
665
774
  }
775
+ /** Recent company posts, up to 50. Upstream exposes no further pages. */
666
776
  async companyPosts(options) {
667
777
  return this.client._post("/api/v1/linkedin/company/posts", options);
668
778
  }
669
- async companyPeople(options) {
670
- return this.client._post("/api/v1/linkedin/company/people", options);
671
- }
672
- async companyJobs(options) {
673
- return this.client._post("/api/v1/linkedin/company/jobs", options);
674
- }
675
- async searchPeople(options) {
676
- return this.client._post("/api/v1/linkedin/search/people", options);
677
- }
779
+ /** Job search. Upstream rotates its result set, so repeat calls differ. */
678
780
  async searchJobs(options) {
679
781
  return this.client._post("/api/v1/linkedin/search/jobs", options);
680
782
  }
681
- async searchPosts(options) {
682
- return this.client._post("/api/v1/linkedin/search/posts", options);
683
- }
783
+ /** Full detail for one job listing, including the hiring company. */
684
784
  async job(options) {
685
785
  return this.client._post("/api/v1/linkedin/job", options);
686
786
  }
787
+ /** Full detail for one post, including its top visible comments. */
687
788
  async post(options) {
688
789
  return this.client._post("/api/v1/linkedin/post", options);
689
790
  }
791
+ /** Comments with their replies, 10 per page. */
690
792
  async postComments(options) {
691
793
  return this.client._post("/api/v1/linkedin/post/comments", options);
692
794
  }
795
+ /**
796
+ * @deprecated Retired by the upstream provider. Always returns HTTP 410 and is
797
+ * never billed.
798
+ */
799
+ async personContact(options) {
800
+ return this.client._post("/api/v1/linkedin/person/contact", options);
801
+ }
802
+ /**
803
+ * @deprecated Retired by the upstream provider. Always returns HTTP 410 and is
804
+ * never billed. `company()` returns `featured_employees`, a small sample of
805
+ * staff profiles.
806
+ */
807
+ async companyPeople(options) {
808
+ return this.client._post("/api/v1/linkedin/company/people", options);
809
+ }
810
+ /**
811
+ * @deprecated Retired by the upstream provider. Always returns HTTP 410 and is
812
+ * never billed. Use `searchJobs()` with the company name as the search term.
813
+ */
814
+ async companyJobs(options) {
815
+ return this.client._post("/api/v1/linkedin/company/jobs", options);
816
+ }
817
+ /**
818
+ * @deprecated Retired by the upstream provider. Always returns HTTP 410 and is
819
+ * never billed.
820
+ */
821
+ async searchPeople(options) {
822
+ return this.client._post("/api/v1/linkedin/search/people", options);
823
+ }
824
+ /**
825
+ * @deprecated Retired by the upstream provider. Always returns HTTP 410 and is
826
+ * never billed.
827
+ */
828
+ async searchPosts(options) {
829
+ return this.client._post("/api/v1/linkedin/search/posts", options);
830
+ }
693
831
  };
694
832
 
695
833
  // src/client.ts
@@ -700,6 +838,7 @@ var Scavio = class {
700
838
  youtube;
701
839
  reddit;
702
840
  tiktok;
841
+ tiktokShop;
703
842
  instagram;
704
843
  x;
705
844
  linkedin;
@@ -727,6 +866,7 @@ var Scavio = class {
727
866
  this.youtube = new YouTubeNamespace(this);
728
867
  this.reddit = new RedditNamespace(this);
729
868
  this.tiktok = new TikTokNamespace(this);
869
+ this.tiktokShop = new TikTokShopNamespace(this);
730
870
  this.instagram = new InstagramNamespace(this);
731
871
  this.x = new XNamespace(this);
732
872
  this.linkedin = new LinkedInNamespace(this);