Skip to Content
For AI & agentsREST APIEndpoints reference

Endpoints reference

The base URL is https://styleref.io/api/v1.

AnonymousGET /styles, GET /styles/{ref}, GET /styles/{ref}/style.md, POST /style-md/validate. Pass a bearer token to additionally resolve your own private styles.

AuthenticatedPOST /extractions, GET /extractions/{id}, POST /styles/{ref}/save, GET /me/styles, GET /me/saved-styles, GET /me/extractions, GET /me/extractions/{id}. See Overview & authentication.

The {ref} path parameter

Endpoints that take a style accept a {ref} in one of four forms: a share slug (1an6edgp-c42c0cba5315), a /share URL, or — with a bearer token, for your own styles — the style’s UUID id or its /styles/{id} builder URL. When the ref contains / or : (both URL forms do), URL-encode the whole value (encodeURIComponent) so it stays one path segment. See Core concepts.

Every ref is an opaque id or a link containing one — you can’t build one from a style’s name. Get slugs from the search endpoint below or a share URL, and your own ids from GET /me/styles, which returns a ready-to-use ref per style. Anything else returns 400 invalid_request with a message naming the accepted forms.

GET /styles

Search the public gallery.

Query paramTypeNotes
querystringAesthetic, mood, discipline, or keywords.
categorystringGallery category filter.
sortpopular | recentDefaults to recent.
limitinteger 1–25Defaults to 8.
offsetinteger 0–200Rows to skip. Pass back nextOffset for the next page.
clientfigma-skill | gpt | skillAttribution channel — see below.
curl "https://styleref.io/api/v1/styles?query=warm%20editorial&limit=5"

Returns JSON. Feed a result’s slug to /styles/{ref}:

{ "count": 1, "styles": [ { "name": "80s-Inspired Poster", "slug": "1an6edgp-c42c0cba5315", "author": "@styleref", "category": "UI/UX", "tags": ["glass","calm"], "url": "https://styleref.io/share/1an6edgp-c42c0cba5315", "heroImage": null, "palette": ["#f4e3c1", "#c2452d", "#1b1b1b"] } ], "offset": 0, "hasMore": true, "nextOffset": 5, "attribution": "Generated by StyleRef.io — https://styleref.io" }

palette carries up to six hex colors from the style, so a picker can paint swatches without fetching each style.

Page with offset: pass nextOffset back as ?offset=, and stop when hasMore is false. When nothing matches on the first page, the response includes availableCategories as a retry hint.

GET /styles/{ref}

Get a style compiled to a ready-to-use spec.

Query paramTypeNotes
formatdefault | flux | midjourney | diffusion | stylemd | jsonDefaults to default.
sectionsstringComma-separated section ids (for example colors,typography) to cut tokens.
compact1 | true | 0 | falseStrip boilerplate to save tokens.
raw1 | true | 0 | falseOmit the metadata header/attribution and return only the compiled spec.
clientfigma-skill | gpt | skillAttribution channel — see below.

format=json returns application/json; every other format returns text/markdown.

Telling us where a request came from

Both read endpoints accept ?client=. Every response carries canonical styleref.io/share/... links back to the style, and client tags those links so the visits they generate are attributable to the tool that made the call.

Reach for it when the caller is an agent that fetches URLs with a built-in web-fetch tool and cannot set request headers — a published agent skill, or a custom GPT. Clients that can set headers should send X-StyleRef-Client instead, which takes any short slug.

curl "https://styleref.io/api/v1/styles/1an6edgp-c42c0cba5315?format=json&client=figma-skill"

Accepted values are figma-skill, gpt, and skill. Anything else is ignored rather than rejected, so a request never fails on this parameter alone.

Your own styles serve your last generation

A ref that resolves to your own style compiles from the last time you pressed Generate on styleref.io — not from your live, unsaved edits. Blocks you added since that generation are left out, exactly like the public share view. Gallery styles are unaffected: a published style is already a frozen snapshot.

So the rule is the same everywhere: using a gallery style is free, using your own style as last generated is free, and getting fresh output after editing means generating it once on the web.

A style you have never generated has no output to serve, so it answers 409:

{ "error": "styleref_not_generated", "message": "This StyleRef hasn't been generated yet — open it on styleref.io and Generate it first." }

message is written for a person — render it as-is. This applies to every format, and to /styles/{ref}/style.md.

