Skip to main content
blueprint=true on a snapshot returns everything an AI coding agent needs to rebuild the page: a screenshot at each viewport, the content as markdown, the region structure with real geometry, the motion vocabulary, ready-to-use stylesheets, and the technologies it was built with. It is designed to be called by an agent — Cursor, Claude Code, v0 — mid-task, so it is synchronous. One request, one response, no polling.
A blueprint is a snapshot with more in it, not a different kind of call — the same way design=true works. Everything a plain snapshot returns is still at the top level, and the reconstruction data is added under one blueprint key. It costs 8 credits and typically takes 10–15 seconds: it visits the page at two viewports and scrolls through each, which is more browser work than a screenshot and less than a crawl.

What comes back

url, metadata, design and renderTime are the snapshot’s own fields and are not repeated inside blueprint. Add markdown=true and links=true to get those too — they are opt-in on a blueprint exactly as they are on any other snapshot. Everything is inline except the screenshots, which are written to your bucket because images cannot ride inside JSON without base64 inflating them by a third. A typical response is 20–150 KB — small enough to hand straight to a model.
A blueprint reuses one page load for all of it. The markdown, metadata and links are produced by the same visit as the layout and motion capture, so you never need a second call for the same URL.

The stylesheets are inline

Data alone loses fidelity, because an agent has to re-derive valid CSS from JSON and that is where detail goes missing. So blueprint.stylesheets carries stylesheets built from observed values:
tokens.css holds resolved custom properties, states.css the observed :hover, :focus-visible and :active rules, and responsive.css the layout differences between the desktop and mobile captures. A file that would be empty is omitted.
responsive.css is built from two rendered states compared, not the site’s media queries read back. Values that merely track the viewport — computed width and height — are excluded, because emitting them would pin the layout to one screen size. They remain in the region data as observations.

Regions, not a DOM dump

An agent rebuilds a landing page section by section, so the blueprint is organised that way. Each region carries a compact skeleton — the structural nodes that matter, not every wrapper — plus its motion, components and responsive deltas.
box is [top, left, width, height] in CSS pixels, and skeleton nodes use the same four-element form. Properties equal to the CSS default are dropped rather than restated. Every motion, state and responsive delta references a node id from the region it belongs to, so which part of the page does this is answerable rather than inferred. Anything that cannot be placed appears in motionOrphans instead of being silently dropped. Labels are inference and carry their evidence. A region matching nothing comes back "label": null with its geometry intact — an unlabelled region is useful, a wrong one is not. Repeated components are detected. Siblings with the same structure and size are reported with a count, which is what lets an agent emit a loop instead of hand-writing twelve near-identical cards.

Motion is classified

opacity 0→1 plus translateY(24px)→0 over 600ms, firing at 20% in view, offset 100ms across three siblings, is a fade-up-stagger-on-scroll. The blueprint says so. Each record reports the tier that produced it and a matching confidence. Because the backbone is document.getAnimations(), an effect is described the same way whichever library produced it. Where an animation has a named @keyframes rule, the record points at it with keyframesRef rather than repeating frames already in keyframes.css.

Optional: have a model read it back

interpret=true adds a grounded Markdown document to the blueprint — the page described in prose, region by region, for an engineer who has to rebuild it and cannot see it.
It costs 3 credits on top of the blueprint’s 8, or 1 with your own key. Bring your own with the X-Scrinly-Provider-API-Key header and pick the model family with provider.

Only a projection is sent

The model never sees the page. It sees a projection of the blueprint, capped at 16 KB, from which page copy, headings, body text, selectors, screenshot URLs and metadata have all been removed. What crosses is the part that is both stable and checkable: region labels and their evidence, motion types with their triggers and timings, computed styles, tokens, breakpoints and detected technologies.
The projection is also why the cache works. It excludes everything that differs between two captures of the same page — node ids, timestamps, a pixel of measurement jitter — so an unchanged page hashes identically and reuses its document. That is the difference between 11 credits and 9.

It is refused rather than trusted

Every CSS literal in the document must appear in the projection it came from. A duration, colour or breakpoint the model invented is caught, and enough of them fails the generation outright rather than returning a confident document with made-up numbers in it. grounding.status reports which happened:

When the model fails

The blueprint is yours either way. A provider outage, timeout or grounding refusal returns HTTP 200 with status: "blueprint_only", the full blueprint, interpretation: null, and an interpretationError explaining which of those happened. Only the interpretation credits are refunded — the capture succeeded and is in the response.

What a blueprint cannot do

Application logic is not recovered. Form validation, pricing calculators, search, checkout and authentication are detected and described, never reproduced. Minified bundles do not give up their logic.
WebGL is partial: shader source, the library and its parameters are captured, but a working scene cannot be reassembled from intercepted GL calls. Every blueprint carries a limitations array naming what was found but not explained, and instrumentation.degraded is true when motion interception failed and the capture fell back to observation only. A partial capture is usable; a partial capture presented as complete would not be.

Options

design=true is implied — the stylesheets are generated from it. interpret, viewports and screenshots apply only to a blueprint. Sending either without blueprint=true is a 400 rather than being silently ignored, so a typo in the flag name cannot cost you a capture you thought you had asked for.
A blueprint cannot be combined with async or webhookUrl — it is synchronous by design.