Python client for the Pre.dev API — AI-powered software specs + browser automation.
Specs
- 🚀 Fast Spec: Comprehensive specifications for MVPs and prototypes
- 🔍 Deep Spec: Ultra-detailed specifications for complex systems
- ⚡ Async Spec: Non-blocking async methods with status polling
- 📄 File upload support (PDFs, docs, images as reference context)
Browser automation (NEW)
- 🌐 Browser Tasks: Scrape, fill forms, navigate pages with structured JSON output
- 📡 SSE streaming: Watch execution live — screenshots, plans, actions
- ⏱ Async mode: Fire-and-forget, poll for progress
- 🔁 Retrieval: Get any past task with full timeline for audit/replay
Quality of life
- ✨ Full type hints
- 🛡 Custom exceptions for auth / rate limit / API errors
pip install predev-apifrom predev_api import PredevAPI
# Initialize the predev client with your API key
predev = PredevAPI(api_key="your_api_key_here")
# Generate a fast specification
result = predev.fast_spec(
input_text="Build a task management app with team collaboration"
)
print(result)All fast_spec, deep_spec, fast_spec_async, and deep_spec_async methods support optional file uploads. This allows you to provide architecture documents, requirements files, design mockups, or other context files to improve specification generation.
from predev_api import PredevAPI
predev = PredevAPI(api_key="your_api_key")
# Just pass the file path as a string
result = predev.fast_spec(
input_text="Generate specs based on these requirements",
file="path/to/requirements.pdf"
)# Open and upload a file
with open("architecture.doc", "rb") as f:
result = predev.deep_spec(
input_text="Create comprehensive specs",
file=f
)
# Or pass a file-like object
from io import BytesIO
file_content = BytesIO(b"Design specifications...")
result = predev.fast_spec(
input_text="Generate specs",
file=file_content
)- PDF documents (
*.pdf) - Word documents (
*.doc,*.docx) - Text files (
*.txt) - Images (
*.jpg,*.png,*.jpeg)
When you upload a file, the response includes:
result = predev.fast_spec(
input_text="Based on the design document",
file="design.pdf"
)
print(result.uploadedFileName) # "design.pdf"
print(result.uploadedFileShortUrl) # "https://api.pre.dev/f/xyz123"
print(result.codingAgentSpecUrl) # Spec for AI systems
print(result.humanSpecUrl) # Spec for humansThe Pre.dev API uses API key authentication. Get your API key from the pre.dev dashboard under Settings → API Keys:
predev = PredevAPI(api_key="your_api_key")fast_spec(input_text: str, current_context: Optional[str] = None, doc_urls: Optional[List[str]] = None) -> SpecResponse
Generate a fast specification (30-40 seconds, ~5-10 credits).
Parameters:
input_text(required):str- Description of what you want to buildcurrent_context(optional):str- Existing project contextdoc_urls(optional):List[str]- Documentation URLs to reference (e.g., Stripe docs, framework docs)
Returns: SpecResponse object with complete specification data
Example:
result = predev.fast_spec(
input_text="Build a SaaS project management tool with real-time collaboration"
)Example with Documentation URLs:
result = predev.fast_spec(
input_text="Build a payment processing integration with Stripe",
doc_urls=["https://stripe.com/docs/api"]
)
# When doc_urls are provided, the response includes zippedDocsUrls:
# result.zippedDocsUrls = [
# ZippedDocsUrl(
# platform="stripe.com",
# masterZipShortUrl="https://api.pre.dev/s/xyz789"
# )
# ]
# These zipped documentation folders can be downloaded and help coding agents
# stay on track by providing complete, up-to-date documentation context.deep_spec(input_text: str, current_context: Optional[str] = None, doc_urls: Optional[List[str]] = None) -> SpecResponse
Generate a deep specification (2-3 minutes, ~10-50 credits).
Parameters: Same as fast_spec
Returns: SpecResponse object with comprehensive specification data
Example:
result = predev.deep_spec(
input_text="Build a healthcare platform with HIPAA compliance"
)fast_spec_async(input_text: str, current_context: Optional[str] = None, doc_urls: Optional[List[str]] = None) -> AsyncResponse
Generate a fast specification asynchronously (returns immediately).
Parameters: Same as fast_spec
Returns: AsyncResponse object with specId for polling
Example:
result = predev.fast_spec_async(
input_text="Build a comprehensive e-commerce platform"
)
# Returns: AsyncResponse(specId="spec_123", status="pending")deep_spec_async(input_text: str, current_context: Optional[str] = None, doc_urls: Optional[List[str]] = None) -> AsyncResponse
Generate a deep specification asynchronously (returns immediately).
Parameters: Same as fast_spec
Returns: AsyncResponse object with specId for polling
Example:
result = predev.deep_spec_async(
input_text="Build a fintech platform with regulatory compliance"
)
# Returns: AsyncResponse(specId="spec_456", status="pending")Check the status of an async specification generation request.
Parameters:
spec_id(required):str- The specification ID from async methods
Returns: SpecResponse object with current status and data (when completed)
Example:
status = predev.get_spec_status("spec_123")
# Returns SpecResponse with status: "pending" | "processing" | "completed" | "failed"list_specs(limit: Optional[int] = None, skip: Optional[int] = None, endpoint: Optional[Literal["fast_spec", "deep_spec"]] = None, status: Optional[Literal["pending", "processing", "completed", "failed"]] = None) -> ListSpecsResponse
List all specs with optional filtering and pagination.
Parameters:
limit(optional):int- Results per page (1-100, default: 20)skip(optional):int- Offset for pagination (default: 0)endpoint(optional):"fast_spec" | "deep_spec"- Filter by endpoint typestatus(optional):"pending" | "processing" | "completed" | "failed"- Filter by status
Returns: ListSpecsResponse object with specs array and pagination metadata
Examples:
# Get first 20 specs
result = predev.list_specs()
# Get completed specs only
completed = predev.list_specs(status='completed')
# Paginate: get specs 20-40
page2 = predev.list_specs(skip=20, limit=20)
# Filter by endpoint type
fast_specs = predev.list_specs(endpoint='fast_spec')find_specs(query: str, limit: Optional[int] = None, skip: Optional[int] = None, endpoint: Optional[Literal["fast_spec", "deep_spec"]] = None, status: Optional[Literal["pending", "processing", "completed", "failed"]] = None) -> ListSpecsResponse
Search for specs using regex patterns.
Parameters:
query(required):str- Regex pattern (case-insensitive)limit(optional):int- Results per page (1-100, default: 20)skip(optional):int- Offset for pagination (default: 0)endpoint(optional):"fast_spec" | "deep_spec"- Filter by endpoint typestatus(optional):"pending" | "processing" | "completed" | "failed"- Filter by status
Returns: ListSpecsResponse object with matching specs and pagination metadata
Examples:
# Search for "payment" specs
payment_specs = predev.find_specs(query='payment')
# Search for specs starting with "Build"
build_specs = predev.find_specs(query='^Build')
# Search: only completed specs mentioning "auth"
auth_specs = predev.find_specs(
query='auth',
status='completed'
)
# Complex regex: find SaaS or SASS projects
saas_specs = predev.find_specs(query='saas|sass')Regex Pattern Examples:
| Pattern | Matches |
|---|---|
payment |
"payment", "Payment", "make payment" |
^Build |
Specs starting with "Build" |
platform$ |
Specs ending with "platform" |
(API|REST) |
Either "API" or "REST" |
auth.*system |
"auth" then anything then "system" |
\\d{3,} |
3+ digits (budgets, quantities) |
saas|sass |
SaaS or SASS |
@dataclass
class AsyncResponse:
specId: str # Unique ID for polling (e.g., "spec_abc123")
status: Literal['pending', 'processing', 'completed', 'failed']@dataclass
class SpecResponse:
# Basic info
_id: Optional[str] = None # Internal ID
created: Optional[str] = None # ISO timestamp
endpoint: Optional[Literal['fast_spec', 'deep_spec']] = None
input: Optional[str] = None # Original input text
status: Optional[Literal['pending', 'processing', 'completed', 'failed']] = None
success: Optional[bool] = None
# Output data (when completed)
uploadedFileShortUrl: Optional[str] = None # URL to input file
uploadedFileName: Optional[str] = None # Name of input file
codingAgentSpecUrl: Optional[str] = None # Spec optimized for AI/LLM systems
humanSpecUrl: Optional[str] = None # Spec optimized for human readers
totalHumanHours: Optional[int] = None # Estimated hours for human developers
# Direct returns (new)
codingAgentSpecJson: Optional[CodingAgentSpecJson] = None # Simplified JSON for coding tools
codingAgentSpecMarkdown: Optional[str] = None # Simplified markdown for coding tools
humanSpecJson: Optional[HumanSpecJson] = None # Full JSON with hours/personas/roles
humanSpecMarkdown: Optional[str] = None # Full markdown with all details
executionTime: Optional[int] = None # Processing time in milliseconds
# Integration URLs (when completed)
predevUrl: Optional[str] = None # Link to pre.dev project
# Documentation (when doc_urls provided)
zippedDocsUrls: Optional[List[ZippedDocsUrl]] = None
# Complete documentation as zipped folders
# Helps coding agents stay on track with full context
# Each entry contains platform name and download URL
# Error handling
errorMessage: Optional[str] = None # Error details if failed
progress: Optional[int] = None # Overall progress percentage (0-100)
progressMessage: Optional[str] = None # Detailed progress message
# Credit usage - available during processing (real-time) and on completion
# Fast spec: ~5-10 credits, Deep spec: ~10-50 credits
creditsUsed: Optional[float] = None # Total credits consumed by this spec generation@dataclass
class SpecCoreFunctionality:
name: str
description: str
priority: Optional[str] = None
@dataclass
class SpecTechStackItem:
name: str
category: str
@dataclass
class SpecPersona:
title: str
description: str
primaryGoals: Optional[List[str]] = None
painPoints: Optional[List[str]] = None
keyTasks: Optional[List[str]] = None
@dataclass
class SpecRole:
name: str
shortHand: str
@dataclass
class CodingAgentSubTask:
id: Optional[str] = None
description: str = ""
complexity: str = ""
@dataclass
class CodingAgentStory:
id: Optional[str] = None
title: str = ""
description: Optional[str] = None
acceptanceCriteria: Optional[List[str]] = None
complexity: Optional[str] = None
subTasks: Optional[List[CodingAgentSubTask]] = None
@dataclass
class CodingAgentMilestone:
milestoneNumber: int = 0
description: str = ""
stories: Optional[List[CodingAgentStory]] = None
@dataclass
class CodingAgentSpecJson:
title: Optional[str] = None
executiveSummary: str = ""
coreFunctionalities: Optional[List[SpecCoreFunctionality]] = None
techStack: Optional[List[SpecTechStackItem]] = None
techStackGrouped: Optional[Dict[str, List[str]]] = None
milestones: Optional[List[CodingAgentMilestone]] = None
@dataclass
class HumanSpecSubTask:
id: Optional[str] = None
description: str = ""
hours: float = 0
complexity: str = ""
roles: Optional[List[SpecRole]] = None
@dataclass
class HumanSpecStory:
id: Optional[str] = None
title: str = ""
description: Optional[str] = None
acceptanceCriteria: Optional[List[str]] = None
hours: float = 0
complexity: Optional[str] = None
subTasks: Optional[List[HumanSpecSubTask]] = None
@dataclass
class HumanSpecMilestone:
milestoneNumber: int = 0
description: str = ""
hours: float = 0
stories: Optional[List[HumanSpecStory]] = None
@dataclass
class HumanSpecJson:
title: Optional[str] = None
executiveSummary: str = ""
coreFunctionalities: Optional[List[SpecCoreFunctionality]] = None
personas: Optional[List[SpecPersona]] = None
techStack: Optional[List[SpecTechStackItem]] = None
techStackGrouped: Optional[Dict[str, List[str]]] = None
milestones: Optional[List[HumanSpecMilestone]] = None
totalHours: Optional[float] = None
roles: Optional[List[SpecRole]] = None@dataclass
class ListSpecsResponse:
specs: List[SpecResponse] # Array of spec objects
total: int # Total count of matching specs
hasMore: bool # Whether more results are availableCheck out the examples directory for detailed usage examples.
For more information about the Pre.dev Architect API, visit:
For issues, questions, or contributions, please visit the GitHub repository.
Run browser automation — navigate, interact, and/or extract data from any web page. Each task navigates a URL, performs actions, and optionally returns typed JSON.
from predev_api import PredevAPI
client = PredevAPI(api_key="your_api_key")
result = client.browser_agent([
{
"url": "https://news.ycombinator.com",
"instruction": "Extract the top 5 stories",
"output": {
"type": "object",
"properties": {
"stories": {
"type": "array",
"items": {
"type": "object",
"properties": {
"title": {"type": "string"},
"points": {"type": "number"},
},
},
},
},
},
}
])
for story in result["results"][0]["data"]["stories"]:
print(f"{story['title']} ({story['points']} pts)")result = client.browser_agent([
{"url": "https://example.com", "output": {"type": "object", "properties": {"heading": {"type": "string"}}}}
])
print(result["results"][0]["data"]) # {'heading': 'Example Domain'}
print(result["totalCreditsUsed"]) # 0.1Yields events as the agent runs. Good for showing progress in a UI.
for msg in client.browser_agent(tasks, stream=True):
e, d = msg["event"], msg["data"]
if e == "task_event":
# navigation | screenshot | plan | action | validation | done
print(f"[{d['type']}]", d.get("data"))
elif e == "task_result":
print(f"Task {d['taskIndex']} done:", d.get("data"))
elif e == "done":
print("Batch complete:", d["totalCreditsUsed"], "credits")
elif e == "error":
print("Batch error:", d)Returns the batch ID immediately. Use for long-running batches or background jobs.
import time
r = client.browser_agent(tasks, run_async=True)
# {"id": "batch_abc", "status": "processing", "completed": 0, "total": 3}
while True:
state = client.get_browser_agent(r["id"])
print(f"{state['completed']}/{state['total']}")
for done in state["results"]:
print(f" ✓ {done['url']} -> {done.get('data')}")
if state["status"] == "completed":
break
time.sleep(1)Each task's behavior is determined by which fields are set:
| Fields | Shape | Example |
|---|---|---|
url + output |
Scrape | Extract structured data from a page |
url + instruction |
Act | Click, navigate, search |
url + instruction + input |
Form fill | Fill & submit a form |
url + instruction + output |
Act + extract | Navigate then extract data |
Every task records navigation, screenshots, LLM plans, actions, validations. Retrieve for audit, replay, or debugging. Screenshots are uploaded to a CDN during execution — retrieved events contain data["url"], not base64.
details = client.get_browser_agent(batch_id, include_events=True)
for result in details["results"]:
for ev in result.get("events", []):
if ev["type"] == "screenshot":
# ev["data"]["url"] is a permanent CDN URL. Use directly in an <img>.
print(f"Iter {ev.get('iteration')} screenshot:", ev["data"]["url"])
elif ev["type"] == "plan":
print(f"Iter {ev.get('iteration')} plan: {ev['data'].get('notes')}")Note: The live SSE stream (
stream=True) still sends screenshots inline as base64 (ev["data"]["base64"]) so live UIs render instantly. The retrieval path (get_browser_agent) always returns CDN URLs.
# Scrape 100 URLs with 20 browsers in parallel
urls = [...100 urls...]
result = client.browser_agent(
[{"url": u, "output": {"type": "object", "properties": {"title": {"type": "string"}}}} for u in urls],
concurrency=20
)
print(f"{result['completed']}/{result['total']} done in {result['totalCreditsUsed']} credits")| Method | Returns | Use when |
|---|---|---|
browser_agent(tasks, concurrency=N) |
dict | Default — wait for completion |
browser_agent(tasks, stream=True) |
iterator of SSE dicts | Live UI showing execution timeline |
browser_agent(tasks, run_async=True) |
dict (empty results, returned immediately) | Long batches, background jobs |
get_browser_agent(batch_id, include_events=False) |
dict | Poll progress or retrieve a completed batch |
| Status | Meaning |
|---|---|
SUCCESS |
Task completed, data extracted |
BLOCKED |
Page blocked automation (bot detection) |
TIMEOUT |
Task exceeded time limit |
LOOP |
Agent detected it was stuck in a loop |
ERROR |
Unexpected error |
NO_TARGET |
Could not find target elements |
CAPTCHA_FAILED |
CAPTCHA solve failed |
- Minimum: 0.1 credits per task ($0.01)
- 10x margin on underlying LLM + sandbox compute
- 1 credit = $0.10
The browser-task endpoints raise typed exceptions for the most common
gating cases. Every billing-gate exception carries an action_url — a
deep link back to pre.dev that auto-opens the right modal (subscribe /
buy credits) when the user lands there:
from predev_api import (
PredevAPI,
InsufficientCreditsError,
SubscriptionRequiredError,
RateLimitError,
QueueFullError,
)
import webbrowser
client = PredevAPI(api_key="your_pre.dev_api_key")
try:
client.browser_agent([{"url": "https://example.com", "instruction": "extract h1"}])
except InsufficientCreditsError as e:
# Send the user to the credits modal — `action_url` is the canonical link.
if e.action_url:
webbrowser.open(e.action_url)
except SubscriptionRequiredError as e:
if e.action_url:
webbrowser.open(e.action_url)
except RateLimitError:
pass # back off and retry
except QueueFullError:
pass # wait for in-flight tasks to drainMid-stream errors on the SSE stream raise the same typed exceptions, so a
for msg in client.browser_agent(..., stream=True): loop wrapped in
try/except is enough — no special event handling required.
| Exception | HTTP | code |
|---|---|---|
SubscriptionRequiredError |
402 | SUBSCRIPTION_REQUIRED |
InsufficientCreditsError |
402 | INSUFFICIENT_CREDITS |
RateLimitError |
429 | RATE_LIMITED |
QueueFullError |
429 | QUEUE_FULL |
BatchTooLargeError |
400 | BATCH_TOO_LARGE |
AuthenticationError |
401 | — |
PredevAPIError |
other | — |