Skip to main content

Turn web pages into structured data and change events — extract, search, crawl, monitor, and get webhook notifications when pages change.

Project description

FlyPython SDK

The Python-first web runtime for agents.

Search, extract, crawl, monitor, and interact with any website. Structured data ready for LLMs.

Installation

pip install flypython

CLI

export FLYPYTHON_API_KEY=fp_your_key_here

flypython monitor https://example.com/pricing \
  --schema '{"price":"number"}' \
  --schedule '0 */6 * * *'

flypython extract https://example.com --schema '{"title":"string"}'
flypython search "competitor pricing changes" --limit 5
flypython tasks --status active

For async support:

pip install flypython[async]

Quick Start

import flypython

# Set your API key
export FLYPYTHON_API_KEY=fp_your_key_here

# Extract structured data from any URL
result = flypython.extract(
    url="https://example.com/product",
    schema={"title": "string", "price": "number", "rating": "number"}
)
print(result.data)
# { "title": "...", "price": 29.99, "rating": 4.5 }

# Search the web
results = flypython.search("best mechanical keyboards 2025")
for r in results:
    print(r.title, r.url, r.snippet)

# Crawl multiple pages
result = flypython.crawl(
    url="https://example.com",
    max_pages=10,
    schema={"title": "string", "description": "string"}
)

# Capture a screenshot
screenshot = flypython.screenshot("https://example.com")
print(screenshot.screenshot.image_url)

# Monitor a page for changes
task = flypython.monitor(
    url="https://example.com/price",
    schema={"price": "number"},
    webhook="https://myapp.com/webhooks/price",
    schedule="0 */6 * * *"  # Every 6 hours
)
print(task.id)

Client Usage

Sync Client

from flypython import FlyPythonClient

client = FlyPythonClient(api_key="fp_your_key_here")

# Extract
result = client.extract(url="https://example.com", schema={"title": "string"})

# Search
results = client.search("python tutorials")

# Crawl
result = client.crawl(url="https://example.com", max_pages=5)

# Screenshot
screenshot = client.screenshot("https://example.com")

# Browser actions + extraction in one session
result = client.interact(
    url="https://example.com/product",
    actions=[{"type": "wait", "ms": 1000}],
    extract={"price": ".price"}
)

# AI-assisted selector suggestion
suggestion = client.suggest_template(
    "Watch this product page for price and stock changes",
    url="https://example.com/product"
)

# Status history
history = client.get_status_history(task_id="task-uuid", limit=30)

# API keys
keys = client.list_api_keys()
new_key = client.create_api_key(
    name="Production Key",
    scopes=["tasks:read", "tasks:write", "extract:run"],
    usage_limit=10000,
    ip_whitelist="203.0.113.0/24"
)
client.revoke_api_key("key-uuid")

Async Client

import asyncio
from flypython import AsyncFlyPythonClient

async def main():
    async with AsyncFlyPythonClient(api_key="fp_your_key_here") as client:
        result = await client.extract(url="https://example.com")
        print(result.data)

        tasks = await client.list_tasks()
        for t in tasks:
            print(t.name, t.status)

        task = await client.monitor(
            url="https://competitor.com/pricing",
            schema={"price": "number"},
            notify={"slack": "https://hooks.slack.com/services/..."},
        )
        print(task.id)

asyncio.run(main())

Media Download

Download images and videos during extraction:

# Get image URL only (default)
result = flypython.extract(
    url="https://example.com/product",
    schema={"image": "image_url"}
)
print(result.data["image"])  # "https://example.com/product.jpg"

# Download image as base64
result = flypython.extract(
    url="https://example.com/product",
    schema={"image": "image_url"},
    download_images=True
)
print(result.data["image"]["data"])  # base64 string
print(result.data["image"]["mime_type"])  # "image/jpeg"

# Save media files to disk
result = flypython.extract_with_media(
    url="https://example.com/product",
    schema={"image": "image_url"},
    download_images=True,
    save_media=True,
    output_dir="./downloads"
)
# Saved 1 media files to ./downloads

Task Management

Full CRUD for monitor tasks:

# Create a task
task = flypython.create_task(
    name="Competitor Price Monitor",
    target_url="https://competitor.com/product",
    mode="monitor",
    selectors={"price": ".price", "title": "h1"},
    schedule_cron="0 */6 * * *",
    destinations=[{"type": "webhook", "config": {"url": "https://hooks.example.com/flypython"}}],
)

# List all tasks
tasks = flypython.list_tasks(status="active")

# Get a specific task
task = flypython.get_task(task.id)

# Update task
flypython.update_task(task.id, status="paused")

# Run immediately
run = flypython.run_task(task.id)

# Inspect or export a run
run_detail = flypython.get_run(run["run_id"])
rows = flypython.get_run_data(run["run_id"])
csv_text = flypython.export_run(run["run_id"], format="csv")

# Delete task
flypython.delete_task(task.id)

Change Detection (Diffs)

# List all detected changes
diffs = flypython.list_diffs(change_type="price_drop", limit=10)
for d in diffs.diffs:
    print(d.summary, d.confidence)

