npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@tiny-fish/sdk

v0.0.8

Published

TinyFish TypeScript SDK — State-of-the-art web agents in an API

Readme

TinyFish TypeScript SDK

Typed access to TinyFish web agents, fetch extraction, remote browser sessions, run history, and search.

Use the SDK when you want to:

  • automate a page with a natural-language goal
  • stream live progress events and a browser preview URL
  • queue jobs and poll them later
  • fetch clean page content from up to 10 URLs at once
  • create a remote browser session for direct CDP control
  • query TinyFish Search from the same client

Installation

npm install @tiny-fish/sdk

Requirements:

Authenticate with either the constructor or TINYFISH_API_KEY:

import { TinyFish } from "@tiny-fish/sdk";

const client = new TinyFish({
  apiKey: process.env.TINYFISH_API_KEY,
});

Quickstart

agent.stream() is the best default for product integrations because it gives you progress updates while the run is happening.

import { TinyFish } from "@tiny-fish/sdk";

const client = new TinyFish();

const stream = await client.agent.stream(
  {
    goal: "Extract the top 5 headlines",
    url: "https://news.ycombinator.com",
  },
  {
    onStarted: (event) => console.log(`Run: ${event.run_id}`),
    onStreamingUrl: (event) => console.log(`Watch live: ${event.streaming_url}`),
    onProgress: (event) => console.log(`> ${event.purpose}`),
    onComplete: (event) => console.log(event.result),
  },
);

for await (const event of stream) {
  if (event.type === "COMPLETE") {
    console.log(`Finished with status ${event.status}`);
  }
}

Need request-scoped cancellation without changing the client-wide timeout? Pass an AbortSignal as the optional second argument:

const controller = new AbortController();

const stream = await client.agent.stream(
  {
    goal: "Extract the top 5 headlines",
    url: "https://news.ycombinator.com",
  },
  {
    signal: controller.signal,
  },
);

controller.abort();

Choose an API

| Method | Use it when | Returns | | --- | --- | --- | | client.agent.stream() | You want live events and a browser preview URL | AgentStream | | client.agent.run() | You want one blocking request that waits for the final result | AgentRunResponse | | client.agent.queue() | You want to enqueue work and check back later | AgentRunAsyncResponse | | client.fetch.getContents() | You want extracted page content without running a browser agent | FetchResponse | | client.browser.sessions.create() | You want a remote browser session and CDP connection info | BrowserSession | | client.runs.get() | You already have a run_id and need the latest state | Run | | client.runs.list() | You want to list or filter historical runs | RunListResponse | | client.search.query() | You want TinyFish Search results | SearchQueryResponse |

Core workflows

Run and wait

Use agent.run() for scripts, cron jobs, and backend tasks that should block until the result is ready.

import {
  BrowserProfile,
  ProxyCountryCode,
  RunStatus,
  TinyFish,
} from "@tiny-fish/sdk";

const client = new TinyFish();

const response = await client.agent.run({
  goal: "Find the price of the latest MacBook Pro",
  url: "https://www.apple.com/shop/buy-mac/macbook-pro",
  browser_profile: BrowserProfile.STEALTH,
  proxy_config: {
    enabled: true,
    country_code: ProxyCountryCode.US,
  },
});

if (response.status === RunStatus.COMPLETED) {
  console.log(response.result);
} else {
  console.error(response.error?.message);
}

run() also accepts an optional second argument with signal:

const controller = new AbortController();

const response = await client.agent.run(
  {
    goal: "Extract the page title",
    url: "https://example.com",
  },
  {
    signal: controller.signal,
  },
);

Queue and poll

Use agent.queue() when you do not want to keep the request open.

import { RunStatus, TinyFish } from "@tiny-fish/sdk";

const client = new TinyFish();

const queued = await client.agent.queue({
  goal: "Extract all job titles from the careers page",
  url: "https://example.com/careers",
});

if (queued.error) {
  throw new Error(queued.error.message);
}

let run = await client.runs.get(queued.run_id);

while (run.status === RunStatus.PENDING || run.status === RunStatus.RUNNING) {
  await new Promise((resolve) => setTimeout(resolve, 5000));
  run = await client.runs.get(run.run_id);
}

