🎩 You're Invited:Meet the Socket team at Black Hat in Las Vegas, August 3-6.RSVP
Sign In

scavio

Package Overview
Dependencies
Maintainers
1
Versions
14
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

scavio

TypeScript SDK for the Scavio Search API — real-time Google, Amazon, Walmart, YouTube, Reddit, TikTok, TikTok Shop, Instagram, X, and LinkedIn data

latest
Source
npmnpm
Version
0.14.0
Version published
Maintainers
1
Created
Source

Scavio

TypeScript SDK for the Scavio Search API — real-time Google, Amazon, Walmart, YouTube, Reddit, TikTok, TikTok Shop, Instagram, X, and LinkedIn data.

Install

npm install scavio

Quick Start

import { Scavio } from "scavio";

const client = new Scavio({ apiKey: "sk_..." });

// Google search
const results = await client.search({ query: "web scraping api" });

// Amazon product lookup
const product = await client.amazon.product({ asin: "B09V3KXJPB" });

// Check usage
const usage = await client.getUsage();

Configuration

const client = new Scavio({
  apiKey: "sk_...",               // or set SCAVIO_API_KEY env var
  baseUrl: "https://api.scavio.dev", // default
  timeout: 30_000,                   // ms, default
  maxRequestsPerSecond: 1,           // 1-10, default 1
  maxRetries: 2,                     // default 2, set 0 to disable
});

maxRetries is the number of extra attempts after the first request, applied only to transient failures — HTTP 429, 500, 502, 503, 504 and network or timeout errors. Backoff is exponential with full jitter, capped at 8s, and a Retry-After header is honored when the API sends one. Non-transient errors (400, 401, 402, 404) are never retried.

maxRequestsPerSecond throttles the client so it never sends more than N requests in any one-second window. Your plan also has a server-side concurrency limit on simultaneous in-flight requests: 1 on free and pay-as-you-go, 2 on Project, 3 on Bootstrap, 5 on Startup, 10 on Growth.

API Reference

Google

Each method hits its own /api/v2/google* endpoint and returns Google's full response (raw passthrough); each costs 1 credit. Results come back as organic_results[] with link and snippet. Geo and paging use gl, hl, start, google_domain and device — the old v1 vocabulary (light_request, country_code, language, search_type, page) does not exist on v2 and is dropped server-side.

// SERP search (includes the AI Overview when Google shows one)
await client.google.search({
  query: "web scraping",
  gl: "us",                // optional — geo (country)
  hl: "en",                // optional — UI language
  device: "desktop",       // optional
});

// Other Google surfaces
await client.google.aiMode({ query: "how does rag work" });
await client.google.mapsSearch({ query: "coffee near me" });
await client.google.shopping({ query: "laptop" });
await client.google.flights({ departure_id: "JFK", arrival_id: "LAX", outbound_date: "2026-12-15" });
await client.google.hotels({ query: "Bali", check_in_date: "2026-08-01", check_out_date: "2026-08-03" });
await client.google.news({ query: "openai" });
await client.google.trends({ query: "bitcoin" });

Amazon

Responses are normalized to a stable shape (see the breaking-change note below); each call costs 1 credit. country is an ISO 3166-1 alpha-2 marketplace code (us, gb, de, ...) and defaults to us.

// Search products
await client.amazon.search({
  query: "laptop",
  country: "de",           // optional, marketplace
  page: 2,                 // optional, 1-based
});

// Get product by ASIN
await client.amazon.product({
  asin: "B09V3KXJPB",
  country: "us",           // optional
});

// Every seller offering that ASIN, with the buy box winner flagged
const res = await client.amazon.offers({ asin: "B09V3KXJPB" });
// res.data   -> { asin, title, count, total_offers, has_more_pages, page, offers[] }
// res.data.offers[i] -> { price, currency, condition, seller_name,
//                         is_buy_box_winner, is_fulfilled_by_amazon,
//                         shipping_price, list_price, delivery, prime_delivery }

// Supported marketplaces (no API key required)
await client.amazon.options();

There is no sort parameter: the marketplace was verified to ignore every sort value and return the same unordered set, so exposing one would be a filter that silently does nothing.

Amazon changed in 0.12.0 (breaking)