# Get specific diff detail
diff = flypython.get_diff("diff-uuid")
print(diff.field_changes)

# Compare two snapshots locally (no API call)
diff = flypython.compare_snapshots(before={"price": "100"}, after={"price": "90"})
print(diff.change_type)  # "price_change"

Snapshots

# List snapshots for a task
snapshots = flypython.list_snapshots(task_id="task-uuid", limit=5)

# Get the most recent snapshot
latest = flypython.get_latest_snapshot(task_id="task-uuid")
print(latest.extracted_json)

Billing

# Check balance
balance = flypython.get_balance()
print(balance.balance_display)  # "$5.00"
print(balance.free_calls_remaining)  # 95

# Check usage
usage = flypython.get_usage()
print(usage.estimated_cost_display)

# List transactions
transactions = flypython.list_transactions(limit=10)

# Get pricing
options = flypython.get_top_up_options()
for p in options.pricing:
    print(p.operation, p.display)

Alerts

# List alerts
alerts = flypython.list_alerts(limit=20)
for a in alerts:
    print(a.task_name, a.alert_type, a.severity)

# Acknowledge an alert
flypython.acknowledge_alert("alert-uuid")

Templates

# List official templates
templates = flypython.list_templates()

# Include community templates
templates = flypython.list_templates(include_community=True)

# Get a specific template
template = flypython.get_template("amazon-product")
print(template.selectors)

# Create a community template
template = flypython.create_template(
    id="generic-price",
    name="Generic Price",
    selectors={"price": ".price", "title": "h1"},
    sample_url="https://example.com/product"
)

# Admin-only approval, and owner/admin delete
flypython.approve_template("generic-price")
flypython.delete_template("generic-price")

Browser Interaction and Selector Suggestions

# Browser Rendering supports wait, click, type, scroll, select, and extraction.
result = flypython.interact(
    url="https://example.com/product",
    actions=[{"type": "wait", "ms": 1000}],
    extract={"price": ".price", "stock": ".stock"}
)

suggestion = flypython.suggest_template(
    "Monitor this product page for price and stock",
    url="https://example.com/product"
)
print(suggestion["suggested_selectors"])

API Keys, Status, and Account Data

keys = flypython.list_api_keys()
new_key = flypython.create_api_key(
    name="CI",
    scopes=["tasks:read", "tasks:write", "extract:run"],
    usage_limit=10000,
)
flypython.revoke_api_key(new_key["id"])

history = flypython.get_status_history(task_id="task-uuid", limit=30)
print(history.days)

exported = flypython.export_account_data()

# Guarded because this disables the user and active API keys.
flypython.delete_account(confirm=True)

Webhook Verification

FlyPython signs webhooks with an X-FlyPython-Signature: t=<timestamp>,v1=<hex> header. The digest is HMAC-SHA256(secret, "<timestamp>.<raw_body>") over the exact raw request bytes. Verify it with:

from flypython import verify_webhook

signature = request.headers.get("X-FlyPython-Signature", "")
is_valid = verify_webhook(
    payload=request.body,          # raw bytes, exactly as received
    signature=signature,           # "t=1700000000,v1=abc123..."
    secret="whsec_your_webhook_secret",
    tolerance=300,                 # max signature age in seconds; 0 skips the check
)

Both FlyPythonClient.verify_webhook and AsyncFlyPythonClient.verify_webhook share this signature. Pass tolerance=0 to disable the replay/freshness check.

Error Handling

