Task-level site adapters, and running one on twenty identities
Hand an agent a browser and you have handed it a problem, not a solution. It still has to find the selectors, notice that the list is rendered client-side, discover that the JSON endpoint wants a CSRF header, and redo all of it when the markup moves. Most agent browsing sessions are that work, performed badly, on every run.
A recipe is the other shape: one file, one site, one command. The agent asks for reddit/hot and gets a JSON array back.
anti-detect-browser recipe run reddit/hot --temporary --json
anti-detect-browser recipe run google/search --profile shopper-01 --jq '.items[].title'
Recipes live in a separate public repository, antibrow/recipes, and both SDKs read the same set, so adding a site is a pull request there instead of a release of the package. The format is deliberately dull: export const meta declaring the hosts it may reach, and export async function run(ctx, args). No imports, no dependencies, no build step.
The part that needs a browser per identity
The reason to do this inside an antidetect browser rather than a scraping library is the second command:
anti-detect-browser recipe fanout amazon/search --profiles 'shopper-*' --concurrency 4
One command, four profiles, four personas, four cookie jars, four exit IPs. Concurrency is capped by your plan and read before anything is queued, so a fanout does not walk into a refusal halfway through and look like a broken recipe. One profile failing does not take the others down, and the result says which one failed and why.
That last detail matters more than it sounds. A recipe that returns an empty list when a site served a captcha is worse than one that throws, because on a fanout you need to know which identity got stopped. Every recipe in the repo detects the challenge and says so.
Where the code actually runs
run() executes inside the page the browser already opened, not in Node or Python. A relative ctx.fetchJson('/hot.json') therefore carries that profile's session for that site, and the SDK never touches a cookie to make it happen. A site with no JSON view can be read straight off the DOM instead.
Two constraints came out of building this that are worth knowing if you write your own:
A recipe may only reach the hosts it declares. Enforcement is at the network layer, not inside the helper functions, because code running in a page can always call fetch itself. A Reddit recipe cannot reach your mail. Blocked hosts come back in the result, which is also how you find out what a new recipe still needs to declare.
Some sites only answer a real navigation. Google replies to an in-page fetch for its own result page with a redirect interstitial, so meta.entry can interpolate arguments (/search?q={query}) and the runtime lands on the page instead. The host is never interpolated, since that is what the allowlist is checked against.
A dated run, not a claim
Every recipe was run against the live site on 2026-08-27, from a throwaway profile on one residential exit. Fifteen returned data: Reddit, Hacker News, Amazon, Walmart, Medium, GitHub, PyPI, npm, DuckDuckGo and the exit-IP checks.
Four did not, and the reasons are the useful part:
| recipe | what happened |
|---|---|
google/search |
consent wall, intermittently. The same recipe got a clean result page on one run and a wall on the next |
indeed/jobs |
Cloudflare interstitial |
yelp/search |
anti-bot challenge |
x/timeline |
not signed in, which is what a throwaway profile is |
None of that is fixable in the parsing code. Exit reputation, request cadence and the history a profile carries decide whether those sites answer at all. Those four ship marked unreviewed, which means they do not run unless you pass --allow-unreviewed, and their descriptions say they need a clean residential exit. If you were told a tool gets past bot management by default, that run is what actually happens.
When this is the wrong tool
If you need one site, once, and it has a public API, use the API. If you need a hundred pages of one site with no login and no identity requirements, a plain HTTP scraper with a proxy pool is cheaper than a browser per identity, and it will be faster. If the site you care about is not in the repo, you are writing the adapter either way, and doing it here only pays off if you also want it to run on several identities.
The overlap where this earns its place is narrow and specific: tasks that need a session, on sites that check who is asking, repeated across accounts you are authorized to operate.
Check it yourself
recipe info <id> prints the arguments, the declared hosts, the identity requirement and the review state before you run anything, and recipe guide prints the authoring guide for you or your agent. The five-minute version:
npm install -g anti-detect-browser
anti-detect-browser recipe update
anti-detect-browser recipe list
anti-detect-browser recipe run hackernews/top --temporary --jq '.items[].title'
More on the agent side of this in Giving an MCP agent a browser that keeps its session, and on why identity separation is the hard part in Why accounts get flagged. Proxy choices are covered in Residential vs datacenter proxies.
Try it on the free tier.
Unlimited local profiles, no credit card. Check it against the detectors yourself.