The image formats carry only visual instructions: a style that mixes visual and writing-voice items compiles its voice items (formality, emoji and hashtag usage, tone of copy) into default and stylemd only. flux, midjourney and diffusion leave them out — in an image prompt they are wasted tokens that dilute the visual weighting.

A style’s guardrails compile per format, in each model’s native negation idiom: default/stylemd keep the instruction-following “Avoid …” prose; diffusion renders a separate Exclude: terse, comma, terms. section ready for a negative-prompt field; midjourney renders a comma-separated --no list; flux carries no negative terms at all (FLUX has no negative channel, and naming an unwanted element tends to attract it) — its guardrails are expressed as positive alternatives instead.

curl "https://styleref.io/api/v1/styles/1an6edgp-c42c0cba5315?format=flux&compact=1&raw=1"

The response carries X-StyleRef-Canonical-Url (the public page), X-Token-Count (approximate token size), and an ETag. Send the ETag back as If-None-Match to revalidate a cached copy — an unchanged style answers 304 Not Modified with no body, so polling for staleness costs almost nothing.

const res = await fetch( "https://styleref.io/api/v1/styles/1an6edgp-c42c0cba5315?format=default" ); const spec = await res.text(); // Prepend `spec` to your AI prompt as a hard constraint.

Returns 403 if the style exists but is private or pending moderation, 404 if no public style matches, 409 if it is your own style and has never been generated.

format=json also carries the style’s images

The structured format adds referenceImages when the style has any — the images you uploaded as inspiration, the image a style was extracted from, and its public sample outputs, flattened into one list with an explicit kind:

{ "sections": { "...": {} }, "referenceImages": [ { "url": "https://…/inspo.png", "kind": "inspiration", "thumbnailUrl": "https://…/inspo_thumb.png" }, { "url": "https://…/source.png", "kind": "source", "thumbnailUrl": null } ] }

Inspiration images come first, so referenceImages[0] is the most reference-like image. Use it to feed an image-to-image pipeline instead of relying on prose alone.

Every entry is a still you can decode: a sample output that is a clip contributes its poster frame rather than the video file, and a written sample output contributes nothing.

GET /styles/{ref}/style.md

Get a complete STYLE.md body, ready to commit.

curl "https://styleref.io/api/v1/styles/1an6edgp-c42c0cba5315/style.md" > STYLE.md

Write it to STYLE.md in a project root and reference it from AGENTS.md. See Add STYLE.md to a project.

POST /style-md/validate

Lint a STYLE.md document. A failing document still returns HTTP 200 — read the valid field.

Send the document as text/markdown, or as JSON with a text field:

curl -X POST "https://styleref.io/api/v1/style-md/validate" \ -H "Content-Type: text/markdown" \ --data-binary @STYLE.md
{ "valid": true, "errors": [], "warnings": [], "meta": { "name": "80s-Inspired Poster", "generator": "styleref.io", "sections": ["colors"] } }

See Validate & lint for the full rule list.

POST /extractions

Requires a bearer token with the extract scope. Consumes extraction credits.

Extract a style from an image, a writing sample, a named reference, or a public URL. Asynchronous: this starts the run and returns an id to poll.

There are two ways to send the source.

Upload files — multipart/form-data

For files you hold locally, with no public URL. This accepts the same sources the extraction box in the app does: several images, plus PDFs and text documents on Pro.

PartTypeNotes
image, image_0…, file_0…, document_0fileThe sources. Max 8 MB each. Images: JPEG, PNG, GIF, or WebP — detected from file contents, not the declared Content-Type. Documents: PDF, .txt, .md, .csv, .json (Pro only). Repeat the part or number it to send several.
namestringOptional draft name; defaults to the AI-suggested title.
textstringOptional extra description fused into the extraction.

At least one file or a text field is required.

How many files you may attach depends on your plan — the same limits as the app: 3 on Free, 10 on Pro. See Plans & limits. Going over, or attaching a document without Pro, returns 400 and spends no credits.

curl -X POST "https://styleref.io/api/v1/extractions" \ -H "Authorization: Bearer YOUR_TOKEN" \ -H "X-StyleRef-Client: comfyui" \ -F "file_0=@reference.png" \ -F "file_1=@moodboard.jpg" \ -F "name=Crimson Editorial"

Send text or URLs — application/json

The same sources the MCP extract_style tool accepts. At least one of text, reference, image_url, or document_url is required.

