@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/sdkRequirements:
- Node.js 18+
- a TinyFish API key from agent.tinyfish.ai/api-keys
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:
stringforFetchFormat.MarkdownandFetchFormat.HtmlRecord<string, unknown>forFetchFormat.Jsonnullif 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:
STARTEDSTREAMING_URLPROGRESSrepeated zero or more timesCOMPLETE
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
onProgressandonComplete - 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.aitimeout:600000msmaxRetries: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:
goalandurlare requiredbrowser_profilecan beBrowserProfile.LITEorBrowserProfile.STEALTHproxy_configcan enable a proxy and optionally pin a country
Supported proxy country codes:
USGBCADEFRJPAU
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;
}
}