Playwright
Using SurfSky with Playwright
Prerequisites
To use SurfSky, you'll need the following:
- API Key - A unique authentication token for accessing our services
- Assigned Hostname - Your dedicated Surfsky endpoint
- Proxies - Proxies in the format:Supported protocols: HTTP, HTTPS, SOCKS5, SSH
protocol://username:password@host:port
To obtain your API key and hostname, please contact our team.
If you need proxies, please contact our team.
Code Example
- Python
- JavaScript
- Java
First, install the required packages:
pip install playwright requests
Here's a basic example of how to use SurfSky with Playwright:
from playwright.sync_api import sync_playwright
import requests
def create_profile(api_token: str, proxy: str | None = None) -> dict:
"""Create a one-time browser profile"""
url = "https://api-public.surfsky.io/profiles/one_time"
headers = {
"Content-Type": "application/json",
"X-Cloud-Api-Token": api_token
}
data = {}
if proxy:
data["proxy"] = proxy
response = requests.post(url, headers=headers, json=data)
response.raise_for_status()
return response.json()
def close_browser_cdp(browser):
"""Properly close browser using CDP
Note: While browser.close() typically cleans up contexts for connected browsers,
we use CDP's Browser.close command to ensure complete cleanup of remote resources.
This is especially important for cloud-based browser instances where we need
to ensure proper termination of the remote browser server.
https://github.com/microsoft/playwright/issues/858
Using CDP directly:
1. Guarantees remote browser cleanup
2. Prevents resource limits from being reached
"""
context = browser.contexts[0]
page = context.pages[0]
cdp_client = context.new_cdp_session(page)
cdp_client.send("Browser.close")
def main():
# Your credentials
api_token = "YOUR_API_TOKEN"
proxy = "protocol://username:password@host:port"
browser = None
with sync_playwright() as playwright:
try:
# Connect to SurfSky
profile = create_profile(api_token, proxy)
browser = playwright.chromium.connect_over_cdp(profile["ws_url"])
# Get default context and create a page
page = browser.contexts[0].pages[0]
# Your automation code here
page.goto("https://example.com")
print(f"Page title: {page.title()}")
except Exception as e:
print(f"Error occurred: {e}")
finally:
# Proper cleanup
if browser:
close_browser_cdp(browser)
if __name__ == "__main__":
main()
First, install the required packages:
npm install playwright axios
Here's a basic example of how to use SurfSky with Playwright:
const { chromium } = require('playwright');
const axios = require('axios');
async function createProfile(apiToken, proxy = null) {
const url = 'https://api-public.surfsky.io/profiles/one_time';
const headers = {
'Content-Type': 'application/json',
'X-Cloud-Api-Token': apiToken
};
const data = {}
if (proxy) {
data.proxy = proxy;
}
const response = await axios.post(url, data, { headers });
return response.data;
}
async function closeBrowserCdp(browser) {
/**
* Properly close browser using CDP
*
* Note: While browser.close() typically cleans up contexts for connected browsers,
* we use CDP's Browser.close command to ensure complete cleanup of remote resources.
* This is especially important for cloud-based browser instances where we need
* to ensure proper termination of the remote browser server.
* https://github.com/microsoft/playwright/issues/858
*
* Using CDP directly:
* 1. Guarantees remote browser cleanup
* 2. Prevents potential resource leaks
*/
const context = browser.contexts()[0];
const page = context.pages()[0];
const cdpClient = await context.newCDPSession(page);
await cdpClient.send('Browser.close');
}
async function main() {
const API_TOKEN = 'YOUR_API_TOKEN';
const PROXY = 'protocol://username:password@host:port';
let browser;
try {
// Create profile and connect to browser
const profile = await createProfile(API_TOKEN, PROXY);
browser = await chromium.connectOverCDP(profile.ws_url);
// Get default context and create a page
const page = await browser.contexts()[0].newPage();
// Your automation code here
await page.goto('https://example.com');
console.log(`Page title: ${await page.title()}`);
} catch (error) {
console.error('Error:', error);
} finally {
// Proper cleanup
if (browser) {
await closeBrowserCdp(browser);
}
}
}
main();
First, add the required dependencies to your pom.xml
:
<dependencies>
<dependency>
<groupId>com.microsoft.playwright</groupId>
<artifactId>playwright</artifactId>
<version>1.40.0</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.15.2</version>
</dependency>
</dependencies>
Here's a basic example of how to use SurfSky with Playwright:
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.microsoft.playwright.*;
import com.microsoft.playwright.options.*;
import java.io.IOException;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.util.HashMap;
import java.util.Map;
public class SurfSkyAutomation {
private static final ObjectMapper objectMapper = new ObjectMapper();
public static Map<String, Object> createProfile(
String apiToken,
String proxy
) throws IOException, InterruptedException {
String url = "https://api-public.surfsky.io/profiles/one_time";
HttpClient client = HttpClient.newHttpClient();
ObjectNode requestBody = objectMapper.createObjectNode();
if (proxy != null && !proxy.isEmpty()) {
requestBody.put("proxy", proxy);
}
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create(url))
.header("Content-Type", "application/json")
.header("X-Cloud-Api-Token", apiToken)
.POST(HttpRequest.BodyPublishers.ofString(requestBody.toString()))
.build();
HttpResponse<String> response = client.send(
request,
HttpResponse.BodyHandlers.ofString()
);
if (response.statusCode() != 200) {
throw new RuntimeException(
"Failed to create profile. Status code: " +
response.statusCode() +
", Response: " +
response.body()
);
}
return objectMapper.readValue(response.body(), Map.class);
}
public static void closeBrowserCdp(Browser browser) {
/**
* Properly close browser using CDP
*
* Note: While browser.close() typically cleans up contexts for connected browsers,
* we use CDP's Browser.close command to ensure complete cleanup of remote resources.
* This is especially important for cloud-based browser instances where we need
* to ensure proper termination of the remote browser server.
* https://github.com/microsoft/playwright/issues/858
*
* Using CDP directly:
* 1. Guarantees remote browser cleanup
* 2. Prevents resource limits from being reached
*/
try {
BrowserContext context = browser.contexts().get(0);
Page page = context.pages().get(0);
CDPSession cdpSession = context.newCDPSession(page);
cdpSession.send("Browser.close");
} catch (Exception e) {
System.err.println(
"Error closing browser via CDP: " + e.getMessage()
);
}
}
public static void main(String[] args) {
String apiToken = "YOUR_API_TOKEN";
String proxy = "protocol://username:password@host:port";
Browser browser = null;
try (Playwright playwright = Playwright.create()) {
try {
// Connect to SurfSky
Map<String, Object> profile = createProfile(apiToken, proxy);
String wsUrl = (String) profile.get("ws_url");
browser = playwright.chromium().connectOverCDP(wsUrl);
// Get default context and page
BrowserContext context = browser.contexts().get(0);
Page page = context.pages().get(0);
// Your automation code here
page.navigate("https://example.com");
System.out.println("Page title: " + page.title());
} catch (Exception e) {
System.err.println("Error occurred: " + e.getMessage());
e.printStackTrace();
} finally {
// Proper cleanup
if (browser != null) {
closeBrowserCdp(browser);
}
}
}
}
}
Important Notes
- Always remember to close the browser when you're done to release your session limit. Inactive sessions are automatically closed after 30 seconds. Set
inactive_kill_timeout
to change this value. - One time profile is used only once and then deleted. Use persistent profiles for long-term sessions
- A proxy is required and must be passed to the
create_profile
function - You can run multiple sessions according to your subscription plan's session limit
For more advanced usage and error handling, check out our API Reference.