Anonymous scraping¶
Not every task needs a managed account. Public pages — news, docs, pricing tables, public registries — don't need login, don't deserve a persistent profile, and shouldn't touch your signed-in sessions. Ghostvault's ephemeral sessions are built for exactly this: a throwaway anti-detect browser that opens, scrapes, and disappears.
Why this matters¶
A managed account is expensive state: a locked fingerprint, real session cookies, a profile directory you have to protect. Spinning one up to read a public Hacker News thread is wasteful and risky — you'd be exposing a sensitive session to an arbitrary third-party site for no reason.
Ephemeral sessions flip every default:
- No account, no profile, no DB row — nothing persists after close.
- Random identity per session — a fresh Camoufox fingerprint each time.
- Tuned for scraping — headless, images blocked, short idle timeout.
Once an ephemeral session is open, every browser tool works on it unchanged. There is no separate "scraping API" — gv_get_page_content, gv_get_page_links, gv_click_element, gv_scroll, gv_screenshot all just work, because the ephemeral session becomes the active target automatically.
The lifecycle¶
Two tools, always used as a pair:
| Tool | Role |
|---|---|
gv_open_ephemeral |
Launch a throwaway Camoufox + navigate to a URL. Returns a session_id. The session becomes the active target. |
gv_close_ephemeral |
Close the session, kill the browser, wipe the tempdir. Frees the slot. |
# Open → do stuff → close. Always close.
await gv.open_ephemeral(url="https://news.ycombinator.com")
content = await gv.get_page_content(include_screenshot=True)
links = await gv.get_page_links(limit=20)
await gv.scroll(direction="down", amount=3)
await gv.close_ephemeral() # frees the browser + removes the tempdir
Always close your ephemeral sessions
Each session is a full Firefox process. If you forget gv_close_ephemeral, the session lingers until the 5-minute idle timeout (see defaults below) or until you hit the concurrency cap. In a long-running script, close explicitly — don't rely on the idle timer.
Defaults — tuned for scraping¶
Ephemeral sessions have different defaults than managed accounts. Managed accounts are headful (so you can complete 2FA) and load images; ephemeral sessions are the opposite.
| Setting | Default | Why |
|---|---|---|
headless |
true |
Scraping doesn't need a visible window |
block_images |
true |
Faster text scraping; smaller memory footprint |
humanize |
false |
Read-only scraping doesn't need cursor movement |
auto_close_minutes |
5 |
Abandoned sessions clean themselves up |
max_concurrent |
3 |
Cap on simultaneous ephemeral sessions (each is a full Firefox process) |
Override any of these per call:
# Visible browser + keep images for screenshot-based scraping
await gv.open_ephemeral(
url="https://example.com/dashboard",
headless=False,
block_images=False,
)
Or change the defaults globally via env vars (see Advanced → Environment variables):
GHOSTVAULT_EPHEMERAL_HEADLESS=false
GHOSTVAULT_EPHEMERAL_BLOCK_IMAGES=false
GHOSTVAULT_EPHEMERAL_AUTO_CLOSE_MINUTES=15
GHOSTVAULT_EPHEMERAL_MAX_CONCURRENT=5
All browser tools work unchanged¶
This is the key design point. There is no gv_get_page_content_ephemeral. Once gv_open_ephemeral returns, the session is the active target and every browser tool operates on it exactly as it would on a managed account:
await gv.open_ephemeral(url="https://example.com")
await gv.open_url("https://example.com/about") # navigate
await gv.get_page_content(include_screenshot=True) # read text + see it
await gv.get_page_links(limit=50) # collect links
await gv.click_element("Read more") # follow a link
await gv.fill_input("Search", "pricing") # fill a public form
await gv.press_key("Enter") # submit
await gv.scroll(direction="down", amount=2) # scroll
await gv.screenshot(full_page=True) # capture
await gv.close_ephemeral()
A complete one-shot scrape¶
This is the pattern from examples/scripts/scrape_public_page.py — the simplest realistic scrape:
import asyncio, json
from ghostvault_sdk import GhostvaultClient
async def scrape(url: str) -> None:
async with GhostvaultClient() as gv:
# 1. Open a throwaway browser + navigate.
await gv.open_ephemeral(url)
# 2. Read text + screenshot (vision support).
await gv.get_page_content(include_screenshot=True, max_chars=3000)
# 3. Collect visible links.
await gv.get_page_links(limit=10)
# 4. Close — frees the browser, removes the tempdir.
await gv.close_ephemeral()
asyncio.run(scrape("https://news.ycombinator.com"))
Nothing persisted. No account was created, no profile written, no DB row inserted. The session is gone.
Multi-page scraping on one session¶
You don't need to re-open for every page. Keep the session open, navigate with gv_open_url or gv_click_element, and only close at the end. This is the pattern in examples/scripts/scrape_with_pagination.py:
async def scrape_paginated(start_url: str, pages: int) -> None:
async with GhostvaultClient() as gv:
await gv.open_ephemeral(url=start_url)
for i in range(pages):
await gv.get_page_content(max_chars=5000) # read current page
await gv.scroll(direction="down", amount=2) # engage
# Click the "Next" button — same session, new page
await gv.click_element("Next")
await gv.close_ephemeral() # one close at the end
This keeps you under the max_concurrent cap and avoids relaunching Firefox for every page.
Mixing ephemeral + managed accounts¶
The two modes coexist cleanly. Use ephemeral for public research, then switch to a managed account when you need authenticated access — Gmail, Drive, any login-gated site.
# 1. Scrape a public page anonymously (no account touched)
await gv.open_ephemeral(url="https://example.com/public-pricing")
data = await gv.get_page_content()
await gv.close_ephemeral()
# 2. Now switch to a managed account for authenticated work
await gv.open_account("acc_work123")
await gv.open_url("https://mail.google.com") # signed-in inbox
The active target tracks whichever you opened last. If you have both an ephemeral session and a managed account open, calls go to whichever was opened most recently — or you can make it explicit with gv_switch_account / by re-opening the target you want.
Don't mix sensitive accounts with arbitrary scrapes
If you're about to navigate to an untrusted URL, close any open managed account first (or use an ephemeral session instead). There's no reason to drag your real session cookies onto a random site.
When to use which¶
| Scenario | Use |
|---|---|
| Scrape a public page (news, docs, pricing) | gv_open_ephemeral |
| Fill a public form (contact, signup-free download) | gv_open_ephemeral |
| Multi-page crawl of a public site | gv_open_ephemeral + loop |
| Captcha-protected public scrape target | gv_open_ephemeral with humanize=True (see Human-like typing & clicks) |
| Login-required site (Gmail, Drive, SSO app) | Managed account — see Reading Gmail |
| Read Gmail / access Google services | Managed account |
| Anything where you need to stay signed in | Managed account |
Captcha-protected scrape targets¶
Some public pages run reCAPTCHA v3 or similar behavioral checks. The defaults (humanize=False) are fine for plain text scraping, but for protected targets, enable humanization for the session and follow the warm-up pattern from Human-like typing & clicks → reCAPTCHA v3 warm-up:
await gv.open_ephemeral(
url="https://protected-site.com",
humanize=True, # enable cursor humanization for this session
block_images=False, # some captcha scripts key off image loads
)
await gv.scroll(direction="down", amount=2, humanize=True)
# ...then scrape as usual
Troubleshooting¶
| Symptom | Fix |
|---|---|
gv_open_ephemeral errors with "max concurrent reached" |
You have 3 sessions open (default cap). Close one with gv_close_ephemeral, or raise GHOSTVAULT_EPHEMERAL_MAX_CONCURRENT. |
| Session disappeared mid-scrape | The 5-minute idle timer fired. For long scrapes, raise GHOSTVAULT_EPHEMERAL_AUTO_CLOSE_MINUTES, or call a browser tool periodically to reset the idle clock. |
| Screenshots look broken / missing images | block_images=True is the default. Pass block_images=False if you need accurate screenshots. |
| Site loads but text is empty | Heavy JS-rendered page. Add await gv.scroll(...) and a short asyncio.sleep to let it render, then re-read. |
gv_close_ephemeral says "no active session" |
It was already closed (by you, or by the idle timer). Safe to ignore. |
Related guides¶
- Human-like typing & clicks — for captcha-protected scrape targets
- Reading Gmail — the managed-account counterpart for authenticated reads
- Encryption at rest — irrelevant to ephemeral sessions (no profile to encrypt), but applies to any managed accounts you mix in