Skip to content

Actions DSL

Markdownagents.txt

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.

h1 → text of the first <h1> (default extractor: text)
a.primary@href → absolutized href
xpath=//*[@id='x']@html → outer HTML
"div[data-x='@']"@text → quoting lets you put '@' inside the selector

Full string form: [scheme=]<selector>@<extractor>.

  • Scheme — omit (css default) or xpath= / text=.
  • Selector — standard CSS or XPath. Wrap in quotes if it contains @.
  • Extractor — what to pull (see table). Defaults to text.
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.

{
"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 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 @href and @text to 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.

Form Returns Example
Dot / bracket scalar json:items.0.name
JSONPath (*, .., or $…) array of matches json:items[*].name

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" }
}
}
{
"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.

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.

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.

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.

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"
}
}