Search macros are URL shortcuts that expand search queries into full URLs for popular sites. Instead of manually constructing search URLs with proper encoding, use macros like @google_search or @youtube_search.
What are search macros?
Macros simplify navigation by handling URL construction and query encoding:
# Without macro (manual URL encoding)
POST /tabs/:tabId/navigate
{
"userId": "agent1",
"url": "https://www.google.com/search?q=machine%20learning%20tutorial"
}
# With macro (automatic encoding)
POST /tabs/:tabId/navigate
{
"userId": "agent1",
"macro": "@google_search",
"query": "machine learning tutorial"
}
Both produce identical results, but macros eliminate manual encodeURIComponent() calls and URL structure knowledge.
Why macros exist
From practical experience building AI browser agents:
- Avoid encoding bugs: LLMs frequently forget to encode special characters (spaces, &, #, etc.)
- Site-specific URL structures: Each site has different query param names (
q=, search_query=, keywords=)
- API format changes: When Reddit switches from HTML to JSON, only the macro needs updating
- Prompt brevity: Shorter tool calls = more context for reasoning
Macros are optional - you can always use the url parameter directly if you prefer full control.
Complete macro list
From lib/macros.js:1-16, here are all 14 supported macros:
| Macro | Site | Expands to |
|---|
@google_search | Google | https://www.google.com/search?q=... |
@youtube_search | YouTube | https://www.youtube.com/results?search_query=... |
@amazon_search | Amazon | https://www.amazon.com/s?k=... |
@reddit_search | Reddit | https://www.reddit.com/search.json?q=...&limit=25 |
@reddit_subreddit | Reddit | https://www.reddit.com/r/....json?limit=25 |
@wikipedia_search | Wikipedia | https://en.wikipedia.org/wiki/Special:Search?search=... |
@twitter_search | Twitter/X | https://twitter.com/search?q=... |
@yelp_search | Yelp | https://www.yelp.com/search?find_desc=... |
@spotify_search | Spotify | https://open.spotify.com/search/... |
@netflix_search | Netflix | https://www.netflix.com/search?q=... |
@linkedin_search | LinkedIn | https://www.linkedin.com/search/results/all/?keywords=... |
@instagram_search | Instagram | https://www.instagram.com/explore/tags/... |
@tiktok_search | TikTok | https://www.tiktok.com/search?q=... |
@twitch_search | Twitch | https://www.twitch.tv/search?term=... |
Usage in navigate endpoint
From server.js:872-934, the navigate endpoint accepts both url and macro:
POST /tabs/:tabId/navigate
{
"userId": "agent1",
"macro": "@youtube_search",
"query": "python tutorial"
}
Server-side expansion:
let targetUrl = url;
if (macro) {
targetUrl = expandMacro(macro, query) || url;
}
if (!targetUrl) throw new Error('url or macro required');
Macro takes precedence - if both macro and url are provided, macro is used and url serves as fallback.
Real expansion examples
Google search
Input:
{"macro": "@google_search", "query": "best restaurants near me"}
Expands to:
https://www.google.com/search?q=best%20restaurants%20near%20me
YouTube search
Input:
{"macro": "@youtube_search", "query": "how to bake bread"}
Expands to:
https://www.youtube.com/results?search_query=how%20to%20bake%20bread
Amazon search
Input:
{"macro": "@amazon_search", "query": "wireless headphones"}
Expands to:
https://www.amazon.com/s?k=wireless%20headphones
Wikipedia search
Input:
{"macro": "@wikipedia_search", "query": "quantum computing"}
Expands to:
https://en.wikipedia.org/wiki/Special:Search?search=quantum%20computing
LinkedIn search
Input:
{"macro": "@linkedin_search", "query": "software engineer"}
Expands to:
https://www.linkedin.com/search/results/all/?keywords=software%20engineer
Reddit JSON behavior
Reddit macros return JSON instead of HTML, enabling direct parsing without snapshot/scraping.
@reddit_search
From lib/macros.js:5:
'@reddit_search': (query) => `https://www.reddit.com/search.json?q=${encodeURIComponent(query || '')}&limit=25`
Example:
POST /tabs/:tabId/navigate
{"userId": "agent1", "macro": "@reddit_search", "query": "best programming languages"}
GET /tabs/:tabId/snapshot?userId=agent1
Returns raw JSON response:
{
"kind": "Listing",
"data": {
"children": [
{
"kind": "t3",
"data": {
"title": "What's the best programming language to learn in 2024?",
"selftext": "I'm a beginner and...",
"url": "https://reddit.com/r/learnprogramming/comments/abc123",
"score": 456,
"num_comments": 89
}
}
]
}
}
@reddit_subreddit
From lib/macros.js:6:
'@reddit_subreddit': (query) => `https://www.reddit.com/r/${encodeURIComponent(query || 'all')}.json?limit=25`
Fetch hot posts from a subreddit:
POST /tabs/:tabId/navigate
{"userId": "agent1", "macro": "@reddit_subreddit", "query": "programming"}
Expands to:
https://www.reddit.com/r/programming.json?limit=25
Returns JSON listing of top 25 posts in r/programming.
Use page.evaluate() to extract specific fields from the JSON instead of parsing the entire snapshot.
Implementation details
Macro expansion function
From lib/macros.js:18-21:
function expandMacro(macro, query) {
const macroFn = MACROS[macro];
return macroFn ? macroFn(query) : null;
}
- Returns
null if macro doesn’t exist (triggers fallback to url parameter)
- Query defaults to empty string if undefined
- Uses native
encodeURIComponent() for safety
Available macros lookup
From lib/macros.js:23-25:
function getSupportedMacros() {
return Object.keys(MACROS);
}
For programmatic discovery:
const { getSupportedMacros } = require('./lib/macros');
console.log(getSupportedMacros());
// ['@google_search', '@youtube_search', ...]
Special characters handling
Macros automatically handle special characters:
Spaces
# Query with spaces
{"macro": "@google_search", "query": "new york weather"}
# Expands to: ?q=new%20york%20weather
Ampersands
# Query with &
{"macro": "@amazon_search", "query": "pots & pans"}
# Expands to: ?k=pots%20%26%20pans
Unicode
# Query with emoji
{"macro": "@twitter_search", "query": "🔥 trending"}
# Expands to: ?q=%F0%9F%94%A5%20trending
Quotes
# Query with quotes
{"macro": "@google_search", "query": '"exact phrase search"'}
# Expands to: ?q=%22exact%20phrase%20search%22
Error handling
Unknown macro
If macro doesn’t exist:
POST /tabs/:tabId/navigate
{
"userId": "agent1",
"macro": "@nonexistent",
"query": "test",
"url": "https://google.com" # Fallback
}
Behavior: Falls back to url parameter. If no url provided, returns error: “url or macro required”.
Empty query
From lib/macros.js:2:
'@google_search': (query) => `https://www.google.com/search?q=${encodeURIComponent(query || '')}`
Empty queries are allowed:
{"macro": "@google_search", "query": ""}
# Expands to: https://www.google.com/search?q=
This navigates to the search homepage.
Workflow examples
Multi-site research
# 1. Google search
POST /tabs/:tabId/navigate
{"userId": "researcher", "macro": "@google_search", "query": "climate change effects"}
GET /tabs/:tabId/snapshot?userId=researcher
# Parse top result link (e.g., e5)
POST /tabs/:tabId/click
{"userId": "researcher", "ref": "e5"}
# 2. YouTube search
POST /tabs/:tabId2/navigate
{"userId": "researcher", "macro": "@youtube_search", "query": "climate change documentary"}
GET /tabs/:tabId2/snapshot?userId=researcher
# Get video thumbnails and titles
# 3. Reddit discussion
POST /tabs/:tabId3/navigate
{"userId": "researcher", "macro": "@reddit_search", "query": "climate change"}
GET /tabs/:tabId3/snapshot?userId=researcher
# Parse JSON directly - no need for element refs
E-commerce price comparison
# Search Amazon
POST /tabs/:tabId1/navigate
{"userId": "shopper", "macro": "@amazon_search", "query": "coffee maker"}
GET /tabs/:tabId1/snapshot?userId=shopper
# Extract prices from snapshot
# Search alternative site (no macro - use URL)
POST /tabs/:tabId2/navigate
{"userId": "shopper", "url": "https://www.target.com/s?searchTerm=coffee+maker"}
GET /tabs/:tabId2/snapshot?userId=shopper
# Compare prices
# Twitter trending
POST /tabs/:tabId1/navigate
{"userId": "monitor", "macro": "@twitter_search", "query": "#AI"}
# Instagram hashtag
POST /tabs/:tabId2/navigate
{"userId": "monitor", "macro": "@instagram_search", "query": "AI"}
# TikTok search
POST /tabs/:tabId3/navigate
{"userId": "monitor", "macro": "@tiktok_search", "query": "AI tutorial"}
Adding custom macros
Custom macros require modifying lib/macros.js - there’s no runtime registration API.
To add a new macro:
- Edit
lib/macros.js:
const MACROS = {
// ... existing macros ...
'@custom_search': (query) => `https://example.com/search?q=${encodeURIComponent(query || '')}`,
};
- Restart server:
- Use new macro:
POST /tabs/:tabId/navigate
{"userId": "agent1", "macro": "@custom_search", "query": "test"}
Macro expansion cost
Macro expansion is synchronous and lightweight:
const targetUrl = expandMacro(macro, query); // ~0.1ms
No measurable performance impact compared to direct URL usage.
JSON responses are typically faster than HTML:
- Smaller payload (25 posts ≈ 50KB JSON vs 500KB+ HTML)
- No need to wait for page hydration
- Direct parsing without accessibility tree traversal
For Reddit data extraction, always use @reddit_search or @reddit_subreddit macros to get JSON directly.
Debugging
See expanded URL
Check server logs for expanded URL:
npm start
# In another terminal
POST /tabs/:tabId/navigate
{"userId": "test", "macro": "@google_search", "query": "test"}
# Server logs:
# {"level":"info","msg":"navigated","tabId":"...","url":"https://www.google.com/search?q=test"}
List all macros
From AGENTS.md:87-100:
| Macro | Site |
|-------|------|
| `@google_search` | Google |
| `@youtube_search` | YouTube |
...
Or programmatically:
const { getSupportedMacros } = require('./lib/macros');
console.log(getSupportedMacros());