[Functions](https://cdpfleet.com/docs/functions)

# 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`.

 

```bash
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'
```

```js
// 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
# 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
// 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
  }
}
```

```csharp
// .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
// 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](#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](https://cdpfleet.com/docs/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}`. Open `live_url` in 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_url` and the tab's `cookies` for 10 minutes — a person signs in by hand, your code takes the cookies. `DELETE` closes 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](https://cdpfleet.com/docs/functions#own-browser)).
-   Build your own viewer: `ws_url` streams JPEG frames and takes mouse, key, text and navigate messages after `{"type": "auth", "token": …}` (the [contract](https://cdpfleet.com/docs/functions/livetab) is the dashboard's live-view protocol).

Billing, errors and countries: [Functions](https://cdpfleet.com/docs/functions).

---
Source: https://cdpfleet.com/docs/functions/livetab · Markdown: https://cdpfleet.com/docs/functions/livetab.md · All docs for AI tools: https://cdpfleet.com/llms.txt
