Skip to main content
POST
/
v1
/
design
/
extract
Submit design extraction
curl --request POST \
  --url https://api.example.com/v1/design/extract \
  --header 'Content-Type: application/json' \
  --data '
{
  "url": "<string>",
  "endUserId": "<string>",
  "extractors": [
    "<string>"
  ]
}
'
{
  "taskId": "<string>",
  "sessionId": "<string>",
  "status": "<string>",
  "extractors": [
    "<string>"
  ],
  "createdAt": {}
}

Documentation Index

Fetch the complete documentation index at: https://docs.stablebrowse.com/llms.txt

Use this file to discover all available pages before exploring further.

For an overview of what each extractor returns, see Design extraction.

Request body

url
string
required
The page to extract from. Must be a valid http:// or https:// URL.
endUserId
string
required
Opaque identifier for the user this extraction runs on behalf of. See End users. Must be ≤ 256 characters.
extractors
string[]
Subset of extractors to run. Valid values: "images", "fonts", "colors", "icons", "tokens", "logo". Omit (or pass an empty array) to run all six. Unknown names are silently dropped; if the resulting list is empty the request returns 400.

Response — 202 Accepted

taskId
string
Identifier for the extraction. Poll GET /v1/tasks/{taskId} until status === "completed".
sessionId
string
Always a freshly-minted session — design extractions are single-turn and don’t chain like agent tasks.
status
string
Always "pending" on submission.
extractors
string[]
The extractors that will actually run (echoed back so callers know exactly what to expect on the result).
createdAt
string (ISO 8601)
Submission timestamp.
Both SDKs wrap submit + poll into a single client.design.run(...) call. Most integrators should use this — it returns the completed task with design.results populated and never needs you to think about the task lifecycle.
from stablebrowse import Stablebrowse
client = Stablebrowse()  # reads STABLEBROWSE_API_KEY

task = client.design.run(
    url="https://www.figma.com/",
    end_user_id="alice",
    extractors=["colors", "fonts", "logo"],
)
colors = task.design["results"]["colors"]["colors"]
fonts  = task.design["results"]["fonts"]["fonts"]
print("Primary:", next(c for c in colors if c["role"] == "primary")["hex"])
print("Body font:", next(f for f in fonts if f["usage"] == "body")["family"])
task.design carries the full result shape (response schema).

Async primitives

If you need to queue extractions and poll on your own schedule (cron jobs, batch processors), the underlying primitives are available:
submission = client.design.submit(
    url="https://www.figma.com/",
    end_user_id="alice",
    extractors=["colors", "fonts"],
)
# ... later ...
task = client.tasks.get(submission.task_id)
if task.is_terminal and task.status == "completed":
    print(task.design["results"]["colors"])
client.tasks.get(taskId) is the same call you’d use for an agent task — design tasks ride the same task record and the design field is populated automatically.

Submit response — 202 Accepted

{
  "taskId": "7f2a1b8c-...",
  "sessionId": "a31d5e9f-...",
  "status": "pending",
  "extractors": ["colors", "fonts", "logo"],
  "createdAt": "2026-05-04T22:10:18Z"
}

Get-task response (completed)

{
  "taskId": "7f2a1b8c-...",
  "status": "completed",
  "design": {
    "url": "https://www.figma.com/",
    "extractors": ["colors", "fonts", "logo"],
    "durationMs": 8420,
    "results": {
      "colors": { "colors": [ { "hex": "#635BFF", "rgb": "rgb(99,91,255)", "count": 142, "role": "primary" } ], "contrastIssues": [] },
      "fonts":  { "fonts":  [ { "family": "Sohne", "usage": "body", "weights": [400, 500, 700], "source": "self-hosted", "faceUrl": "https://...s3..." } ] },
      "logo":   { "logo":   { "found": true, "src": "https://...s3...", "type": "svg", "width": 120, "height": 32 } }
    }
  },
  "createdAt": "2026-05-04T22:10:18Z",
  "updatedAt": "2026-05-04T22:10:27Z"
}
Typical extractions finish in 5–15 seconds. Recommended poll interval if you’re driving the loop yourself: 2 seconds.

Result schema

The design.results object contains one key per requested extractor. Top-level shape per extractor:
ExtractorResult keyTop-level fields
imagesimages.images[]src, type, naturalWidth, naturalHeight, alt, svgSource?, downloadUrl?, originalSrc?, s3Key?
fontsfonts.fonts[]family, usage, count, weights[], source, faceUrl?, downloadUrl?, originalFaceUrl?, s3Key?
colorscolors.colors[], colors.contrastIssues[]colors: hex, rgb, count, role (one of primary, background, text, error, success, warning, neutral — see Color roles). issues: fg, bg, ratio, passesAA, passesAAA
iconsicons.icons[]hash, source?, size, style, count, signedUrl?, downloadUrl?, s3Key?
tokenstokens.tokensdtcg, spacing[], radii[], shadows[], gradients[], motionDurationsMs[], cssVariables[]
logologo.logofound, src?, type?, width?, height?, svgSource?, alt?, downloadUrl?, originalSrc?, s3Key?
See Design extraction for what each field means and the URL TTL caveat.

Errors

CodeMeaning
400url missing or not http(s); endUserId missing or > 256 chars; invalid extractors (no valid names); invalid JSON body
401Missing Authorization header
403Revoked API key
429Monthly task quota exceeded; see response body for the limit
500Worker enqueue failed (transient; safe to retry)