The Sentience Query DSL (Domain-Specific Language) allows you to find elements on a webpage using semantic properties instead of brittle CSS selectors. This guide covers all supported operators, fields, and usage patterns.
- Quick Start
- Basic Syntax
- Operators
- Fields
- Query Examples
- Free Tier vs Pro/Enterprise
- Best Practices
- API Reference
from sentience import SentienceBrowser, snapshot, query, find
with SentienceBrowser() as browser:
browser.page.goto("https://example.com")
browser.page.wait_for_load_state("networkidle")
snap = snapshot(browser)
# Find all buttons
buttons = query(snap, "role=button")
# Find button with specific text
sign_in = find(snap, "role=button text~'Sign in'")
# Find high-importance elements (Pro/Enterprise only)
important = query(snap, "importance>500")A selector is a sequence of terms separated by whitespace (implicit AND). Each term is a comparison: field operator value.
# Single condition
"role=button"
# Multiple conditions (AND)
"role=button text~'Sign in' clickable=true"
# Negation
"role!=link"
# Numeric comparison
"importance>500"- Strings: Use quotes for values with spaces:
text~'Sign in'ortext~"Sign in" - Barewords: No quotes needed for single words:
role=button - Booleans:
trueorfalse:clickable=true - Numbers: Integers or floats:
importance>500,bbox.x>100.5
| Operator | Description | Example | Use Case |
|---|---|---|---|
= |
Exact match | role=button |
Match exact value |
!= |
Not equal | role!=link |
Exclude specific values |
~ |
Contains (case-insensitive) | text~'Sign in' |
Text substring matching |
^= |
Starts with | text^='Sign' |
Prefix matching |
$= |
Ends with | text$='in' |
Suffix matching |
> |
Greater than | importance>500 |
Numeric comparisons |
>= |
Greater than or equal | importance>=1000 |
Numeric comparisons |
< |
Less than | importance<300 |
Numeric comparisons |
<= |
Less than or equal | importance<=200 |
Numeric comparisons |
# Exact match
query(snap, "role=button")
query(snap, "clickable=true")
# Contains (case-insensitive)
query(snap, "text~'sign in'") # Matches "Sign In", "SIGN IN", "sign in"
# Prefix matching
query(snap, "text^='Sign'") # Matches "Sign In", "Sign Out", "Sign Up"
# Suffix matching
query(snap, "text$='In'") # Matches "Sign In", "Log In"
# Numeric comparisons
query(snap, "importance>500") # Importance greater than 500
query(snap, "importance>=1000") # Importance greater than or equal to 1000
query(snap, "importance<300") # Importance less than 300
query(snap, "importance<=200") # Importance less than or equal to 200
# Spatial comparisons
query(snap, "bbox.x>100") # X position greater than 100
query(snap, "bbox.width>=50") # Width greater than or equal to 50
query(snap, "bbox.y<500") # Y position less than 500| Field | Type | Description | Example |
|---|---|---|---|
role |
string | Semantic role (button, link, textbox, etc.) | role=button |
text |
string | Visible text content | text~'Sign in' |
name |
string | Accessible name (aria-label, placeholder) | name~'email' |
clickable |
boolean | Whether element is clickable | clickable=true |
visible |
boolean | Whether element is visible (in viewport and not occluded) | visible=true |
importance |
number | Importance score (Pro/Enterprise only) | importance>500 |
tag |
string | HTML tag name (future) | tag=button |
| Field | Type | Description | Example |
|---|---|---|---|
bbox.x |
number | X coordinate (left edge) | bbox.x>100 |
bbox.y |
number | Y coordinate (top edge) | bbox.y<500 |
bbox.width |
number | Element width | bbox.width>50 |
bbox.height |
number | Element height | bbox.height>30 |
| Field | Type | Description | Example |
|---|---|---|---|
in_viewport |
boolean | Whether element is in viewport | in_viewport=true |
is_occluded |
boolean | Whether element is covered by overlay | is_occluded=false |
z_index |
number | CSS z-index value | z_index>10 |
| Field | Type | Description | Example |
|---|---|---|---|
visual_cues.is_clickable |
boolean | Clickable detection | Use clickable=true instead |
visual_cues.is_primary |
boolean | Primary action detection (Pro/Enterprise only) | Not directly queryable |
visual_cues.background_color_name |
string | Color name (Pro/Enterprise only) | Not directly queryable |
Note: Visual cues fields are not directly queryable. Use the top-level clickable field instead.
| Field | Type | Description | Status |
|---|---|---|---|
attr.* |
string | HTML attributes (dot notation) | Parser ready, matching pending |
css.* |
string | CSS properties (dot notation) | Parser ready, matching pending |
# Find all buttons
buttons = query(snap, "role=button")
# Find button with specific text
sign_in = find(snap, "role=button text~'Sign in'")
# Find clickable links
clickable_links = query(snap, "role=link clickable=true")
# Find visible elements only
visible = query(snap, "visible=true")
# Exclude generic elements
actionable = query(snap, "role!=generic")# Contains (case-insensitive)
query(snap, "text~'sign in'") # Matches "Sign In", "SIGN IN", etc.
# Prefix
query(snap, "text^='Sign'") # Matches "Sign In", "Sign Out", "Sign Up"
# Suffix
query(snap, "text$='In'") # Matches "Sign In", "Log In"
# Combined
query(snap, "role=button text^='Submit'")# Importance filtering (Pro/Enterprise only)
high_importance = query(snap, "importance>500")
medium_importance = query(snap, "importance>=200 importance<=500")
# Spatial filtering
right_side = query(snap, "bbox.x>500")
large_elements = query(snap, "bbox.width>100 bbox.height>50")
# Z-index filtering
above_overlay = query(snap, "z_index>10")# Multiple conditions (AND)
query(snap, "role=button importance>500 clickable=true")
# Spatial region
query(snap, "bbox.x>100 bbox.x<200 bbox.y>50 bbox.y<150")
# Visibility + importance
query(snap, "visible=true importance>300")
# Text + spatial
query(snap, "text~'Submit' bbox.x>400")# Find primary submit button
submit = find(snap, "role=button text~'Submit' clickable=true")
# Find email input field
email = find(snap, "role=textbox name~'email'")
# Find all links in viewport
visible_links = query(snap, "role=link in_viewport=true")
# Find high-importance buttons (Pro/Enterprise)
primary_actions = query(snap, "role=button importance>800 clickable=true")
# Find elements in specific region (top-right)
top_right = query(snap, "bbox.x>800 bbox.y<100")
# Find large clickable elements
large_clickable = query(snap, "clickable=true bbox.width>100 bbox.height>40")Available Fields:
- ✅
role,text,name,clickable,visible - ✅
bbox.*(spatial fields) - ✅
in_viewport,is_occluded,z_index
Limited Fields:
⚠️ importance- Always0(not useful for filtering)⚠️ visual_cues.is_primary- Alwaysfalse⚠️ visual_cues.background_color_name- AlwaysNone
Example (Free Tier):
# ✅ Works - Basic role and text matching
query(snap, "role=button text~'Sign in'")
# ✅ Works - Spatial filtering
query(snap, "bbox.x>100 bbox.y<500")
# ⚠️ Works but not useful - All importance is 0
query(snap, "importance>500") # Returns empty (all importance = 0)All Fields Available:
- ✅
importance- Proprietary scoring (e.g., 1250) - ✅
visual_cues.is_primary- Primary action detection - ✅
visual_cues.background_color_name- Color analysis - ✅ All free tier fields
Example (Pro/Enterprise):
# ✅ Works - Importance filtering
query(snap, "importance>500") # Returns high-importance elements
query(snap, "importance>=1000") # Returns very important elements
# ✅ Works - Combined with importance
query(snap, "role=button importance>800 clickable=true")Note: To use Pro/Enterprise tier, set api_key when creating the browser:
browser = SentienceBrowser(api_key="your-api-key")
snap = snapshot(browser) # Uses server-side API✅ Good: role=button text~'Submit'
❌ Avoid: CSS selectors (not supported)
✅ Good: role=button clickable=true text~'Sign in'
❌ Avoid: Single condition if multiple elements match
✅ Good: text~'Sign in' (case-insensitive, flexible)
✅ Good: text^='Sign' (prefix matching)
❌ Avoid: text='Sign In' (exact match, too brittle)
✅ Good: visible=true or in_viewport=true for actionable elements
✅ Good: is_occluded=false to exclude covered elements
✅ Good: importance>500 to find high-priority elements
✅ Good: Combine with role: role=button importance>800
✅ Good: bbox.x>100 to find elements in specific regions
✅ Good: Combine with other conditions: role=button bbox.x>400
- ✅ Use
find()instead ofquery()[0]for single elements - ✅ Add
visible=trueto reduce result set size - ✅ Use specific conditions to narrow results
⚠️ Avoidimportancequeries on free tier (all values are 0)
Query elements from snapshot using semantic selector.
Parameters:
snapshot(Snapshot): Snapshot object fromsnapshot()selector(str | dict): DSL selector string or query dict
Returns:
List[Element]: Matching elements, sorted by importance (descending)
Example:
results = query(snap, "role=button importance>500")
for element in results:
print(f"Found: {element.text} (importance: {element.importance})")Find single best matching element.
Parameters:
snapshot(Snapshot): Snapshot object fromsnapshot()selector(str | dict): DSL selector string or query dict
Returns:
Optional[Element]: Best matching element (highest importance) orNone
Example:
button = find(snap, "role=button text~'Submit'")
if button:
click(browser, button.id)Parse DSL selector string into structured query dictionary.
Parameters:
selector(str): DSL selector string
Returns:
Dict[str, Any]: Structured query dictionary
Example:
query_dict = parse_selector("role=button text~'Sign in'")
# Returns: {"role": "button", "text_contains": "Sign in"}# Pro/Enterprise: Use importance
button = find(snap, "role=button importance>800 clickable=true")
# Free tier: Use text and position
button = find(snap, "role=button text~'Submit' bbox.y<200")# Email field
email = find(snap, "role=textbox name~'email'")
# Password field
password = find(snap, "role=textbox name~'password'")
# All text inputs
inputs = query(snap, "role=textbox")# All links
links = query(snap, "role=link")
# Visible links only
visible_links = query(snap, "role=link in_viewport=true")
# Links with specific text
about_link = find(snap, "role=link text~'About'")# Top-left corner
top_left = query(snap, "bbox.x<200 bbox.y<200")
# Right side
right_side = query(snap, "bbox.x>800")
# Center region
center = query(snap, "bbox.x>400 bbox.x<600 bbox.y>300 bbox.y<500")Problem: Query returns empty list
Solutions:
- Check if field exists: Free tier
importanceis always 0 - Verify text matching: Use
~(contains) instead of=(exact) - Check visibility: Add
visible=trueorin_viewport=true - Use less specific conditions: Remove some terms
Problem: Query returns too many elements
Solutions:
- Add more specific conditions
- Use
importancefiltering (Pro/Enterprise) - Add spatial constraints (
bbox.x,bbox.y) - Use
find()instead ofquery()for single element
Problem: Parser fails or returns unexpected results
Solutions:
- Quote values with spaces:
text~'Sign in'nottext~Sign in - Use correct operators:
~for contains,=for exact - Check field names: Use
role,text,clickable, notelement.role - Verify numeric comparisons: Use
>,>=,<,<=for numbers