Actions DSL
For engine: "json", actions are a flat output-name → JSON-path map; omitted/empty actions return the whole document. See JSON engine. The DOM DSL below applies to HTML/browser.
The actions field is a tree. Leaves extract values; branches recurse into matched elements; fn nodes are imperative steps (mostly browser; see the matrix below).
CSS is the default selector syntax. Prefer bare selectors (h1, a.cta) — do not prefix with css= (accepted but redundant). Use xpath= for XPath. jQuery extensions (:contains(), :eq(), …) are not supported and throw at runtime.
Selector syntax
Section titled “Selector syntax”h1 → text of the first <h1> (default extractor: text)a.primary@href → absolutized hrefxpath=//*[@id='x']@html → outer HTML"div[data-x='@']"@text → quoting lets you put '@' inside the selectorFull string form: [scheme=]<selector>@<extractor>.
- Scheme — omit (
cssdefault) orxpath=/text=. - Selector — standard CSS or XPath. Wrap in quotes if it contains
@. - Extractor — what to pull (see table). Defaults to
text.
Extractors
Section titled “Extractors”| Extractor | Returns | Notes |
|---|---|---|
text / textOriginal |
string |
Visible text (text trims). |
html / innerHtml |
string |
Outer / inner HTML. |
md / fitMd |
string |
Element as Markdown; fitMd uses Readability article detection and may omit content. For whole-page Markdown over MCP, prefer scrape_markdown without actions or selectors. |
value |
string | null |
Input value. Browser engine. |
tagName |
string |
e.g. "div". |
cssSelector |
string |
A CSS path for the matched element. |
json / json:<path> |
unknown |
Parse text as JSON; optional path drill. See JSON paths. |
attributes |
Record<string, string> |
All DOM attributes. |
href / hrefOriginal |
string |
Absolutized (Original = raw). Same pattern for src / poster / srcset. |
table / tableJson / tableArray |
rows | options.headers = false → 2D array. |
screenshot |
string |
Deterministic browser only — public PNG URL by default (returnType: 'dataUrl' for base64). |
| anything else | string | null |
Named DOM attribute via getAttribute. |
context / ocr / htmlTree are not supported in v2.
Branches: many, output
Section titled “Branches: many, output”{ "cards": { "selector": ".sf-card", "many": true, "output": { "title": "a.sf-title@text", "link": "a.sf-title@href", "points": ".sf-points" } }}many: true— apply once per match → array.many: false(default) — first match only.- Nested branches are relative to the matched element.
Flat vs nested output (easy to get wrong)
Section titled “Flat vs nested output (easy to get wrong)”Flat string — output names an extractor to run on each match:
{ "selector": "a", "many": true, "output": "href" }Nested self — a leading @ extracts from the current match:
{ "selector": "a", "many": true, "output": { "url": "@href", "label": "@text" }}Nested child — a selector looks up a descendant (the first match only):
{ "selector": "li", "many": true, "output": { "link": "a@href" } }Nested list — use many: true to return an array inside each row:
{ "output": { "tags": { "selector": "a.tag", "many": true, "output": "text" } }}Two common mistakes:
{ "output": { "tags": "a.tag@text" } }returns only the first tag, not an array.{ "output": { "url": "href", "label": "text" } }searches for<href>and<text>elements and returns null. Use@hrefand@textto extract from the current match.
String nested leaves never return arrays. For list fields inside a row (tags, chips, multi-images), use object form + many: true.
Always model multi-field rows as one nested many: true action. Never parallel arrays (titles[] + authors[]) — they desync when a selector misses a row.
JSON paths
Section titled “JSON paths”| Form | Returns | Example |
|---|---|---|
| Dot / bracket | scalar | json:items.0.name |
JSONPath (*, .., or $…) |
array of matches | json:items[*].name |
JSON-row projection
Section titled “JSON-row projection”When selector ends in @json:<path> and output is an object, each row is projected. Leaf strings are row-relative JSON paths (no @ — @ means DOM extractors and is rejected here).
{ "images": { "selector": "body@json:products[*].images[*]", "output": { "pos": "position", "src": "src" } }}Function actions (fn)
Section titled “Function actions (fn)”{ "login": { "fn": "fill", "selector": "input[name=email]", "args": "ada@example.com" }, "submit": { "fn": "click", "selector": "button[type=submit]" }, "wait": { "fn": "wait", "args": 1500 }}Keys run top-to-bottom; fn nodes sequence side effects.
html vs browser
Section titled “html vs browser”fn / extractor |
html |
browser |
|---|---|---|
wait |
Rejected — HTML engine, use browser |
Yes |
goto |
Rejected | Yes |
back |
Rejected | Yes |
forward |
Rejected | Yes |
click |
Rejected | Yes |
fill |
Rejected | Yes |
selectOption |
Rejected | Yes |
2fa |
Rejected | Yes (otpauth only) |
evaluate |
Both — JSDOM eval, sync only (no async / Promise) |
Page evaluate; async allowed, 60s timeout |
screenshot extractor |
Not supported — use browser |
Yes (PNG URL, or returnType: "dataUrl") |
Engine pick and request options: Engines.
The browser also supports the registered facebook-ads-library function for structured ad collection. See specialty extractors for the full request and completeness contract.
Options
Section titled “Options”Per-action options:
| Field | Meaning |
|---|---|
timeout |
Per-action ms (caps apply). |
filter |
Regex; value must match. On flat many, drops non-matches. |
match |
Regex; return first capture group (or full match). |
maxChars |
Cap string length after match/filter (+head / −tail). |
headers |
tableJson: false → 2D array. |
excludeTags / includeTags |
md / fitMd / html strip lists. |
returnType / fullPage |
Screenshot only. |
Top-level request options (sibling of actions): waitFor, timeoutMs, resolution, headless, blockAds — see Engines.
End-to-end examples
Section titled “End-to-end examples”Static list — html against /fixtures/listing:
{ "url": "https://scrapesilo.com/fixtures/listing", "engine": "html", "actions": { "cards": { "selector": ".sf-card", "many": true, "output": { "title": "a.sf-title@text", "link": "a.sf-title@href", "points": ".sf-points" } } }}Interaction — browser against /fixtures/form (fill + click, then extract):
{ "url": "https://scrapesilo.com/fixtures/form", "engine": "browser", "actions": { "type": { "fn": "fill", "selector": "#sf-q", "args": "widget" }, "submit": { "fn": "click", "selector": "#sf-submit" }, "title": ".sf-result-title", "count": ".sf-result-count" }}Write actions yourself. For interpretation (outline, summary, “is X present?”), use POST /analyse — not scrape, and not a query field on /scrape.
Copy for an agent
Section titled “Copy for an agent”ScrapeSilo actions DSL (POST /scrape). Required: url, engine ("html"|"json"|"browser"); actions required for html/browser, optional flat JSON paths for json. No query. No engine "ai".
Rules:- CSS is the default selector syntax. Write h1, not css=h1. Use xpath= only when needed. No jQuery (:contains, :eq) — they throw.- String leaf: "selector@extractor" (default extractor: text). Example: a.sf-cta@href- Rows: one nested { selector, many: true, output: { ... } }. Never parallel arrays (titles[] + authors[]).- List field inside a row needs object form + many: true. A string leaf is first-match only.- Extractor on the matched element itself needs a leading @ in nested output ({ "url": "@href" }). Bare "href" is CSS for a <href> tag → null.- html fn limits: wait/goto/back/forward/click/fill/selectOption/2fa are rejected. evaluate is sync JSDOM only. screenshot extractor is browser only.- Interpretation (outline, summary) → POST /analyse, not scrape.
html (static list):{ "url": "https://scrapesilo.com/fixtures/listing", "engine": "html", "actions": { "cards": { "selector": ".sf-card", "many": true, "output": { "title": "a.sf-title@text", "link": "a.sf-title@href", "points": ".sf-points" } } }}
browser (fill + click):{ "url": "https://scrapesilo.com/fixtures/form", "engine": "browser", "actions": { "type": { "fn": "fill", "selector": "#sf-q", "args": "widget" }, "submit": { "fn": "click", "selector": "#sf-submit" }, "title": ".sf-result-title", "count": ".sf-result-count" }}