FieldTypeNotes
textstringA prose sample to emulate, or a description of the style you want. Max 6000 characters.
referencestringA named creative reference to emulate, e.g. "1970s Polish film posters".
image_urlstring or arrayPublic https URL pointing directly at an image, max 8 MB — or a list of them.
document_urlstring or arrayPublic https URL pointing at a PDF or text file, max 8 MB — or a list of them. Pro only.
namestringOptional draft name; defaults to the AI-suggested title.
curl -X POST "https://styleref.io/api/v1/extractions" \ -H "Authorization: Bearer YOUR_TOKEN" \ -H "Content-Type: application/json" \ -d '{"text":"Terse, declarative, no adverbs.","reference":"Swiss editorial design"}'

URL sources are fetched server-side through an SSRF guard: https only, and private or loopback addresses are refused.

The response

Returns 202:

{ "extractionId": "run_abc~sIgN3d", "statusUrl": "https://styleref.io/api/v1/extractions/run_abc~sIgN3d?name=Crimson%20Editorial", "status": "running", "creditsRemaining": 41, "sources": ["image"], "images": [ { "width": 1024, "height": 768, "mediaType": "image/png", "filename": "reference.png" } ] }

images is present only for a multipart upload, and lists every accepted file in upload order.

The extractionId is signed and bound to your account — no one else can poll it.

The image is downscaled server-side to ~1024px before it reaches the model, so you don’t pay vision tokens for a full-resolution upload. Don’t pre-shrink to 1024 yourself: the same upload is the source for the stored inspiration image (up to 1600px). If you need to reduce size for transport, WebP at ~2048px longest edge is the sweet spot.

Errors: 400 unsupported/corrupt image, no source given, or an unfetchable URL; 401 missing scope; 402 out of credits; 413 over 8 MB; 429 rate limited.

GET /extractions/{id}

Requires a bearer token with the extract scope.

Poll an extraction. While running:

{ "extractionId": "run_abc~sIgN3d", "status": "running", "message": "Still running — poll again in ~10s." }
Query paramTypeNotes
save_as_drafttrue | 1Save the result into your library as a draft.
namestringDraft name; defaults to the AI-suggested title.

Without save_as_draft the compiled spec comes back inline, so a stateless client never has to touch your library:

curl "https://styleref.io/api/v1/extractions/run_abc~sIgN3d" \ -H "Authorization: Bearer YOUR_TOKEN"
{ "status": "complete", "saved": false, "name": "Crimson Editorial", "blocks": [{ "section": "colors", "type": "color_palette", "value": "crimson #DC143C" }], "spec": "…ready-to-use style spec…", "styleMd": "---\nname: Crimson Editorial\n…", "lockedBlockCount": 0 }

A plan can cap how many style items an extraction returns, keeping the highest-ranked ones. No current plan sets that cap — free and Pro both return the full extraction — but the fields exist so a client never has to guess whether a short list was truncated. If a cap is ever applied, the response says so:

{ "lockedBlockCount": 12, "note": "Free plan: the top 10 style items were returned and 12 more are locked — Pro unlocks the full extraction: https://styleref.io/pricing" }

lockedBlockCount is 0 and note is absent when nothing was withheld — which is the case on every plan today. Render note verbatim when it does appear; it is the same message the web app shows.

With save_as_draft=true it saves and returns a reference plus an edit link:

{ "status": "complete", "saved": true, "idempotentReplay": false, "style": { "ref": "e7c1f2a9-2f65-4f0e-9a3d-2b1c4d5e6f70", "name": "Crimson Editorial", "blockCount": 7 }, "editUrl": "https://styleref.io/styles/abc-123" }

Saving is idempotent — polling twice with the flag returns the same style with idempotentReplay: true, never a duplicate. The source image is attached to the draft as an inspiration image, matching what the app does.

A failed status returns a generic message and refunds the credit automatically. A 404 means no extraction with that id belongs to you.

POST /styles/{ref}/save

Save (like) a public gallery style to your library, so it appears under My Styles → Saved. Requires a bearer token with the styles:write scope. Idempotent — saving an already-saved style succeeds.

curl -X POST -H "Authorization: Bearer $TOKEN" \ "https://styleref.io/api/v1/styles/1an6edgp-c42c0cba5315/save"
{ "saved": true, "alreadySaved": false, "name": "80s-Inspired Poster", "slug": "1an6edgp-c42c0cba5315", "libraryUrl": "https://styleref.io/my-styles" }

Returns 401 without a valid token or scope, 404 if no public style matches.

