Each module did:
X_DIR = Path(os.path.expanduser("~/.pry/x"))
After:
from paths import PRY_DATA_DIR
X_DIR = PRY_DATA_DIR / "x"
The module-level Path construction is preserved, so the rest of the
code is unchanged. PRY_DATA_DIR is read once at import (overridable via
the env var of the same name).
Verified:
- 407 tests collect (was 5 collection errors from a misplaced import)
- 83 sampled tests pass (intelligence, proxy_manager, x402, agency,
gdpr, referrals, marketplace, api)
- 0 remaining hardcoded ~/.pry references in .py files
Follow-up: paths.py adds subdir(name) helper for new code that wants
auto-mkdir; existing modules still call .mkdir(exist_ok=True) themselves
to preserve the eager-init behavior they had before.
|
||
|---|---|---|
| .forgejo | ||
| .github | ||
| browser-extension | ||
| docs/adr | ||
| llm_providers | ||
| shopify-app | ||
| stealth_scripts | ||
| templates | ||
| tests | ||
| wordpress-plugin | ||
| workers | ||
| .dockerignore | ||
| .env.example | ||
| .gitignore | ||
| .pre-commit-config.yaml | ||
| .secretsallow | ||
| account_manager.py | ||
| actor_marketplace.py | ||
| adaptive.py | ||
| advanced.py | ||
| agency.py | ||
| AGENTS.md | ||
| ai_plugin.py | ||
| alerter.py | ||
| anomaly.py | ||
| api.py | ||
| ARCHITECTURE.md | ||
| AUDIT.md | ||
| auth.py | ||
| auth_connector.py | ||
| automator.py | ||
| behavioral_biometrics.py | ||
| browser_pool.py | ||
| cache.py | ||
| camoufox_integration.py | ||
| captcha_solver.py | ||
| cli.py | ||
| client.py | ||
| commerce_sync.py | ||
| compliance.py | ||
| config.py | ||
| cookie_warmer.py | ||
| costing.py | ||
| crm_sync.py | ||
| db.py | ||
| DECISIONS.md | ||
| dedup.py | ||
| DEPLOYMENT.md | ||
| destinations.py | ||
| DEVELOPMENT.md | ||
| docker-compose.yml | ||
| Dockerfile | ||
| email_scraper.py | ||
| enrichment.py | ||
| errors.py | ||
| extraction.py | ||
| extractor.py | ||
| FEATURES.md | ||
| freshness.py | ||
| gdpr.py | ||
| gdpr_real.py | ||
| glama.json | ||
| graphql_discovery.py | ||
| intelligence.py | ||
| jobqueue.py | ||
| lazy_load.py | ||
| LICENSE | ||
| LICENSE-BSL-STEALTH | ||
| LICENSING_PRICING_STRATEGY.md | ||
| llm_features.py | ||
| Makefile | ||
| markdown_gen.py | ||
| mconfig.py | ||
| mcp_production.py | ||
| mcp_server.py | ||
| mcp_sse.py | ||
| MCP_X402_AUDIT.md | ||
| monitor.py | ||
| network.py | ||
| observability.py | ||
| ocr_extractor.py | ||
| openapi.json | ||
| parser.py | ||
| paths.py | ||
| pdf_extractor.py | ||
| pipeline.py | ||
| pipelines.py | ||
| PLAN.md | ||
| proxy_manager.py | ||
| proxy_referrals.py | ||
| pry_sdk.py | ||
| pryextras.py | ||
| pryfile.py | ||
| pulsemcp.json | ||
| pyproject.toml | ||
| quality.py | ||
| ratelimit.py | ||
| README.md | ||
| reconciliation.py | ||
| referrals.py | ||
| reports.py | ||
| reports_real.py | ||
| requirements.txt | ||
| review.py | ||
| ROADMAP.md | ||
| schema_extraction.py | ||
| scraper.py | ||
| SECURITY.md | ||
| seo_monitor.py | ||
| sessions.py | ||
| settings.py | ||
| shadow_dom.py | ||
| signup_automator.py | ||
| smithery.yaml | ||
| STATUS.md | ||
| stealth_engine.py | ||
| structure_monitor.py | ||
| tasks.py | ||
| template_engine.py | ||
| test.sh | ||
| TESTING.md | ||
| tls_fingerprint.py | ||
| training_data.py | ||
| ultimate_scraper.py | ||
| USAGE.md | ||
| webhook_delivery.py | ||
| websocket_scraper.py | ||
| wrangler.toml | ||
| x402.py | ||
| x402_middleware.py | ||
//: # (Copyright (c) 2026 Rug Munch Media LLC)
Pry — Open any website
Self-hosted web scraping + browser automation API. Cloudflare bypass, document parsing, AI summarization. No API keys needed.
Quickstart
# Install
pip install pry
# Start the server
pry serve
# Scrape a URL
pry open https://example.com
# With JSON extraction
pry open https://store.com/product --json --schema product.json
# Crawl a site
pry crawl https://docs.com --max-pages 20 -o data.json
Docker
docker compose up -d
# Pry on :8002, FlareSolverr on :8191
CLI Reference
| Command | Description |
|---|---|
pry open <url> |
Scrape URL to clean markdown |
pry watch <url> |
Monitor page for changes |
pry crawl <url> |
Crawl multiple pages |
pry batch <file> |
Batch scrape URLs from file |
pry parse <url> |
Parse PDF/DOCX/image |
pry ss <url> |
Take screenshot |
pry run [pry.yml] |
Execute job file |
pry serve |
Start API server |
API
POST /v1/scrape
{"url": "https://example.com", "bypassCloudflare": true, "formats": ["markdown"]}
POST /v1/crawl
{"url": "https://docs.com", "maxPages": 10, "maxDepth": 2}
POST /v1/automate
{"steps": [{"action": "navigate", "url": "https://...", {"action": "click", "selector": "#btn"}]]}
POST /v1/vision
Analyze images with free OpenRouter vision models. Auto-fallback across 5 models.
{"url": "https://...", "question": "What is shown?"}
POST /v1/extract
Extract structured data with JSON schema + optional AI fallback.
POST /v1/batch
Scrape up to 50 URLs in parallel.
POST /v1/compare
Diff two URLs.
POST /v1/summarize
AI summarization via local Ollama (free, private).
GET /health
Service health + cache stats + active sessions.
Python SDK
from pry_sdk import PryCrawl
mc = PryCrawl("http://localhost:8002")
result = await mc.scrape("https://example.com")
print(result["data"]["markdown"])
from pry_sdk import PryCrawlSync
mc = PryCrawlSync("http://localhost:8002")
result = mc.scrape("https://example.com")
SDK Methods
| Method | Description |
|---|---|
scrape(url, **opts) |
Scrape to markdown |
scrape_json(url, schema, **opts) |
Scrape with JSON extraction |
crawl(url, max_pages, **opts) |
Crawl site |
map(url, limit) |
Discover URLs |
parse(url) |
Parse document |
automate(steps, **opts) |
Browser automation |
screenshot(url) |
Screenshot page |
health() |
Service health |
MCP (AI Agent Integration)
POST /mcp/call
{"name": "pry_scrape", "arguments": {"url": "https://..."}}
Compatible with Claude, Hermes, Cursor, and any MCP client.
Features
- 4-tier anti-detection: Direct → FlareSolverr → Playwright → Googlebot
- Cloudflare bypass: Automatic via FlareSolverr
- Document parsing: PDF, DOCX, images (OCR), CSV, JSON
- Browser automation: Login flows, form filling, sessions
- Vision AI: Free OpenRouter vision models with auto-fallback
- Diff tracking: Page change monitoring with webhooks
- Batch processing: Parallel scrape with templates
- SEO analysis: Title, meta, headings, keywords, readability
- Schema extraction: JSON-LD, Open Graph, microdata
- Export formats: JSON, CSV, RSS, TXT, SQL
- Rate limiting: Per-IP token bucket (default 120 RPM)
- Caching: LRU + Redis with TTL-based invalidation
- WebSocket streaming: Real-time job progress
- Circuit breaker: Per-domain backoff on failures
Architecture
┌─────────────┐ ┌──────────────┐ ┌──────────┐
│ Client │→ │ Pry API │→ │ Scraper │
│ (CLI/SDK) │ │ (FastAPI) │ │ Engine │
└─────────────┘ └──────┬───────┘ └────┬─────┘
│ ├─ Direct HTTP
│ ├─ FlareSolverr
│ ├─ Playwright
│ └─ Googlebot
│
┌────┴────┐
│ Cache │
│ Redis │
│ Rate │
└─────────┘
Deployment
Docker Compose (recommended)
docker compose up -d
Bare metal
pip install pry
playwright install chromium
# Optional: docker run -d -p 8191:8191 ghcr.io/flaresolverr/flaresolverr
pry serve
Environment
| Variable | Default | Description |
|---|---|---|
PRY_URL |
http://localhost:8005 |
API endpoint for CLI |
TOR_ENABLED |
false |
Enable Tor routing |
PROXY_URL |
— | HTTP/SOCKS proxy URL |
RATE_LIMIT_RPM |
120 |
Requests per minute |
OPENROUTER_API_KEY |
— | For vision endpoint |
Development
make install # Install with dev deps
make dev # Start hot-reload server
make lint # ruff check
make format # ruff format
make typecheck # mypy
make test # pytest
make check # Full audit
License
Pry is dual-licensed to balance community openness with the protection of its advanced anti-detection and stealth capabilities.
-
Most code (core functionality, utilities, and standard modules) is licensed under the MIT License. This permissive license encourages community contributions and wide adoption. See LICENSE.
-
The anti-detection / stealth module is licensed under the BSL 1.1 (Business Source License). This module is free to use for non-production purposes; a commercial license is required for any production use. See LICENSE-BSL-STEALTH.
Additional Use Grant
Under the BSL 1.1, an Additional Use Grant permits free use of the stealth module for:
- Personal, non-commercial projects
- Non-production testing and evaluation
- Academic research
- Non-commercial open source software development
- A 90-day evaluation period for any other purpose
For commercial licensing (production use, enterprise deployment, or embedding in proprietary products), please contact: enterprise@rugmunch.io