Public
Likeplaywright-selector-gen
Val Town is a collaborative website to build and scale JavaScript apps.
Deploy APIs, crons, & store data – all from the browser, and deployed in milliseconds.
Endpoint: https://playwright.val.run
AI-powered service that fetches any URL's rendered DOM via Lightpanda headless browser and generates Playwright-native locators. The AI model does all selector generation — zero algorithmic logic in the code.
Every POST request requires the X-Api-Key header.
curl -X POST https://playwright.val.run \ -H "Content-Type: application/json" \ -H "X-Api-Key: your_endpoint_key" \ -d '{"url": "https://www.saucedemo.com"}'
| Env Var | Required | Purpose |
|---|---|---|
X_API_KEY | Yes | Protects the endpoint |
ANTHROPIC_API_KEY | Yes | Z.AI / Anthropic API key for the model |
ANTHROPIC_URL | No | API endpoint (default: https://api.z.ai/api/anthropic/v1/messages) |
ANTHROPIC_MODEL | No | Model identifier (default: anthropic/claude-sonnet-4-20250514) |
LPD_TOKEN | Yes | Lightpanda cloud token (sign up) |
LPD_REGION | No | Lightpanda region: euwest (default) or uswest |
Every request goes through Lightpanda's headless browser. No raw fetch(), no fallbacks.
POST { url } + X-Api-Key
│
▼
┌─ Lightpanda Cloud ────────────┐
│ puppeteer.connect() via CDP │
│ WebSocket to Lightpanda │
│ Full JS rendering │
│ networkidle2 wait │
│ page.content() → HTML │
└──────────┬────────────────────┘
│
▼
┌─ Parse DOM (cheerio) ─────────┐
│ Extract raw elements: │
│ tag, attributes, outerHTML, │
│ text content │
│ │
│ 0 elements? → 422 error │
└──────────┬────────────────────┘
│
▼
┌─ AI Model (Z.AI) ────────────┐
│ Receives raw DOM data. │
│ Generates ALL locators. │
│ Ranks by Playwright priority.│
│ Returns JSON. │
└──────────┬────────────────────┘
│
▼
JSON response (200)
Health check.
{ "status": "ok", "service": "playwright-selector-gen" }
Headers:
| Header | Required | Description |
|---|---|---|
X-Api-Key | Yes | Endpoint access key |
Content-Type | Yes | application/json |
Body:
| Field | Type | Required | Description |
|---|---|---|---|
url | string | Yes | Page URL to analyze |
filter | string[] | No | Only return elements matching these tags |
{ "url": "https://www.saucedemo.com", "totalElements": 8, "elements": [ { "index": 0, "tag": "input", "locators": [ { "method": "getByPlaceholder", "playwrightCode": "page.getByPlaceholder('Username')", "confidence": "high" }, { "method": "getByRole", "playwrightCode": "page.getByRole('textbox', { name: 'Username' })", "confidence": "high" }, { "method": "getByTestId", "playwrightCode": "page.getByTestId('username')", "confidence": "high" } ] } ] }
The AI follows the official Playwright recommendation:
| Rank | Method | Used For |
|---|---|---|
| 1 | getByRole() | ARIA role + accessible name |
| 2 | getByLabel() | Form fields with associated <label> |
| 3 | getByPlaceholder() | Inputs with placeholder text |
| 4 | getByText() | Visible text content |
| 5 | getByAltText() | Images with alt attribute |
| 6 | getByTitle() | Elements with title attribute |
| 7 | getByTestId() | data-testid / data-test / data-cy |
| 8 | locator() CSS | Short CSS only — absolute last resort |
| 9 | locator() XPath | Only for truly complex DOM traversal |
Each element gets up to 3 locators ranked best → fallback. Deep nested selectors are never generated.
| Status | Meaning |
|---|---|
401 | Missing or invalid X-Api-Key header |
400 | Missing url in request body |
405 | Method not allowed (use POST) |
422 | No elements found after rendering |
500 | Missing env vars / AI API error |
502 | Lightpanda could not fetch the URL |