A tiny web-search superpower for AI coding agents: run one command, fan out several searches across Modal serverless containers, and get clean Markdown notes back.
modal-parallel-search is an Agent Skill plus a Modal-powered CLI. It is useful when an AI agent needs current web information but you do not want to set up a search API key, browser automation, a hosted service, or local fan-out scripts.
It works with any terminal-capable coding agent: Pi, Claude Code, Codex-style agents, OpenClaw, Hermes, or your own agent loop. If the agent can run modal run ..., it can use this tool.
- What it does: searches the web and returns research notes.
- Why Modal: the bursty work runs in short-lived cloud containers instead of on your laptop.
- What you need: a terminal, Python/uv or pip, and a Modal account/token.
- Search API keys: none required for search. The CLI uses
ddgsbackends. - Typical output: Markdown, because it is easy for people and AI agents to read.
- Cost note: this workload uses near $0 on Modal — the searches are tiny, short-lived CPU bursts. As of May 23, 2026, Modal gives you $30 USD/month in free credits every month once you add a credit card, so normal usage of this tool is effectively free.
Copy/paste these commands into your terminal:
curl -fsSL https://raw.githubusercontent.com/krittaprot/modal-parallel-search/main/scripts/install-skill.sh | bash
uv tool install modal
modal setup
modal run ~/.agents/skills/modal-parallel-search/scripts/modal_search_cli.py \
--query "Modal Python serverless" \
--max-results 1What those commands do:
- Install this skill into
~/.agents/skills/modal-parallel-search. - Install the Modal CLI.
- Sign in to Modal in your browser.
- Run a tiny smoke-test search.
If you do not have uv, install Modal with pip instead:
python3 -m pip install --user modalThe installer keeps the repo here:
~/.agents/skills/.repos/modal-parallel-search
And creates this agent-friendly path:
~/.agents/skills/modal-parallel-search
The important files are:
~/.agents/skills/modal-parallel-search/
├── SKILL.md
├── scripts/
│ ├── install-skill.sh
│ └── modal_search_cli.py
└── examples/
├── benchmark_queries.txt
└── queries.txt
Agent Skills-aware tools read SKILL.md. Simpler terminal agents can directly call scripts/modal_search_cli.py.
modal --versionIf that says command not found, install Modal:
uv tool install modalOr with pip:
python3 -m pip install --user modalFor a normal laptop/desktop:
modal setupThis opens a browser login flow and stores credentials locally, usually in ~/.modal.toml.
Then verify:
modal token infoIf you cannot use a browser, create a token in Modal and set it directly:
modal token set --token-id "YOUR_TOKEN_ID" --token-secret "YOUR_TOKEN_SECRET"For CI or temporary shells:
export MODAL_TOKEN_ID="YOUR_TOKEN_ID"
export MODAL_TOKEN_SECRET="YOUR_TOKEN_SECRET"Never commit token values to git.
modal run ~/.agents/skills/modal-parallel-search/scripts/modal_search_cli.py \
--query "Modal Python serverless" \
--max-results 1If you see # Search research notes, it worked.
curl -fsSL https://raw.githubusercontent.com/krittaprot/modal-parallel-search/main/scripts/install-skill.sh | bashYou can customize the install location:
AGENTS_SKILLS_DIR="$HOME/.agents/skills" bash scripts/install-skill.shYou can point the installer at a fork:
MODAL_PARALLEL_SEARCH_REPO_URL="https://github.com/YOU/modal-parallel-search.git" \
bash scripts/install-skill.shIf you prefer not to pipe a script into bash:
mkdir -p ~/.agents/skills/.repos
git clone https://github.com/krittaprot/modal-parallel-search.git \
~/.agents/skills/.repos/modal-parallel-search
rm -rf ~/.agents/skills/modal-parallel-search
ln -s ~/.agents/skills/.repos/modal-parallel-search \
~/.agents/skills/modal-parallel-searchCheck it:
ls ~/.agents/skills/modal-parallel-search/SKILL.mdgit -C ~/.agents/skills/.repos/modal-parallel-search pull --ff-onlyThe symlink at ~/.agents/skills/modal-parallel-search keeps pointing at the updated repo.
If you use Pi's package manager:
pi install git:github.com/krittaprot/modal-parallel-searchThen ask Pi to use the skill, or invoke it explicitly:
/skill:modal-parallel-search search recent Modal serverless agent examples
Run a single search:
modal run ~/.agents/skills/modal-parallel-search/scripts/modal_search_cli.py \
--query "recent Modal serverless agent examples" \
--max-results 3Run several searches in parallel:
modal run ~/.agents/skills/modal-parallel-search/scripts/modal_search_cli.py \
--queries-json '["pi coding agent", "Modal serverless Python", "coding agents CLI tools"]' \
--max-results 5 \
--timelimit wUse a plain text file with one query per line:
modal run ~/.agents/skills/modal-parallel-search/scripts/modal_search_cli.py \
--queries-file ~/.agents/skills/modal-parallel-search/examples/queries.txtQuick multi-query shortcut:
modal run ~/.agents/skills/modal-parallel-search/scripts/modal_search_cli.py \
--query "Modal pricing;;Modal web endpoints Python;;Modal volumes examples"Modal local entrypoints accept
--queryas one scalar value. Repeating--queryflags does not build a list. Use;;,--queries-json, or--queries-filefor multiple queries.
--query One query, or multiple joined by ";;"
--queries-json JSON array of query strings
--queries-file File with one query per line; # comments and blanks are ignored
--max-results Results per query [default: 5]
--backend auto, yahoo, brave, duckduckgo [default: auto]
--region Search region [default: us-en]
--safesearch on, moderate, off [default: moderate]
--timelimit d, w, m, y
--fetch-pages Fetch and extract top result pages [default: false]
--fetch-top-n Top results per query to fetch [default: 3]
--fetch-chars Max extracted characters per page [default: 4000]
--output-format markdown or json [default: markdown]
--show-events Print spawn/status events before final output [default: false]
--benchmark Compare sequential vs parallel wall time [default: false]
Show help:
modal run ~/.agents/skills/modal-parallel-search/scripts/modal_search_cli.py --helpSearch results include title, URL, and snippet. Add --fetch-pages when snippets are not enough and you want readable text from the top result pages.
modal run ~/.agents/skills/modal-parallel-search/scripts/modal_search_cli.py \
--query "Modal serverless pricing examples" \
--max-results 5 \
--fetch-pages \
--fetch-top-n 3Some sites block automated fetching. That is normal. The search result URL and snippet still remain in the notes.
Markdown is the default:
modal run ~/.agents/skills/modal-parallel-search/scripts/modal_search_cli.py \
--queries-json '["Modal Python serverless", "Modal web endpoints"]' \
--max-results 3Use JSON when another tool needs to parse the result. Add Modal's -q flag so Modal progress messages do not mix with the JSON:
modal run -q ~/.agents/skills/modal-parallel-search/scripts/modal_search_cli.py \
--query "Modal serverless pricing examples" \
--output-format jsonUse --benchmark to compare sequential query execution against parallel fan-out. This runs the same query set two ways and reports wall-clock timings.
modal run ~/.agents/skills/modal-parallel-search/scripts/modal_search_cli.py \
--queries-file ~/.agents/skills/modal-parallel-search/examples/benchmark_queries.txt \
--max-results 3 \
--benchmarkInstall Modal:
uv tool install modal
# or
python3 -m pip install --user modalIf pip installed it but your shell still cannot find it, restart the terminal or add your Python user scripts directory to PATH.
Run:
modal setup
modal token infoInstall Git first. On macOS, running git --version usually prompts Apple developer tools installation. On Ubuntu/Debian:
sudo apt-get update && sudo apt-get install -y gitUse valid JSON with double quotes:
--queries-json '["first query", "second query"]'PowerShell quoting can be different; using --queries-file is often easier on Windows.
Search backends can temporarily fail or rate-limit. Try:
--backend duckduckgoOr reduce --max-results / query count and retry.
- The local Modal entrypoint parses one or many queries.
- Each query becomes a serializable search spec.
- The CLI calls
search_one.spawn(...)for every query. - Modal runs searches in separate lightweight containers.
- Optional page-fetch mode calls
fetch_page.spawn(...)for top result URLs. - Results are collected in input order and printed as Markdown or JSON.
The Modal image is intentionally small:
image = (
modal.Image.debian_slim(python_version="3.12")
.uv_pip_install("ddgs==9.14.4")
)Markdown output:
# Search research notes
- Queries: 1
- Wall time: 2.481s
## 1. Modal Python serverless
Found 3 results in 1.882s.
### 1.1. Modal: High-performance AI infrastructure
Source: https://modal.com/...
Snippet text...JSON output:
{
"query_count": 2,
"wall_seconds": 3.214,
"results": [
{
"query": "Modal Python serverless",
"backend": "auto",
"elapsed_seconds": 1.882,
"result_count": 3,
"results": [
{"title": "...", "href": "https://...", "body": "..."}
]
}
]
}.
├── SKILL.md
├── README.md
├── package.json
├── scripts/
│ ├── install-skill.sh
│ └── modal_search_cli.py
└── examples/
├── benchmark_queries.txt
└── queries.txt
- Optional result caching in a Modal Volume.
- More beginner-friendly wrappers for common agent workflows.
- Additional output templates for citations and comparison tables.
MIT