Docs

Run from your own IP

Send a run's requests to the website yourself, from your own IP, while Unbrowse still decides every request and reads every response. Useful when a site must see your address (your region, your allowlisted IP, your network), or when you would rather no third party connects to it on your behalf.

How it works

Start a run as usual and add "egress": "client". Instead of calling the site, Unbrowse answers with the next request to send:

curl -X POST https://api.unbrowse.ai/api/v1/runs \
  -H "Authorization: Bearer $UNBROWSE_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"capability":"hn.top_stories","input":{"limit":3},"egress":"client"}'
{
  "status": "egress_required",
  "egressId": "eg_mB1s…",
  "requests": [
    {
      "id": "rq_1",
      "method": "GET",
      "url": "https://hn.algolia.com/api/v1/search?tags=front_page&hitsPerPage=12",
      "headers": { "accept": "application/json", "user-agent": "…" },
      "redirect": "manual",
      "curl": "curl -sS -i -X GET 'https://hn.algolia.com/api/v1/search?tags=front_page&hitsPerPage=12' -H 'accept: application/json' …"
    }
  ]
}

Send that request from your machine, then post the site's raw response back:

curl -X POST https://api.unbrowse.ai/api/v1/egress/eg_mB1s… \
  -H "Authorization: Bearer $UNBROWSE_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"requestId":"rq_1","response":{"status":200,"headers":[["content-type","application/json"]],"body":"{\"hits\":[…]}"}}'

Unbrowse reads it and answers with the next request (egress_required again), or with the finished run — the same verified result a normal run returns:

{ "runId": "run_…", "status": "succeeded", "capabilityId": "hn.top_stories", "result": { "stories": [ … ] } }

A multi-step flow (search, then read a detail) is one request at a time: you only ever hold the request in front of you.

Your logins stay on Unbrowse

Every request in a client-egress run is handed to your machine with all its headers and body, so nothing from your vault is ever put in one. When the tool or targetUrl is for a site where you have a kept sign-in, a saved login or a site key, the run goes from Unbrowse instead, through your account's own fixed exit IP, and the answer says so:

{ "runId": "run_…", "status": "succeeded", "egress": { "mode": "server", "reason": "vault" }, "result": { … } }

A plain task names no site up front, so it stays on your machine and runs signed out.

With the SDK

runOnClient does the loop: it sends each request with your fetch and posts the response back.

import { Unbrowse } from "@unbrowse/sdk";

const unbrowse = new Unbrowse({ apiKey: process.env.UNBROWSE_API_KEY });
const run = await unbrowse.runOnClient({ capability: "hn.top_stories", input: { limit: 3 } });
console.log(run.result);

Pass fetch to send through your own proxy or network stack, and onRequest to see or veto each request before it goes out.

Answering

FieldWhat to send
requestIdThe id of the request you sent.
response.statusThe HTTP status the site returned.
response.headers[name, value] pairs (keeps repeated headers such as set-cookie) or an object.
response.bodyThe body, decoded (after gzip/brotli). Text, or base64 with "bodyEncoding": "base64". Up to 10 MB.
response.urlThe final URL, if you followed redirects.
errorInstead of response, when the request could not be sent at all (DNS, TLS, refused). The run treats it as a network error.

When redirect is manual, do not follow redirects: return the 3xx as it came (Unbrowse follows it itself and asks you for the next hop). You have 120 seconds to answer each request; an abandoned run can be closed with DELETE /api/v1/egress/{egressId}. GET /api/v1/egress/{egressId} shows what the run is waiting for.

What stays where