TinyFish Web Agent offers three ways to run automations. Each endpoint serves a different need. Pick the one that matches how you want to handle the request and response.
The Three Endpoints
| Endpoint | Pattern | Best For |
|---|
/run | Synchronous | Quick tasks, simple integrations |
/run-async | Start then check | Long tasks, batch processing |
/run-sse | Live updates | Real-time progress, user-facing apps |
Synchronous: /run
Pattern: Send request → Wait → Get result
The simplest approach. You call the API, it blocks until the automation completes, then returns the result.
const response = await fetch("https://agent.tinyfish.ai/v1/automation/run", {
method: "POST",
headers: {
"X-API-Key": process.env.TINYFISH_API_KEY,
"Content-Type": "application/json",
},
body: JSON.stringify({
url: "https://example.com",
goal: "Extract the page title",
}),
});
const run = await response.json();
console.log(run.result); // Your data
Runs created via /run cannot be cancelled. The request blocks until the automation completes, so there is no window to issue a cancellation. If you need the ability to cancel runs, use /run-async or /run-sse instead.
When to use
When to avoid
- Tasks that complete in under 30 seconds
- Simple scripts and one-off automations
- When you don’t need progress updates
- Long-running tasks (risk of timeout)
- User-facing apps (no progress feedback)
- Batch processing (blocks other requests)
- When you need the ability to cancel a run
Asynchronous: /run-async
Pattern: Send request → Get run ID → Poll for result
The request returns immediately with a run_id. You then poll a separate endpoint to check status and get the result when ready.
1. Start the automation
const response = await fetch("https://agent.tinyfish.ai/v1/automation/run-async", {
method: "POST",
headers: {
"X-API-Key": process.env.TINYFISH_API_KEY,
"Content-Type": "application/json",
},
body: JSON.stringify({
url: "https://example.com",
goal: "Extract all product data",
}),
});
const { run_id } = await response.json();
2. Poll for the result
const response = await fetch(`https://agent.tinyfish.ai/v1/runs/${run_id}`, {
headers: { "X-API-Key": process.env.TINYFISH_API_KEY },
});
const run = await response.json();
// run.status: PENDING, RUNNING, COMPLETED, FAILED, or CANCELLED
// run.result: Your data (when COMPLETED)
3. Cancel a run (optional)
If you need to stop a run before it completes, send a POST to the cancel endpoint:
await fetch(`https://agent.tinyfish.ai/v1/runs/${run_id}/cancel`, {
method: "POST",
headers: { "X-API-Key": process.env.TINYFISH_API_KEY },
});
Learn more about run statuses and lifecycle in Runs.
When to use
When to avoid
- Long-running automations (30+ seconds)
- Batch processing multiple URLs
- Fire-and-forget workflows
- When you need to track runs separately
- When you need real-time progress updates
- Simple, quick extractions (adds complexity)
Streaming: /run-sse
Pattern: Send request → Receive event stream → Process events as they arrive
Uses Server-Sent Events (SSE) to push updates to you in real-time. You’ll receive events for each action the browser takes, plus a streaming URL you can embed in an iframe to watch the automation live.
1. Start the automation
const response = await fetch("https://agent.tinyfish.ai/v1/automation/run-sse", {
method: "POST",
headers: {
"X-API-Key": process.env.TINYFISH_API_KEY,
"Content-Type": "application/json",
},
body: JSON.stringify({
url: "https://example.com",
goal: "Extract all product data",
}),
});
2. Read the event stream
const reader = response.body.getReader();
const decoder = new TextDecoder();
while (true) {
const { done, value } = await reader.read();
if (done) break;
const text = decoder.decode(value);
const lines = text.split("\n");
for (const line of lines) {
if (line.startsWith("data: ")) {
const event = JSON.parse(line.slice(6));
console.log(event.type, event);
}
}
}
Event Types
| Event | Description |
|---|
STARTED | Automation has begun, includes runId |
STREAMING_URL | URL to watch the browser live (valid 24hrs) |
PROGRESS | Browser action taken (click, type, scroll, etc.) |
HEARTBEAT | Connection keep-alive (no action needed) |
COMPLETE | Automation finished, includes status and resultJson |
Cancelling an SSE Run
You can cancel a streaming run using the run_id from the STARTED event:
await fetch(`https://agent.tinyfish.ai/v1/runs/${runId}/cancel`, {
method: "POST",
headers: { "X-API-Key": process.env.TINYFISH_API_KEY },
});
Handling Events
Use this pattern to process each event type as the automation progresses.
const eventHandlers = {
STARTED: (event) => {
console.log(`Run started: ${event.runId}`);
},
STREAMING_URL: (event) => {
console.log(`Watch live: ${event.streamingUrl}`);
},
PROGRESS: (event) => {
console.log(`Action: ${event.purpose}`);
},
COMPLETE: (event) => {
if (event.status === "COMPLETED") {
return event.resultJson;
} else {
throw new Error(event.error?.message || "Automation failed");
}
},
HEARTBEAT: () => {
// Connection is alive, no action needed
},
};
When to use
When to avoid
- User-facing apps (show progress)
- When you want to watch the browser live
- Debugging and development
- Long tasks where you want visibility
- Batch processing (complexity overhead)
- Backend jobs that don’t need progress
Quick Decision Guide
Need real-time progress updates?
Yes → Use /run-sse
Task takes longer than 30 seconds?
Yes → Use /run-async + polling
Submitting multiple tasks at once?
Yes → Use /run-async (parallel submission)
Simple, quick task?
Use /run (synchronous)
Comparison Table
| Feature | /run | /run-async | /run-sse |
|---|
| Response type | Final result | Run ID | Event stream |
| Progress updates | No | No (poll status) | Yes |
| Streaming URL | In response | Poll to get | In events |
| Cancellable | No | Yes | Yes |
| Best for | Quick tasks | Batch/long tasks | Real-time UI |
| Complexity | Low | Medium | Medium |