API errors raise subclasses of flypython.FlyPythonError. Each error carries status_code and detail (the server's {"detail": "..."} message), and the sync and async clients raise the same classes:

Exception HTTP status Notes
AuthenticationError 401 Bad credentials, or API-key auth on a dashboard-only endpoint
InsufficientBalanceError 402 Prepaid balance too low for the call
RateLimitError 429 retry_after holds the Retry-After seconds (or None)
ValidationError 400 Request failed server-side validation
NotFoundError 404 Resource does not exist
ServerError 5xx Server-side failure
FlyPythonError any other Base class — catch it to handle every API error
import flypython
from flypython import InsufficientBalanceError, RateLimitError

try:
    result = flypython.extract(url="https://example.com")
except InsufficientBalanceError as e:
    print("Please top up:", e.detail)
except RateLimitError as e:
    print("Rate limited, retry in", e.retry_after, "seconds")

Retries

Both clients retry 429 and 5xx responses, up to max_retries extra attempts (default 2, i.e. 3 attempts total; 0 disables retries). On 429 the server's Retry-After header is honored (capped at 30 seconds); otherwise exponential backoff (0.5 * 2**n seconds plus jitter) is used.

from flypython import FlyPythonClient, AsyncFlyPythonClient

client = FlyPythonClient(api_key="fp_...", max_retries=4)
async_client = AsyncFlyPythonClient(api_key="fp_...", max_retries=0)

top_up and convert_credits are never retried, so a payment-style operation is never submitted twice.

Dashboard-JWT-Only Methods

The following methods call endpoints that only accept a dashboard session JWT. The backend rejects API-key auth on them with 401 (AuthenticationError) — this is by design. Use them with a session token from the dashboard, not an fp_... API key:

  • Workspaces: list_workspaces, list_workspace_members, invite_member, remove_member
  • API keys: list_api_keys, create_api_key, revoke_api_key
  • Account / GDPR: export_account_data, delete_account
  • Billing: top_up, convert_credits

API Reference

Core Functions

Function Description
extract(url, schema, ...) Extract structured data from a URL
extract_with_media(url, ...) Extract and save media files
search(query, limit, format) Search the web
crawl(url, max_pages, ...) Crawl multiple pages
screenshot(url, ...) Capture webpage screenshot
interact(url, actions, extract, wait_for, timeout) Load a rendered page and extract fields
suggest_template(description, url) Suggest monitor selectors
health() / ready() Check API health/readiness
monitor(url, schema, template, notify, webhook, slack_webhook, email, webhook_secret, schedule, name) Create a monitoring task (shorthand)

Task Management

Function Description
create_task(name, target_url, ...) Create a monitor task
list_tasks(status, limit) List tasks
get_task(task_id) Get task by ID
update_task(task_id, **fields) Update task
delete_task(task_id) Delete task
run_task(task_id) Trigger execution
reset_task(task_id) Reset paused task
list_task_runs(task_id, limit) List execution history
get_run(run_id) Get run detail
get_run_data(run_id) Get extracted run rows
export_run(run_id, format) Export run data as JSON or CSV text

Diffs & Snapshots

Function Description
list_diffs(task_id, change_type, ...) List detected changes
get_diff(diff_id) Get diff details
compare_snapshots(before, after) Local snapshot comparison
list_snapshots(task_id, limit) List snapshots
get_latest_snapshot(task_id) Get most recent snapshot

Billing & Alerts

Function Description
get_balance() Get prepaid balance
get_usage() Get monthly usage
list_transactions(limit) List transactions
get_subscription() Get plan status
get_top_up_options() Get pricing info
convert_credits(credits) Convert credits to balance
list_alerts(task_id, limit) List alerts
acknowledge_alert(alert_id) Acknowledge alert

API Keys & Status History

Function Description
list_api_keys() List API keys
create_api_key(name, scopes, usage_limit, ip_whitelist) Create API key
revoke_api_key(key_id) Revoke API key
get_status_history(task_id, url, from_time, to_time, limit) Daily aggregated run history
export_account_data() Export current user's account data
delete_account(confirm=True) Schedule account deletion

Templates & Delivery

Function Description
list_templates(category, include_community) List templates
get_template(template_id) Get template
create_template(id, name, selectors, ...) Create community template
approve_template(template_id) Approve community template (admin)
delete_template(template_id) Delete community template
list_delivery_logs(task_id, ...) List delivery logs
retry_delivery(log_id) Retry failed delivery

Utilities

Function Description
verify_webhook(payload, signature, secret) Verify HMAC signature

REST API

The SDK uses the FlyPython REST API under the hood. You can also call the API directly:

curl -X POST https://api.flypython.com/v1/extract \
  -H "Authorization: Bearer fp_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://example.com",
    "schema": {"title": "string"}
  }'

See API Documentation for more details.

Requirements

  • Python 3.9+
  • requests>=2.28.0
  • pydantic>=2.0.0
  • httpx>=0.25.0 (for async client)

Testing

cd sdk
python3 -m venv .venv
source .venv/bin/activate
pip install -e '.[dev]'
pytest

License

MIT

Links

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

flypython-0.3.1.tar.gz (42.8 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

flypython-0.3.1-py3-none-any.whl (35.2 kB view details)

Uploaded Python 3

File details

Details for the file flypython-0.3.1.tar.gz.

File metadata

  • Download URL: flypython-0.3.1.tar.gz
  • Upload date:
  • Size: 42.8 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.9.12

File hashes

Hashes for flypython-0.3.1.tar.gz
Algorithm Hash digest
SHA256 7dfba8f43380553f12a48baeb2aaa21a815b1d6e84f7bb7ee4fc39f2fe80e144
MD5 5d50112168685c1561cf92eb2a26ae5c
BLAKE2b-256 b7a2a98d2ca60670b0919c7fa4d5a09ba24c96447df87fa44fe1dc3b7ea68aaa

See more details on using hashes here.

File details

Details for the file flypython-0.3.1-py3-none-any.whl.

File metadata

  • Download URL: flypython-0.3.1-py3-none-any.whl
  • Upload date:
  • Size: 35.2 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.9.12

File hashes

Hashes for flypython-0.3.1-py3-none-any.whl
Algorithm Hash digest
SHA256 6ebe2a8fc2555d8f512869f88f88c4aca8dabcdc3263cf2e5bf6ea2e0e329981
MD5 6a8d82b0ccb9f631b73209b18b4511b5
BLAKE2b-256 59e34769a84fc5487b24c733e4f9bde9303a16a5963cf44e115bc419d024cca8

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page