Amazon moved to a new upstream and the API now returns a normalized shape instead of the previous raw provider payload.

  • search returns {query, page, total_results, total_results_text, count, products[], filters[], related_searches[]}. Each product is {asin, title, url, image, price, currency, rating, reviews_count, is_sponsored, position, badge, sales_volume, delivery{is_free, date, fastest_date}}.
  • product returns flat fields: price, list_price, currency, rating, reviews_count, features, images, videos, variants, specifications, best_sellers_rank, shipping, and more. The old buybox[] array no longer exists — use offers for per-seller pricing.
  • offers is new: every seller for one ASIN. 1 credit, page 1 only.
  • country (ISO 3166-1 alpha-2) is the marketplace selector and replaces domain; page replaces start_page. Both old names still work as deprecated aliases.
  • Nine options were removed: language, currency, device, sort_by, pages, category_id, merchant_id, zip_code, autoselect_variant. Sending one anyway still returns 200, with a top-level warnings array naming what was ignored.
  • options() still returns domains and countries; languages and currencies are now always empty, because neither is a request parameter any more.

Walmart

// Search products
await client.walmart.search({
  query: "tv",
  min_price: 100,          // optional
  max_price: 500,          // optional
});

// Get product by ID
await client.walmart.product({
  product_id: "123456",
});

YouTube

Credit cost varies by endpoint: transcript costs 8; streams costs 3; search and shorts cost 2; every other YouTube endpoint costs 1.

// Search videos
await client.youtube.search({
  query: "typescript tutorial",
  upload_date: "this_week",       // optional
  sort_by: "relevance",           // optional
  features: ["hd", "4k"],         // optional
  cursor: "...",                  // optional (pagination)
});

// Search Shorts
await client.youtube.shorts({ query: "cooking" });

// Search-as-you-type suggestions
await client.youtube.suggestions({ query: "how to" });

// Get video details (accepts an id or a full watch URL)
await client.youtube.video({ video_id: "dQw4w9WgXcQ" });
// youtube.metadata() is a deprecated alias of youtube.video()

// Video comments and threaded replies
await client.youtube.comments({ video_id: "dQw4w9WgXcQ" });
await client.youtube.commentReplies({
  video_id: "dQw4w9WgXcQ",
  reply_cursor: "...",            // from a comment's reply_cursor
});

// Transcript / subtitles (format: 'text' or 'srt')
await client.youtube.transcript({ video_id: "dQw4w9WgXcQ", format: "srt" });

// Related videos
await client.youtube.related({ video_id: "dQw4w9WgXcQ" });

// Playable / downloadable stream URLs
await client.youtube.streams({ video_id: "dQw4w9WgXcQ" });

// Channels
await client.youtube.channelSearch({ query: "mkbhd" });
await client.youtube.channel({ channel_id: "@mkbhd" }); // id, @handle, or URL
await client.youtube.channelVideos({ channel_id: "UC..." });
await client.youtube.channelShorts({ channel_id: "UC..." });
await client.youtube.channelCommunity({ channel_id: "UC..." });
await client.youtube.channelResolve({ channel: "@mkbhd" }); // handle/URL -> id

Reddit

Every Reddit endpoint costs 1 credit.

search() takes only query and cursor — there is no result-type or sort filter upstream, so anything else is dropped server-side. It returns data.results with next_cursor and has_more. post() returns a flat post object under data and carries no comments; use postComments() for those. The subreddit and user feeds return data.posts.

// Search posts
await client.reddit.search({ query: "typescript", cursor: "..." });

// Search-as-you-type suggestions
await client.reddit.searchSuggestions({ query: "python" });

// Post detail (by post_id or url)
await client.reddit.post({ post_id: "t3_1v6ngaf" });
await client.reddit.post({ url: "https://reddit.com/r/typescript/comments/abc123" });

// Post comments and threaded replies
await client.reddit.postComments({ post_id: "t3_1v6ngaf", sort: "TOP" });
await client.reddit.commentReplies({
  post_id: "t3_1v6ngaf",
  cursor: "...",            // reply_cursor from a comment
});

// Subreddit info and feed
await client.reddit.subreddit({ subreddit: "AskReddit" });
await client.reddit.subredditPosts({ subreddit: "AskReddit", sort: "HOT" });

// Redditor profile, posts, and comments
await client.reddit.user({ username: "spez" });
await client.reddit.userPosts({ username: "spez", sort: "NEW" });
await client.reddit.userComments({ username: "spez" });

