Turn a website into an API
writ_website_to_apiTurns a website into a callable API: the one tool for this, every lane. For a service with no official or practical API whose data or actions are wanted programmatically: "turn into an API", "map the API of ", "expose every feature", "give me an endpoint for ".
A build is 3 CALLS: (1) this tool with url + goal (+ save_as); (2) writ_discovery_status(build_id, wait=true), one held call that follows every rung; (3) on succeeded, the answer's run_example (writ_run_workflow: workflow_id + function_name + inputs) runs it, and a working function answers two different inputs differently. START ON THE PAGE THAT ALREADY SHOWS THE ROWS: the url is the search-results / category / listing URL (e.g. https://www.google.com/maps/search/bakeries+Montreal/), not the app's home page; a build seeded at an empty shell spent 6 minutes over three rungs and produced no function. A goal naming the inputs and the fields ("page number in; quotes with text/author/tags and has_next out") shapes the functions. The build has its own browser, and an identical request during it joins it. An answer of existing_workflows / marketplace_candidates is a proposal, not a build: the match runs as is, and skip_existing / skip_marketplace start a fresh build. Status needs_guidance means the build is the caller's: mode=guided build_id= continues it in a browser the caller drives as its brain. writ_diagnose_http_workflow(workflow_id, task_id) explains a run that returns nothing.
NOT FOR: reading a page's content (writ_scrape), collecting a site as a dataset (writ_crawl_site), or a task that is not an API surface (writ_record_website).
Login: an app behind a sign-in builds as a saved identity (persona_id from writ_personas, which also carries 2FA); credentials never pass through this tool. Without one, persona_needed carries tell_user, written to ask the user for that sign-in.
Default mode intelligent: Writ runs the whole ladder itself, and the caller only starts it and waits. The ladder: the user's own matching workflows (existing_workflows; skip_existing=true bypasses), ready-made marketplace APIs (marketplace_candidates; skip_marketplace=true), then the fast path: one real cloud browser (the persona's session, residential exit, CAPTCHA and bot-wall handling, like every Writ session) where one AI call plans the functions the goal needs (GET reads, POST writes, in-page extractions) and the steps that reach each, the browser runs them, and per function one more AI call picks what backs it — the site's own captured request (compiled: tokens traced, inputs templated), the page's list, or the write's captured request (probe_write: never sent) — each live-tested, reads proven on a second input. Only when it cannot prove them does Writ's AI browser rung take over (turn by turn: ranks traffic, promotes HTTP requests, tests pagination); the newer rung's status carries escalations, the reason the fast path handed over. A saved fast-path API with gaps names them in missing_functions.
mode=crawl / mode=browser run the whole-site crawl rungs instead (static / rendered; robots.txt respected unless respect_robots=false): broad maps of server-rendered sites, unverified (verified:false) until a run proves them.
mode=auto is the same ladder with the caller as the last rung: when the fast path cannot prove the functions, the build parks as status=needs_guidance with map (pages, captured calls, each planned function and why it fell short) and what it already defined, instead of spending Writ's agent. mode=fast, mode=crawl and mode=browser start on their own rung and park the same way when they fall short. mode=guided (with build_id= to continue, or alone to start) opens a real browser bound to the build, driven turn by turn with writ_browser_act (navigate, sign in, capture_network, evaluate_js; calls read with writ_browser_network), where writ_browser_compose define_function defines the API: api functions from captured calls via from_index, or proven scripts/extractions, each live-tested as it is defined; set_inputs for parameters; is_auth for a sign-in function whose response_extractions feed the others. writ_browser_save settles the build: the workflow is callable at once (writ_run_workflow with function_name), pinnable, schedulable, exposable as REST (writ_expose_workflow_api), and its API docs are at GET /api/v1/workflows/{workflow_id}/api-docs.
HTTP-first: the guided browser is the experiment bench, and what ships are direct API functions defined from a captured representative search/filter request and next-page request. The default shape is the Auphan-style named function graph: ordered is_auth functions publish tokens/ids/origins through response_extractions, and data functions consume {{extracted:name}}; config.flow covers loops, recursive mapping, cross-page dedupe and composite returns. Typed extraction sources: json, embedded_json, html_css, regex, header, body. search/filter/limit/page/offset/cursor are declared inputs, and next_cursor/next_offset/has_more are returned. writ_diagnose_http_workflow(task_id=...) checks a saved run made with the intended persona; an API is ready to expose once engine=http returns non-empty data and pagination matches the browser baseline, or once a measured browser-only dependency is named.
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| url | No | The page that already shows the rows (search-results, category or listing URL), not the home page. Required unless build_id continues a parked build. | |
| goal | No | What the API should return or do, in plain language. Matched to the user's own workflows and marketplace listings first; narrows the crawl to what was asked (equivalent to `scope`) instead of mapping the whole app. | |
| mode | No | intelligent (default): the full ladder driven by Writ — the AI fast path (a live browser, a few AI steps, every function live-tested), then Writ's own AI browser rung only if needed. guided: a browser bound to a build that the caller drives and composes (sign in, capture_network, define_function, save); with build_id it continues a parked build and inherits its map. 'auto': the same ladder with the caller as the last rung — own workflows and marketplace proposed first, then the fast path; not enough => the build parks as needs_guidance, to finish guided. 'fast' = starts on the fast path. 'crawl' / 'browser' = the whole-site static / rendered crawl (unverified maps). For compatibility, regular/deep remain aliases of guided. | |
| level | No | Write policy for the crawl rungs. 'light' (default) captures every endpoint and payload but never performs a real create/update/delete. 'deep' performs each write once to capture its real confirmation response, so it changes real data; opt-in, for an explicit request. | |
| scope | No | Crawl lanes: maps only this surface (e.g. 'employees') and what it depends on. No value maps the whole app. | |
| device | No | A linked Writ desktop's agent_id (writ_devices) to act on. Omitted: the desktop this connection chose with writ_devices action='use', if any. | |
| save_as | No | Name for the workflow the build saves. | |
| build_id | No | Continues a parked build (status needs_guidance from writ_discovery_status) on the guided rung: opens the browser bound to it, seeded with its map. | |
| anonymous | No | Builds from what is visible without an account even though the site shows a sign-in page; for when the user said the public part is enough. | |
| persona_id | No | Saved identity to sign in with (writ_personas). Without one, a site whose entry page is a sign-in wall is not built: the answer names the persona to pass, or returns `persona_needed` with `tell_user` (a message for the user) and `create_url`; a later call with the new persona_id builds. Required for 2FA: the code is minted server-side and never reaches the caller. | |
| ai_supervise | No | AI-supervised crawl rungs (mode=crawl/browser; default true): after the crawl mines forms, POSTs, query links, scripts and listings, one bounded Writ AI call authors the API from them — which functions serve the goal, their names, inputs and example values; the live verify call then measures each response shape. false = the purely mechanical surface map (no AI spend on the crawl rungs). | |
| skip_existing | No | Skips the proposal of the user's own matching workflows (for after they declined). | |
| respect_robots | No | Crawl rungs (mode=crawl/browser) obey the site's robots.txt (default true). false is for a target the user vouches for after a rung reported robots.txt refused the crawl (`escalations` / `message` name the rule); otherwise such a rung fetches nothing and the build goes straight to a browser. Does not apply to the AI or guided browser rungs. | |
| use_residential | No | Runs on the platform residential network (premium), for a site that blocks datacenter IPs. Default off. Continuing a build (build_id) keeps the build's own persona, residential exit and country unless these fields are passed. | |
| execution_target | No | 'cloud' (the fleet) or a linked desktop's agent_id: builds there, in its own browser and connection. Omitted = the desktop chosen with writ_devices, else the cloud. | |
| skip_marketplace | No | Skips the ready-made marketplace proposals and builds fresh. | |
| residential_country | No | Two-letter ISO country the residential exit should be in (e.g. 'us', 'fr'); applies to every rung of the build, the guided browser included. No value means an automatic exit. Ignored unless the session egresses residential. |