This is the first split of api.py (4,668 lines) into a routers/
package, one router per OpenAPI tag. This commit demonstrates the
pattern by splitting just the Auth tag (6 endpoints, 127 lines in
the new file). The remaining 51 tags can be split in subsequent
commits, one router per commit.
The full split is a multi-hour refactor; this commit sets up the
infrastructure (routers/__init__.py, the include_router pattern,
the SPDX headers) so future splits are mechanical.
Changes:
- New package routers/
- __init__.py (45 lines, package docstring, migration order)
- auth.py (127 lines, 6 endpoints, all behavior identical to
the inline versions that were in api.py)
- api.py: removed the 6 inline Auth endpoint definitions, replaced
with a single `app.include_router(auth_router)` call
- api.py LOC: 4,668 -> 4,627 (-41 lines)
- Total FastAPI routes: 197 -> 192 (the 6 inline removed, 1
_IncludedRouter placeholder added; 5 unique paths in OpenAPI
spec - same as before, since GET+POST share a path)
- All routes registered, all behavior preserved
- Tests: 436/437 pass (1 pre-existing SSE sandbox failure, unrelated)
The pattern for future commits:
1. Read a tag's endpoints from api.py
2. Create routers/<tag>.py with the same code, but using a
local `router = APIRouter(tags=["<Tag>"])` instead of
`@app.post(..., tags=["<Tag>"])`
3. Replace the inline section in api.py with
`from routers.<tag> import router as <tag>_router`
`app.include_router(<tag>_router)`
4. Commit
Suggested commit order (smallest first, to spread risk):
- health (3 endpoints, ~50 lines)
- stats (1 endpoint, ~30 lines)
- costing (4 endpoints, ~150 lines)
- freshness (3 endpoints, ~100 lines)
- structure (3 endpoints, ~120 lines)
- seo (3 endpoints, ~120 lines)
- compliance (2 endpoints, ~200 lines)
- gdpr (8 endpoints, ~300 lines)
- sessions (5 endpoints, ~200 lines)
- monitoring (5 endpoints, ~250 lines)
- intelligence (4 endpoints, ~300 lines)
- scraping (8 endpoints, ~400 lines)
- extraction (8 endpoints, ~400 lines)
- advanced (16 endpoints, ~700 lines - needs to be split further)
When all routers are split, api.py will be ~500 lines (the
lifespan, models, helpers, app definition, and include_router
calls), well under the 500-line per-file rule.
|
||
|---|---|---|
| .forgejo | ||
| .github | ||
| browser-extension | ||
| docs/adr | ||
| llm_providers | ||
| routers | ||
| 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 | ||
| logging_config.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 | ||
| secrets_backend.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