// Site-wide popular feed and trending searches
await client.reddit.popular();
await client.reddit.trending();

X

// Search tweets and people
await client.x.search({ search: "artificial intelligence", search_type: "Latest" });

// Tweet detail, comments, and retweeters
await client.x.tweet({ tweet_id: "1808168603721650364" });
await client.x.tweetComments({ tweet_id: "1808168603721650364", rank: "top" });
await client.x.tweetRetweeters({ tweet_id: "1808168603721650364" });

// User profile and feeds
await client.x.user({ screen_name: "elonmusk" });
await client.x.userTweets({ screen_name: "elonmusk" });
await client.x.userReplies({ screen_name: "elonmusk" });
await client.x.userMedia({ screen_name: "elonmusk" });
await client.x.userFollowers({ screen_name: "elonmusk" });
await client.x.userFollowings({ screen_name: "elonmusk" });

// Trending topics
await client.x.trending({ country: "UnitedStates" });

LinkedIn

// Person profile, about, and posts. A handle or a full LinkedIn URL works
// anywhere a reference is taken.
await client.linkedin.person({ username: "williamhgates" });
await client.linkedin.personAbout({ url: "https://www.linkedin.com/in/williamhgates/" });
await client.linkedin.personPosts({ username: "williamhgates" });

// Company profile and posts
await client.linkedin.company({ company: "microsoft" });
await client.linkedin.companyPosts({ company: "microsoft" });

// Jobs: search, then pull detail for one listing
await client.linkedin.searchJobs({ search: "software engineer", location: "United States" });
await client.linkedin.job({ job_id: "4415427228" });

// A post and its comments (10 per page)
await client.linkedin.post({ post_id: "7488618410256523265" });
await client.linkedin.postComments({ post_id: "7488618410256523265", page: 1 });

Credit cost varies by endpoint: job costs 30; personPosts, companyPosts, searchJobs and postComments cost 10 per page; person, personAbout, company and post cost 1.

Retired endpoints. The upstream provider withdrew the datasets behind personContact, companyPeople, companyJobs, searchPeople and searchPosts. They remain callable but always return HTTP 410 and are never billed. company() returns featured_employees (a small sample of staff), and searchJobs() with a company name substitutes for companyJobs().

personPosts, companyPosts and searchJobs paginate: pass the previous response's next_cursor as cursor to fetch the next page. personPosts also takes type ("posts", "comments" or "reactions") to pick the feed. postComments pages with a 1-based page instead.

TikTok

// User profile
await client.tiktok.profile({ username: "testuser" });

// User posts
await client.tiktok.userPosts({ sec_user_id: "abc123", count: 30 });

// Video details
await client.tiktok.video({ video_id: "vid123" });

// Video comments
await client.tiktok.videoComments({ video_id: "vid123", count: 20 });

// Comment replies
await client.tiktok.commentReplies({ video_id: "vid123", comment_id: "c456" });

// Search videos (sort_type: '0' = relevance, '1' = most likes)
await client.tiktok.searchVideos({ keyword: "dance", sort_type: "1" });

// Search users
await client.tiktok.searchUsers({ keyword: "cooking" });

// Hashtag info
await client.tiktok.hashtag({ hashtag_name: "fyp" });

// Hashtag videos
await client.tiktok.hashtagVideos({ hashtag_id: "h789", count: 30 });

// User followers
await client.tiktok.userFollowers({ sec_user_id: "abc123" });

// User followings
await client.tiktok.userFollowings({ sec_user_id: "abc123" });

TikTok Shop

Every TikTok Shop endpoint costs 1 credit. Two limits to design around:

  • product() resolves only about 44% of the product ids returned by search(). Upstream has no detail data for the rest, so an HTTP 404 is a normal outcome, not an error — skip the item instead of retrying. Search to product is not a reliable pipeline. product() throws NotFoundError on that 404 (there is no data field in the body to test), so a loop over search ids must catch it:

    import { NotFoundError } from "scavio";
    
    for (const productId of productIds) {
      try {
        const detail = await client.tiktokShop.product({ product_id: productId });
      } catch (e) {
        if (e instanceof NotFoundError) continue; // no detail upstream; skip
        throw e;
      }
    }
    

    productReviews() often works for ids product() cannot resolve: of 8 such ids tested, 8 returned HTTP 200 and 7 carried at least one review, so it is a useful fallback source of product detail.

  • product() does not return a price; upstream masks it on the product page. Exact prices come from search(), shopProducts(), and categoryProducts().