if (run.status === RunStatus.COMPLETED) {
  console.log(run.result);
} else {
  console.error(run.error?.message ?? `Run ended with status: ${run.status}`);
}

queue() also accepts an optional second argument with signal for cancelling just the enqueue request.

Fetch clean content

Use fetch.getContents() when you want extracted content from URLs without a browser-agent run.

import { FetchFormat, TinyFish } from "@tiny-fish/sdk";

const client = new TinyFish();

const response = await client.fetch.getContents({
  urls: ["https://example.com", "https://example.org"],
  format: FetchFormat.Markdown,
  links: true,
  image_links: false,
});

console.log(response.results);
console.log(response.errors);

fetch.getContents() accepts 1 to 10 URLs. FetchResult.text is:

  • string for FetchFormat.Markdown and FetchFormat.Html
  • Record<string, unknown> for FetchFormat.Json
  • null if extraction failed for that item

Create a browser session

Use browser.sessions.create() when you want connection details for direct browser control.

import { TinyFish } from "@tiny-fish/sdk";

const client = new TinyFish();

const session = await client.browser.sessions.create({
  url: "https://example.com",
});

console.log(session.session_id);
console.log(session.cdp_url);
console.log(session.base_url);

Inspect and list runs

Fetch a single run when you already know its run_id:

const run = await client.runs.get("run_abc123");

console.log(run.status);
console.log(run.result);
console.log(run.streaming_url);

Use runs.list() for filtering and pagination:

import { RunStatus, SortDirection, TinyFish } from "@tiny-fish/sdk";

const client = new TinyFish();

const response = await client.runs.list({
  status: RunStatus.COMPLETED,
  goal: "headlines",
  sort_direction: SortDirection.DESC,
  limit: 10,
});

for (const run of response.data) {
  console.log(`${run.run_id} | ${run.status} | ${run.goal}`);
}

if (response.pagination.has_more) {
  const nextPage = await client.runs.list({
    cursor: response.pagination.next_cursor ?? undefined,
  });
  console.log(`Fetched ${nextPage.data.length} more runs`);
}

Query search

import { TinyFish } from "@tiny-fish/sdk";

const client = new TinyFish();

const response = await client.search.query({
  query: "tinyfish sdk",
  location: "US",
  language: "en",
});

console.log(response.query);
console.log(response.total_results);
console.log(response.results[0]?.title);

Streaming events

agent.stream() guarantees this event order:

  • STARTED
  • STREAMING_URL
  • PROGRESS repeated zero or more times
  • COMPLETE

HEARTBEAT events may also appear, but they are keepalive events rather than part of the guaranteed ordered sequence.

You can consume the stream in two ways:

  • callbacks like onProgress and onComplete
  • direct iteration with for await...of

To stop a stream early, call await stream.close().

Configuration

import { TinyFish } from "@tiny-fish/sdk";

const client = new TinyFish({
  apiKey: process.env.TINYFISH_API_KEY,
  baseURL: "https://agent.tinyfish.ai",
  timeout: 600_000,
  maxRetries: 2,
});

Defaults:

  • baseURL: https://agent.tinyfish.ai
  • timeout: 600000 ms
  • maxRetries: 2

The SDK automatically retries 408, 429, and 5xx responses with exponential backoff. Authentication, validation, and not-found errors fail immediately.

Browser profiles and proxies

agent.run(), agent.queue(), and agent.stream() all accept the same execution parameters:

  • goal and url are required
  • browser_profile can be BrowserProfile.LITE or BrowserProfile.STEALTH
  • proxy_config can enable a proxy and optionally pin a country

Supported proxy country codes:

  • US
  • GB
  • CA
  • DE
  • FR
  • JP
  • AU

Error handling

import {
  AuthenticationError,
  RateLimitError,
  SDKError,
  TinyFish,
} from "@tiny-fish/sdk";

const client = new TinyFish();

try {
  await client.agent.run({
    goal: "Extract the page title",
    url: "https://example.com",
  });
} catch (error) {
  if (error instanceof AuthenticationError) {
    console.error("Invalid API key");
  } else if (error instanceof RateLimitError) {
    console.error("Rate limited");
  } else if (error instanceof SDKError) {
    console.error(error.message);
  } else {
    throw error;
  }
}

Guides