GET /me/styles

Requires a bearer token with the styles:read scope.

List your own library, including private styles.

Query paramTypeNotes
querystringRelevance search — same scoring as GET /styles.
limitinteger 1–50Defaults to 20.
cursorstringOpaque — pass back nextCursor from the previous page.

query ranks your library exactly the way GET /styles ranks the gallery: each whitespace-separated term matches its best field — name, then tags, then category, then the style’s description — and the term scores sum. Terms shorter than two characters are ignored, so a one-character query returns the plain listing. That parity matters when one search box drives both lists: without it, “kokeshi” would find a gallery style by its tags and miss your own style with the same tags.

A query changes how pages are cursored — keep passing nextCursor back and the difference stays invisible.

curl "https://styleref.io/api/v1/me/styles?query=editorial&limit=20" \ -H "Authorization: Bearer YOUR_TOKEN"
{ "count": 2, "styles": [ { "name": "Crimson Editorial", "slug": null, "ref": "e7c1f2a9-2f65-4f0e-9a3d-2b1c4d5e6f70", "visibility": "private", "category": "Book/editorial design", "tags": ["editorial", "crimson"], "heroImage": "https://cdn.styleref.io/…​.png", "updatedAt": "2026-07-21T10:00:00Z", "lastGeneratedAt": "2026-07-21T10:04:00Z", "url": "https://styleref.io/styles/abc-123" } ], "nextCursor": null }

Feed a ref to /styles/{ref} to compile it. nextCursor is null on the last page.

category, tags and heroImage carry the same card fields as a gallery result, so a picker can render your library and the gallery as one list. category and tags are set when a style is published — before that they are null and []. heroImage is null when the style has no image to show; render your own placeholder rather than a broken one.

lastGeneratedAt is null when you have never generated that style on styleref.io. Fetching such a style returns 409 styleref_not_generated, so check this field before presenting a style as ready to load — a picker can flag the row up front instead of letting the load fail.

GET /me/saved-styles

Requires a bearer token with the styles:read scope.

The styles you saved from the gallery (the sibling of /me/styles, which lists the ones you authored). Same card shape as GET /stylesquery, category, limit and offset all work the same way — so a picker can offer “Saved” as a third tab next to Recent and Popular.

curl "https://styleref.io/api/v1/me/saved-styles?limit=8" \ -H "Authorization: Bearer YOUR_TOKEN"

GET /me/extractions

Requires a bearer token with the styles:read scope.

Your past extractions, newest first, metadata only. Re-reading an extraction you already ran is free — reach for this before spending credits to extract the same source twice.

Query paramTypeNotes
styleuuidLimit to one style (its id, from /me/styles).
limitinteger 1–50Defaults to 20.
cursorstringOpaque — pass back nextCursor from the previous page.
curl "https://styleref.io/api/v1/me/extractions?style=e7c1f2a9-2f65-4f0e-9a3d-2b1c4d5e6f70" \ -H "Authorization: Bearer YOUR_TOKEN"
{ "count": 2, "entries": [ { "id": "8f14…", "styleId": "e7c1…", "createdAt": "2026-07-21T10:00:00Z", "suggestedTitle": "Crimson Editorial", "blockCount": 18, "appliedCount": 7 }, { "id": "2b09…", "styleId": "e7c1…", "createdAt": "2026-07-14T09:12:00Z", "suggestedTitle": "Crimson Editorial", "blockCount": 15, "appliedCount": 0, "locked": true } ], "lockedCount": 1, "fullAccess": false, "nextCursor": null }

Plan scope matches the web app: on the free plan the newest extraction per style is open and older ones are listed with locked: true; browsing across all your styles (omitting style) is a Pro feature and returns 403 plan_limit_reached otherwise.

GET /me/extractions/{id}

Requires a bearer token with the styles:read scope.

One stored extraction with its full block set. No credits — this is a read of something you already own.

Query paramTypeNotes
formatblocks | default | stylemdDefaults to blocks. Adds spec or styleMd compiled from the usable blocks.

Blocks are re-capped against your current plan rather than the plan you were on when the extraction ran, so a plan change applies retroactively. No plan caps extracted items today, so in practice every block comes back; when a cap does apply, withheld blocks are returned marked locked: true (so you can show what an upgrade unlocks) and excluded from any compiled spec.

Returns 403 plan_limit_reached for an older entry on the free plan, 404 if no extraction with that id belongs to you.

Next steps

Last updated on