Take anything apart, from code.
reengineer Anything reads a public web page and writes down how it was built: framework, hosting, palette, fonts, page outline, scripts, API endpoints, storage keys and accessibility gaps. It hands everything back as one markdown file with a rebuild plan and a prompt you can paste into a coding agent.
You can use it three ways: the box on the home page, the HTTP API at /api/scan, or the reeng.js module, which you import straight from this site. No key, no account, no install.
Quick start
One request gets you the whole plan. Pick your language:
curl -X POST https://reenganything.vercel.app/api/scan \
-H 'Content-Type: application/json' \
-d '{"url":"stripe.com","scope":"full","depth":"normal"}'import { scan } from 'https://reenganything.vercel.app/lib/reeng.js';
const run = await scan('stripe.com');
console.log(run.stack); // ['Next.js', 'React', 'Sentry', ...]
console.log(run.markdown); // the full reconstruction planimport requests
r = requests.post('https://reenganything.vercel.app/api/scan',
json={'url': 'stripe.com', 'scope': 'full'}).json()
open(f"reconstruction-{r['domain']}.md", 'w').write(r['markdown'])The answer comes back in one response, usually in 1 to 5 seconds. There is no run ID and nothing to poll.
How it works
Each scan runs the same pipeline. The steps array in the response is the real log of it, with milliseconds since start.
- Resolve. The host is looked up in DNS. Private, loopback, link-local and internal addresses are refused before any request is made.
- Fetch. The page is requested with a normal browser user agent. Redirects are followed by hand, up to 5, and every hop is checked again.
- Parse. The HTML is walked for elements, nesting depth, headings, landmarks, links, forms, media and class names.
- Read styles. Linked stylesheets are fetched (how many depends on depth) and mined for colors, CSS variables, font families, @font-face, breakpoints, radii and motion.
- Read bundles. First-party scripts are fetched and searched for API paths, sockets, storage keys and library fingerprints.
- Match. Around 90 signatures are tested against HTML, CSS, JS and response headers. Every hit keeps its evidence.
- Write. Findings become markdown, filtered by scope, ending with a rebuild plan and an agent prompt.
Scope
Scope decides which sections land in the markdown. The scan itself is the same; you just get less to read.
| Value | Label on the site | Sections |
|---|---|---|
full | Everything | All of them, plus the rebuild plan and agent prompt |
api | Just the API | Stack, scripts + deps, API surface, storage, hosting + headers |
ui | UI + design stuff | Stack, design system, structure, accessibility, assets, rebuild plan |
bundle | Bundles only | Stack, scripts + deps, API surface, storage |
Depth
Depth decides how much the scanner downloads besides the page itself.
| Value | Stylesheets | First-party bundles | robots + sitemap | Time budget |
|---|---|---|---|---|
quick | 0 (inline only) | 0 | no | 9 s |
normal | up to 4 | up to 3 | yes | 16 s |
deep | up to 10 | up to 8 | yes | 26 s |
truncated in the log.Output format
The markdown always opens with a # Reconstruction: <domain> title and a quoted line with the time, scope, depth and duration. Then come these ## sections, in this order:
| Section | What is inside |
|---|---|
| TL;DR | One paragraph summary and a table with title, description, final URL, HTTP status, size, language |
| Stack | Layer, name and the exact evidence string for every signature hit, plus JSON-LD types |
| Design system | Palette by frequency with a role guess, color tokens as a :root block, type, breakpoints, radii, motion |
| Structure | Element counts, top tags, custom elements, nav items, heading outline, internal routes, outbound domains, forms |
| Scripts + deps | External and inline scripts, loading strategy, third-party hosts, bundles read with sizes |
| API surface | Endpoints found in HTML and bundles, marked path, url or socket; fetch and XHR counts |
| Storage | localStorage and sessionStorage keys, IndexedDB names, cookies, Cache API, service worker |
| Accessibility | lang, landmarks, h1 count, heading skips, images without alt, unlabeled inputs, aria, tabindex, skip link |
| Assets | Images by format, lazy and srcset counts, inline SVG, video, canvas, iframes, favicon, share image |
| Hosting + headers | Useful response headers, a security header checklist, robots.txt and sitemap.xml |
| Rebuild plan | Suggested stack, a token block to paste, pages, sections in order, endpoints to stub, services to swap |
| Prompt for your agent | A plain-text brief for Claude Code, Cursor or any agent to rebuild the page with your own copy |
Section names are stable, so you can split the file by ## headings. sections() in reeng.js does exactly that.
Signatures
A signature is a pattern with a name and a layer. A hit is only reported with the evidence that triggered it, so you can check it yourself.
| Layer | Examples |
|---|---|
| Framework | Next.js, Nuxt, Remix, Astro, SvelteKit, Gatsby, Angular |
| UI library | React, Vue, Svelte, Preact, Alpine.js, htmx, jQuery |
| CMS / builder | WordPress, Shopify, Ghost, Webflow, Framer, Wix, Squarespace |
| CSS | Tailwind CSS (by utility class count), Bootstrap, Bulma, styled-components, Emotion, CSS modules |
| Fonts, icons, motion | Google Fonts, Adobe Fonts, Font Awesome, Lucide, GSAP, Framer Motion, Lottie, Lenis |
| 3D / canvas | Three.js, Spline, PixiJS |
| Analytics | Google Analytics, GTM, Plausible, PostHog, Segment, Mixpanel, Amplitude, Hotjar, Clarity, Meta Pixel |
| Services | Sentry, Datadog, Stripe, Paddle, Intercom, Crisp, HubSpot, reCAPTCHA, Turnstile |
| Backend / auth | Firebase, Supabase, GraphQL, Auth0, Clerk |
| Web3 | Solana web3.js, Phantom, wagmi / viem, ethers.js |
| Hosting / CDN | Vercel, Netlify, Cloudflare, CloudFront, S3, Firebase Hosting, GitHub Pages, Fly.io, Render, Fastly, Akamai |
POST /api/scan
Both forms do the same thing. GET is handy for quick checks in a browser tab. CORS is open, so you can call it from any page.
| Field | Type | Default | Notes |
|---|---|---|---|
url | string | required | With or without https://. Paths are fine: vercel.com/pricing |
scope | string | full | full · api · ui · bundle |
depth | string | normal | quick · normal · deep |
Response
{
"status": "complete",
"domain": "linear.app",
"url": "https://linear.app/",
"scope": "full",
"depth": "normal",
"elapsed": "1.3",
"stats": [
{ "label": "nodes", "value": 4781 },
{ "label": "scripts", "value": 16 },
{ "label": "stylesheets", "value": 47 },
{ "label": "colors", "value": 14 },
{ "label": "endpoints", "value": 8 },
{ "label": "tech", "value": 6 }
],
"steps": [{ "t": 0, "s": "resolving linear.app" }, "..."],
"stack": ["Next.js", "React", "Tailwind CSS", "styled-components", "Cloudflare"],
"palette": ["#9c9da1", "#000000", "#ffffff", "#626366", "#191d20"],
"markdown": "# Reconstruction: linear.app\n...",
"version": "1.0.0",
"scannedAt": "2026-11-04T15:15:00.000Z"
}
If the same URL, scope and depth were scanned in the last 10 minutes, you get the stored result with "cached": true, and it does not count against your limit.
Errors + limits
Errors come back as {"status":"error","error":"..."} with a matching HTTP code. The message is written for humans, so you can show it as is.
| Code | When |
|---|---|
| 400 | Missing or malformed URL, a non-http scheme, a custom port or credentials in the URL |
| 403 | The host resolves to a private, loopback or internal address |
| 415 | The URL serves something that is not a page, like a PDF or an image |
| 422 | DNS lookup failed. Usually a typo in the domain |
| 429 | Rate limited. retryAfter in the body and the Retry-After header say how many seconds to wait |
| 504 | The site did not answer in time or dropped the connection |
| 508 | More than 5 redirects |
| Limit | Value |
|---|---|
| Per IP, short window | 6 scans per 10 minutes |
| Per IP, long window | 20 scans per hour |
| Page size read | 4 MB of HTML |
| Cache | 10 minutes per URL + scope + depth |
reeng.js
A 3 KB ES module that lives on this site at /lib/reeng.js. Import it by URL. It works in browsers, Deno, Bun and Node 18 or newer.
import { scan, sections, tokens, prompt, download } from 'https://reenganything.vercel.app/lib/reeng.js';
const run = await scan('vercel.com/pricing', { scope: 'ui', depth: 'deep' });
sections(run.markdown)['Stack']; // that section as a string
tokens(run.markdown); // ':root { --bg: ...; --ink: ...; }'
prompt(run.markdown); // the agent brief, ready to paste
download(run); // browser only: saves reconstruction-vercel.com.md
| Export | What it does |
|---|---|
scan(url, opts) | Calls the API. opts: scope, depth, signal, endpoint. Throws ScanError with status and retryAfter |
scanPolitely(url, opts, tries) | Same, but waits and retries on 429 up to tries times |
sections(md) | Splits the markdown into an object keyed by ## heading |
tokens(md) | Returns the :root CSS block from the rebuild plan |
prompt(md) | Returns the agent prompt as plain text |
download(run) | Saves the markdown as a file in the browser |
Try it
This box imports reeng.js from this site and calls scan(), then prints the tokens and the agent prompt.
For agents
The output is written to be read by a coding agent. A typical loop: scan the reference, save the file next to your project, and ask the agent to follow it.
# save the plan, then hand it to your agent
curl -s "https://reenganything.vercel.app/api/scan?url=linear.app&scope=ui" \
| python3 -c "import json,sys; print(json.load(sys.stdin)['markdown'])" > PLAN.md
claude "Read PLAN.md and rebuild that page in this folder with our own copy"
You can also give an agent the API as a tool. Describe it as: takes url, optional scope and depth; returns markdown.
Limits
- No JavaScript runs. Pages that build everything in the browser show up as a thin shell. The TL;DR says so when it notices. Deep depth reads more bundles, which helps with endpoints and libraries, but not with the rendered DOM.
- Public pages only. No cookies, no logins, no forms. If a page needs an account, the scan sees the login screen.
- Bot walls. Some sites answer our server with 403 or 429. The log shows the status, and the markdown then describes the error page, not the site.
- Colors are counted, not seen. The palette is ranked by how often a value appears in CSS, so a color used once in a giant hero may rank low.
- Private networks are off limits. Addresses in 10/8, 172.16/12, 192.168/16, 127/8, 169.254/16, 100.64/10, IPv6 local ranges and names like
.localor.internalare refused, including after redirects.
$REENG
$REENG pays for the scanner. Creator fees cover the server time and agent credits that every scan burns, so the API stays free with no key.
| Field | Value |
|---|---|
| Ticker | $REENG |
| Chain | Solana |
| Contract | soon |
FAQ
Is it free?
Yes. No key and no account. The rate limits keep it free for everyone.
Why is my site shown as a shell with almost no elements?
It renders on the client. The server sends an empty root and JavaScript fills it in later. Try deep depth to read more bundles, or scan a page that is rendered on the server.
Can it scan a page behind a login?
No. It only reads what an anonymous visitor would get.
Do you keep my scans?
The server keeps a result for 10 minutes in memory so repeats are instant. The Yours tab on Runs lives in your own browser storage, and only you see it.
Why did a signature show up that the site does not use?
The pattern appeared somewhere in the HTML, CSS or bundles. Look at the evidence column in the Stack table to see what matched.
How is this different from viewing source?
View source gives you 4 MB of minified text. This gives you the twelve things you actually wanted, sorted and named, plus a plan.