π· Piggy β
Headless browser automation for TypeScript/Bun/Node.js.
Built on Nothing Browser β real BoringSSL TLS, fingerprint spoofing at DocumentCreation, and 100+ APIs for scraping, automation, and data extraction.
β οΈ Version Requirement: Binary v0.1.14+ | Library v0.0.20+
Quick Start β
import piggy from "nothing-browser";
await piggy.launch({ mode: "tab", binary: "headless" });
await piggy.register("example", "https://example.com");
await piggy.example.navigate();
const title = await piggy.example.title();
console.log(title);
await piggy.close();Why Piggy? β
| Feature | Piggy | Puppeteer |
|---|---|---|
| Library size | ~50KB | ~50MB |
| Communication | Socket (fast) | CDP (complex) |
| Anti-detection | Built-in | Plugins needed |
| Fingerprint spoofing | DocumentCreation | Runtime (detectable) |
| API count | 100+ | ~50 |
Piggy is just a command mapper β thin wrapper around the Nothing Browser binary.
Known Issues & Platform Quirks β
πͺ Windows: file:// URLs Must Use Triple Slash β
On Windows, Qt WebEngine requires file:/// (three slashes) for local file URLs. Two slashes (file://) causes Qt to interpret the drive letter as a hostname, resulting in a silent load failure.
// β Wrong β Qt thinks "C" is the hostname
`file://C:/Users/me/page.html`
// β
Correct
`file:///C:/Users/me/page.html`Also make sure backslashes are converted to forward slashes:
const url = `file:///${join(pagesDir, "page.html").replace(/\\/g, "/")}`Always prefer import.meta.dir over process.cwd() for building paths β it's always the script's own folder regardless of where you ran the script from.
πͺ Windows: The Pipe Bug (Named Pipe Connection Failure) β
Symptom:
error: connect ENOENT \\.\pipe\piggy
errno: -4058,
syscall: "connect",
address: "\\\\.\\pipe\\piggy",
code: "ENOENT"What happened: The Nothing Browser binary spawned successfully, but Windows didn't let it create the named pipe in time β or Windows Defender / UAC blocked the binary from running as a background process entirely.
The fix:
- Open the folder containing your binary (e.g.
piggy-playground/a/) - Double-click
nothing-browser-headful.exedirectly - If Windows shows a security prompt, click Run Anyway
- Close the browser window that opens
- Re-run your script
This whitelists the binary with Windows so it can spawn freely from scripts.
Do we know exactly why this happens? No.
Is it going to be fixed? Also no.
Does the workaround always work? Yes. Will you get such errorsfrequently in windows? Yes.
This issue only affects Windows. Linux and macOS do not require this step.
API Families β
Core β
launch()β Start the browserconnect()β Connect to remote serverregister()β Register a siteclose()β Shutdown
Navigation β
navigate()β Go to URLreload()β Refresh pagegoBack()/goForward()β Historytitle()/url()/content()β Page info
Waiting β
wait.selector()β Wait for element statewait.function()β Wait for JS conditionwaitForResponse()β Wait for network
Interactions β
click()β Click elementstype()β Type texthover()β Hover mouseselect()β Dropdown optionskeyboardβ Key pressesmouseβ Mouse movementscrollβ Scroll page
Find β DOM Query β
find.css()/find.all()β CSS selectorfind.first()β First matchfind.byText()β By text contentfind.byAttr()β By attributefind.byTag()β By tag namefind.byPlaceholder()β By input placeholderfind.byRole()β By ARIA rolefind.closest()β Closest ancestorfind.parent()/find.children()β DOM traversalfind.filter()β Filter resultsfind.count()/find.exists()β Count/checkfind.visible()/find.enabled()/find.checked()β State checks
Provide β Data Extraction β
provide.text()/provide.textAll()β Extract textprovide.html()β Inner HTMLprovide.attr()/provide.attrAll()β Attributesprovide.table()β HTML table to JSONprovide.list()β Extract list itemsprovide.links()/provide.images()β Links & imagesprovide.form()β Form dataprovide.page()β Full page metadataprovide.div()β Element structureprovide.meta()β Meta tagsprovide.select()β Select dropdownprovide.json()β Embedded JSON-LD
Fetch & Search (Legacy) β
fetch.text()/fetch.textAll()β Quick textfetch.links()/fetch.linksAll()β Quick linksfetch.image()β Quick imagessearch.css()/search.id()β DOM snapshots
Capture β Network β
capture.start()/capture.stop()β Start/stop recordingcapture.requests()β HTTP requestscapture.ws()β WebSocket framescapture.cookies()β Network cookiescapture.storage()β Local/session storagecapture.clear()β Clear buffer
Interception β
intercept.block()β Block requestsintercept.redirect()β Redirectintercept.headers()β Modify headersintercept.respond()β Mock responsesintercept.modifyResponse()β Modify real responsesintercept.clear()β Clear rules
Cookies β
Session β
session.export()/session.import()β Save/load sessionsession.reload()β Hot reload cookies/profilesession.paths()β File pathssession.setWsSave()/session.setPingsSave()β Persistence opt-in
Expose β RPC β
exposeFunction()β Call Node.js from browserexposeAndInject()β Expose + inject in one callpiggy.expose()/piggy.unexpose()β Global functions
Iframe β
iframe.list()β List iframesiframe.evaluate()β Run JS in iframeiframe.click()/iframe.type()β Interact inside iframeiframe.text()/iframe.html()β Extract contentiframe.waitSel()β Wait for selector
Captcha & Block β
captcha.status()/captcha.resolve()β Detect/solvecaptcha.pause()/captcha.check()β Manual interventioncaptcha.setAutoRetry()β Auto retryblock.status()/block.retry()β Block detection
Dialog & Upload β
dialog.accept()/dialog.dismiss()β Handle alertsdialog.status()β Check pending dialogdialog.upload()β File uploaddialog.waitAndAccept()/dialog.waitAndDismiss()β Await dialog
Human Mode β
human.set()/human.get()β Configure behaviorhuman.type()/human.click()β Human-like interactions
Screenshot & PDF β
screenshot()β Take screenshotpdf()β Generate PDFblockImages()/unblockImages()β Image blocking
Proxy β
proxy.load()/proxy.fetch()/proxy.ovpn()β Load proxiesproxy.set()/proxy.enable()/proxy.disable()β Configureproxy.test()/proxy.testStop()β Health checkproxy.next()/proxy.rotation()β Rotateproxy.current()/proxy.stats()/proxy.list()β Statusproxy.save()β Save proxy list
API Server β
api()β Register endpointserve()/stopServer()β Start/stop HTTP serverroutes()β List routesall()/diff()β Multi-site operations
Storage β
store()β Schema-based data persistence
Tabs β
Events β
onEvent()/site.on()β Subscribe to events- captcha, dialog, navigate, exposed_call, proxy events
Types β
Search for an API β
Looking for something specific?
| What you want | Use this |
|---|---|
| Extract text from a div | provide.text() |
| Get all links on a page | provide.links() |
| Wait for element to appear | wait.selector() |
| Click a button inside iframe | iframe.click() |
| Handle a confirm dialog | dialog.accept() / dialog.dismiss() |
| Upload a file | dialog.upload() |
| Rotate proxies | proxy.next() / proxy.rotation() |
| Save session to file | session.export() / session.import() |
| Call Node.js from browser | exposeFunction() |
| Solve CAPTCHA | captcha.resolve() |
Response Samples β
See exactly what each command returns with real examples:
- Find API Response Samples
- Provide API Response Samples
- Capture API Response Samples
- Proxy API Response Samples
All examples use the same sample HTML page. Responses shown are real.
Next Steps β
- Installation β Get set up
- Quick Start β First scraper in 5 minutes
- Find API β DOM query (start here for scraping)
Nothing Ecosystem Β· Ernest Tech House Β· Kenya Β· 2026