Live tab
One remote browser tab you — or anyone you send the link to — can see and control in a web browser: log in by hand, solve your own CAPTCHA, show a page from another country, then let your code carry on. POST https://cdpfleet.com/v1/functions/livetab → application/json.
curl -s https://cdpfleet.com/v1/functions/livetab \
-H "x-api-key: $CDPFLEET_API_KEY" -H "content-type: application/json" \
-d '{"url":"https://example.com/","country":"de","max_duration":900}' -o livetab.json -D - | grep -i '^x-call-id'
// Node.js 18+: save as livetab.mjs, run node livetab.mjs
const res = await fetch('https://cdpfleet.com/v1/functions/livetab', {
method: 'POST',
headers: { 'x-api-key': process.env.CDPFLEET_API_KEY, 'content-type': 'application/json' },
body: JSON.stringify({
url: 'https://example.com/',
country: 'de',
max_duration: 900,
}),
signal: AbortSignal.timeout(30000),
});
if (!res.ok) throw new Error(`${res.status} ${await res.text()}`); // 429: wait Retry-After seconds
const { live_url } = await res.json();
console.log(live_url); // open it in any browser: anyone with the link can use the tab
# Python 3 with requests (pip install requests): save as livetab.py, run python livetab.py
import os
import requests
r = requests.post(
"https://cdpfleet.com/v1/functions/livetab",
headers={"x-api-key": os.environ["CDPFLEET_API_KEY"]},
json={
"url": "https://example.com/",
"country": "de",
"max_duration": 900,
},
timeout=30,
)
if not r.ok:
raise SystemExit(f"{r.status_code} {r.text}") # 429: wait r.headers["Retry-After"] seconds
print(r.json()["live_url"]) # open it in any browser: anyone with the link can use the tab
// Java 17+: save as Main.java, run java -cp gson-2.11.0.jar Main.java (Gson: com.google.code.gson:gson)
import com.google.gson.JsonParser;
import java.net.URI;
import java.net.http.*;
import java.nio.charset.StandardCharsets;
import java.time.Duration;
public class Main {
public static void main(String[] args) throws Exception {
String body = """
{
"url": "https://example.com/",
"country": "de",
"max_duration": 900
}
""";
HttpResponse<byte[]> res = HttpClient.newHttpClient().send(
HttpRequest.newBuilder(URI.create("https://cdpfleet.com/v1/functions/livetab"))
.header("x-api-key", System.getenv("CDPFLEET_API_KEY"))
.header("content-type", "application/json")
.timeout(Duration.ofSeconds(30))
.POST(HttpRequest.BodyPublishers.ofString(body))
.build(),
HttpResponse.BodyHandlers.ofByteArray());
if (res.statusCode() != 200) { // 429: wait Retry-After seconds
throw new RuntimeException(res.statusCode() + " " + new String(res.body()));
}
String liveUrl = JsonParser.parseString(new String(res.body(), StandardCharsets.UTF_8)).getAsJsonObject().get("live_url").getAsString();
System.out.println(liveUrl); // open it in any browser: anyone with the link can use the tab
}
}
// .NET 8+: dotnet new console, replace Program.cs with this, run dotnet run
using System.Text;
using System.Text.Json;
using var http = new HttpClient { Timeout = TimeSpan.FromSeconds(30) };
http.DefaultRequestHeaders.Add("x-api-key", Environment.GetEnvironmentVariable("CDPFLEET_API_KEY"));
var body = """
{
"url": "https://example.com/",
"country": "de",
"max_duration": 900
}
""";
var res = await http.PostAsync("https://cdpfleet.com/v1/functions/livetab",
new StringContent(body, Encoding.UTF8, "application/json"));
if (!res.IsSuccessStatusCode) // 429: wait Retry-After seconds
throw new Exception($"{(int)res.StatusCode} {await res.Content.ReadAsStringAsync()}");
using var json = JsonDocument.Parse(await res.Content.ReadAsStringAsync());
Console.WriteLine(json.RootElement.GetProperty("live_url").GetString()); // open it in any browser: anyone with the link can use the tab
// Go 1.21+: save as main.go, run go run main.go
package main
import (
"encoding/json"
"fmt"
"io"
"log"
"net/http"
"os"
"strings"
"time"
)
func main() {
body := `{
"url": "https://example.com/",
"country": "de",
"max_duration": 900
}`
req, _ := http.NewRequest("POST", "https://cdpfleet.com/v1/functions/livetab", strings.NewReader(body))
req.Header.Set("x-api-key", os.Getenv("CDPFLEET_API_KEY"))
req.Header.Set("content-type", "application/json")
res, err := (&http.Client{Timeout: 30 * time.Second}).Do(req)
if err != nil {
log.Fatal(err)
}
defer res.Body.Close()
data, err := io.ReadAll(res.Body)
if err != nil {
log.Fatal(err)
}
if res.StatusCode != http.StatusOK { // 429: wait Retry-After seconds
log.Fatalf("%s %s", res.Status, data)
}
var tab struct {
LiveURL string `json:"live_url"`
}
if err := json.Unmarshal(data, &tab); err != nil {
log.Fatal(err)
}
fmt.Println(tab.LiveURL) // open it in any browser: anyone with the link can use the tab
}
Fields
| Field | Type | Default | What it does |
|---|---|---|---|
url | string | required | The page to load. A public http or https address: private, loopback, link-local, CGNAT and cloud-metadata addresses, localhost, .local/.internal names and names that resolve to them are refused (400 invalid_url). |
country | string | "random" | Two-letter country for the exit IP and for the page's locale, timezone, geolocation and Accept-Language (see countries); "random" picks one. Unknown codes: 400 invalid_country. |
max_wait | number | 20 | Seconds to wait for the page to load, 1–60 (400 invalid_option otherwise). The whole call returns within about max_wait + 5 s, so a client timeout of max_wait + 10 s is safe. A page that is still loading then comes back partial (below); one that never started: 408 page_timeout. |
proxy | string or string[] | the Community proxy | Omit it to use the free Community proxy in country. Or: your own proxy URL (http://, https://, socks5://), a list (one is picked per call), a cdpfleet-plan… token, or a residential cdpfleet-resi… token (tier, country, device OS, sticky session) billed per GB to your residential balance (proxies). proxy_rules isn't supported yet (400 invalid_option). |
max_duration | number | 600 | Seconds the tab stays open at most (your plan's session limit caps it; Community 15 minutes). |
idle_timeout | number | 120 | Seconds without anyone viewing before it closes (the first viewer has 5 minutes to open the link). |
viewport | object | {"width": 1280, "height": 800} | The tab's size. |
Good to know
- Response:
{livetab_id, live_url, ws_url, token, expires_at}. Openlive_urlin any browser — no install — to see the tab and use it with mouse, touch and keyboard, with an address bar, back, forward and reload. Anyone with the link can view and control the tab until it closes, so share it only with the person who should. GET /v1/functions/livetab/{livetab_id}→{status, url, title, viewers, closes_at}; after it closes,final_urland the tab'scookiesfor 10 minutes — a person signs in by hand, your code takes the cookies.DELETEcloses it.- While open it holds one thread and is billed per second like any call. Paid plans can open it in a browser and device of their choice (
browser,launch,device, as in your own browser). - Build your own viewer:
ws_urlstreams JPEG frames and takes mouse, key, text and navigate messages after{"type": "auth", "token": …}(the contract is the dashboard's live-view protocol).
Billing, errors and countries: Functions.