// Search products (US catalog, exact prices, cursor pagination)
await client.tiktokShop.search({ search: "phone case" });

// Keyword suggestions (8 regions)
await client.tiktokShop.searchSuggestions({ search: "wireless", region: "US" });

// Product detail (no price; a 404 is normal, see above)
await client.tiktokShop.product({ product_id: "1732293553906094315" });

// Product reviews (up to 200 per call)
await client.tiktokShop.productReviews({
  product_id: "1732293553906094315",
  page_size: 200,
  sort: "relevant",
});

// Category tree (28 top-level, 240 nodes)
await client.tiktokShop.categories();

// Products in a category (US and GB only)
await client.tiktokShop.categoryProducts({ category_id: "601450" });

// A shop's catalog, 30 per page
await client.tiktokShop.shopProducts({ shop_id: "7495514739648989419" });

// Resolve any TikTok Shop URL or share link to a product_id / shop_id
await client.tiktokShop.resolve({ url: "https://vt.tiktok.com/ZT2AHoGsE/" });

Instagram

Credit cost varies by endpoint, in three tiers:

CreditsMethods
2userPosts
8post, commentReplies
10profile, userReels, userTagged, userStories, postComments, searchUsers, searchHashtags, userFollowers, userFollowings

The 10-credit endpoints run two upstream providers in parallel and bill both legs; the 8-credit ones have no fallback leg to hedge against.

// User profile
await client.instagram.profile({ username: "instagram" });

// User posts / reels / tagged
await client.instagram.userPosts({ username: "instagram", count: 12 });
await client.instagram.userReels({ username: "instagram" });
await client.instagram.userTagged({ username: "instagram" });

// User stories
await client.instagram.userStories({ username: "instagram" });

// Post detail (by url, media_id, or shortcode)
await client.instagram.post({ shortcode: "DUajw4YkorV" });

// Post comments and replies
await client.instagram.postComments({ shortcode: "DUajw4YkorV", sort_order: "newest" });
await client.instagram.commentReplies({ media_id: "123", comment_id: "456" });

// Search
await client.instagram.searchUsers({ keyword: "justin" });
await client.instagram.searchHashtags({ keyword: "fashion" });

// Followers / followings
await client.instagram.userFollowers({ username: "instagram", count: 50 });
await client.instagram.userFollowings({ username: "instagram" });

Usage

const usage = await client.getUsage();

Error Handling

import { Scavio, InvalidAPIKeyError, RateLimitError } from "scavio";

try {
  const results = await client.search({ query: "test" });
} catch (error) {
  if (error instanceof InvalidAPIKeyError) {
    // 401 — bad API key
  } else if (error instanceof RateLimitError) {
    // 429 — too many requests
  }
}

All error classes:

ClassHTTP StatusDescription
MissingAPIKeyErrorNo API key provided
ScavioConnectionErrorRequest never reached the API (DNS, reset, TLS)
ScavioTimeoutErrorRequest exceeded the configured timeout
BadRequestError400Invalid request parameters
InvalidAPIKeyError401Invalid API key
InsufficientCreditsError402No credits remaining
NotFoundError404No data upstream for that id (see TikTok Shop above)
RateLimitError429Rate limit exceeded
ScavioAPIErrorotherCatch-all (has .statusCode)

Every class extends ScavioError, so catch (e) { if (e instanceof ScavioError) } matches all of them. All except MissingAPIKeyError, ScavioConnectionError and ScavioTimeoutError carry .statusCode and .responseBody.

Runtime Support

  • Node.js 18+
  • Deno
  • Bun

Zero dependencies — uses native fetch.

License

MIT

About Scavio

Scavio is a unified search API for AI agents — one API key, structured JSON, no scraping or proxies. A real-time Tavily alternative and SerpAPI alternative with data from:

Teams choosing between providers can compare Scavio vs alternatives side by side.

Get a free API key and explore the documentation.

Keywords

search

FAQs

Package last updated on 04 Aug 2026

Did you know?

Socket

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Install

Related posts