Public HTTP val that turns a URL into a Playwright .ts test file using TinyFish (real-browser HTML fetch) + an OpenAI-compatible LLM (selector & assertion generation).
POST https://khawjaahmad--6bc5243a4d5d11f1ab91ee650bb23af1.web.val.run
Headers:
X-Auth-Key: <AUTH_KEY>— requiredContent-Type: application/json
Body:
{ "url": "https://example.com/login" }
Response: text/plain containing a Playwright TypeScript test file. Save as *.spec.ts and run.
GET / returns a small JSON usage blurb.
Set these in the val's settings (already created with placeholders — replace before first use):
| Key | Purpose |
|---|---|
AUTH_KEY | Shared secret gating the endpoint. Generate a long random string. |
TINYFISH_API_KEY | https://agent.tinyfish.ai/api-keys |
LLM_TOKEN | Bearer token for your OpenAI-compatible provider |
LLM_BASE_URL | Base URL ending in /v1 (OpenAI, OpenRouter, Groq, Together, Gemini-OpenAI, ...) |
LLM_MODEL | Model name passed to chat/completions |
The val is public so its source is visible, but the source doesn't contain secrets — they live in env. The AUTH_KEY check prevents random callers from running up your TinyFish + LLM bills via your public URL. Without it, anyone who finds the endpoint can drain your accounts.
- Auth check (
X-Auth-Key) - TinyFish Fetch with
format: "html"(timeout 20s) - Aggressive cleaning (linkedom):
- Drop
<script>,<style>,<noscript>,<svg>,<iframe>,<canvas>, comments, hidden elements - Drop framework attrs (
ng-*,v-*,_ngcontent-*,data-react*,data-svelte-h,data-astro*) - Drop
on*handlers, inlinestyle,srcset,integrity,nonce - Drop hashed/CSS-in-JS/BEM class names; keep up to 4 meaningful classes
- Keep
data-testid/data-test/data-qa/data-cy(the Playwright testid hooks) - Collapse whitespace; progressively prune
<footer>,<aside>,<nav>, then empty wrapper<div>s - Hard cap at ~120 KB; truncate with marker if still too big
- Drop
- OpenAI-compatible
chat/completionswith a strict system prompt enforcing the locator priority order and the anti-pattern list. - Return the model output as plain text (
text/plain).
If TinyFish returns a Cloudflare / PerimeterX / hCaptcha challenge page, the cleaner would produce garbage. The val detects challenge markers in title + first 4 KB and returns:
{ "error": "target_blocked_by_anti_bot", "detail": "..." }
| Status | error | When |
|---|---|---|
| 400 | invalid_json | Body wasn't JSON |
| 400 | invalid_url | url missing or not http(s) |
| 401 | unauthorized | Missing/wrong X-Auth-Key |
| 405 | method_not_allowed | Not GET or POST |
| 500 | server_misconfigured | An env var is missing |
| 502 | target_blocked_by_anti_bot | Page returned an anti-bot challenge |
| 502 | tinyfish_failed | TinyFish errored or timed out |
| 502 | empty_after_cleaning | Cleaned HTML was too small to be useful |
| 502 | llm_failed | LLM call errored or timed out |
curl -sS -X POST "https://khawjaahmad--6bc5243a4d5d11f1ab91ee650bb23af1.web.val.run" \ -H "X-Auth-Key: $AUTH_KEY" \ -H "Content-Type: application/json" \ -d '{"url":"https://playwright.dev/"}' \ > playwright-dev.spec.ts