Skip to main content

Unofficial async Python client for the Flowith.io web app

Project description

flowith

An unofficial async Python client for the Flowith.io web application.

Disclaimer: This library reverse-engineers the Flowith.io browser API. It is not affiliated with or endorsed by Flowith. Use responsibly.


Features

  • Text generation — single-turn and multi-turn, streaming and non-streaming
  • Image generation — Gemini, GPT Image, and more
  • Video generation — Seedance, Kling, etc.
  • File uploadPOST /file/store with multipart support
  • Conversation management — create, list, rename, delete
  • Flow canvas — read node graph for any conversation
  • Credits & models — fetch balance, browse model catalog
  • Templates — list and filter prompt templates
  • Profile & subscriptions — read/update profile, list grants
  • Supabase content — notices, projects, discussions, oracle stream
  • Daily rewards — claim and check status
  • Account auth — OAuth URL, auth user, refresh token
  • Real-time SSE — listen on /user_stream/stream for live generation events
  • Async-first — built on aiohttp, zero blocking calls

Installation

pip install flowith_webapi

Requires Python 3.10+.


Authentication

  1. Sign in at flowith.io with Google.
  2. After the OAuth redirect, copy the access_token from the URL hash:
    https://flowith.io/#access_token=eyJhbGci...&...
    
  3. Your user ID (sub claim in the JWT) is also needed for SSE streaming — it is parsed automatically from the JWT if you do not supply it explicitly.

To enable token rotation, also capture the refresh token from Supabase. Generate the OAuth URL with access_type=offline and prompt=consent so Google issues one on the first consent screen:

url = client.get_oauth_url(
    provider="google",
    redirect_to="https://flowith.io",
    access_type="offline",
    prompt="consent",
)
print(url)
import os
TOKEN   = os.environ["FLOWITH_TOKEN"]
USER_ID = os.environ["FLOWITH_USER_ID"]          # optional — parsed from JWT
REFRESH = os.environ.get("FLOWITH_REFRESH_TOKEN", "")

Keep these secret — they grant full access to your Flowith account.


How it works (SSE architecture)

The Flowith web app maintains two persistent SSE connections:

Endpoint Purpose
GET /user_stream/sse?user_id=… Keepalive — emits {"heartbeat": …} every ~5 s
GET /user_stream/stream?user_id=… Data channel — emits text_delta, result, image/video URLs

Both connections send the JWT as a bare token in the authorization header (no Bearer prefix) — this matches what the edge server expects.

When generate() is called the client:

  1. Creates the Supabase conversation and user/AI node rows.
  2. POSTs the completion request to /completion/async (returns immediately).
  3. Listens on /user_stream/stream for events whose nodeId matches the AI node.
  4. Falls back to polling flow_node on Supabase if the stream closes early.

Quick start

import asyncio
from flowith_webapi import FlowithClient

async def main():
    async with FlowithClient(TOKEN, user_id=USER_ID, refresh_token=REFRESH) as client:
        r = await client.generate("What is the capital of Poland?")
        print(r.text)   # "Warsaw"

asyncio.run(main())

Usage

Single-turn generation

r = await client.generate("Explain quantum entanglement in one sentence.")
print(r.text)
print(r.model)     # model ID used
print(r.conv_id)   # conversation UUID

Streaming

async for chunk in client.generate_stream("Write me a haiku about async Python."):
    print(chunk.text_delta, end="", flush=True)
print()

Multi-turn session

session = client.start_conversation(model="gemini-3.1-pro-preview")

r1 = await session.send("My name is Alice.")
r2 = await session.send("What is my name?")
print(r2.text)   # "Your name is Alice."

async for chunk in session.send_stream("Tell me a fun fact about Alice."):
    print(chunk.text_delta, end="", flush=True)
print()

Image generation

from flowith_webapi import ImageModel, AspectRatio

result = await client.generate_image(
    "A ripe banana on a marble surface, studio lighting",
    model=ImageModel.GEMINI_3_1_FLASH_IMAGE,
    aspect_ratio=AspectRatio.SQUARE,
)
await result.image.save("./outputs")
print(result.image.url)

Video generation

from flowith_webapi import VideoModel

video = await client.generate_video(
    "A cat walking on a sunny beach",
    model=VideoModel.SEEDANCE_2_FAST,
    timeout=300.0,
)
await video.save("./outputs")
print(video.video_url)

File upload

# Upload a local file and get a public CDN URL
record = await client.upload_file("my_game.html")
print(record.url)   # https://r2-bucket.flowith.net/f/…/my_game.html

# Reference the file in a generation
r = await client.generate(
    f"Review this HTML file: [[file:html|my_game.html|{record.url}]]"
)

# Rotate the access token (uses stored refresh token if not supplied)
if REFRESH:
    await client.refresh_access_token()

Conversations

# List recent conversations
convs = await client.list_conversations(limit=10)
for c in convs:
    print(c.conv_id, c.title)

# Read the flow-canvas node graph
nodes = await client.get_flow_nodes(conv_id)
for n in nodes:
    print(n.node_type, n.text[:80])

# Rename / delete
await client.rename_conversation(conv_id, "New Title")
await client.delete_conversation(conv_id)

Raw SSE streams

# Generation-data stream (/user_stream/stream) — heartbeats filtered out
async for evt in client.stream_user_stream(timeout=60):
    print(evt)

# Keepalive stream (/user_stream/sse) — heartbeats included
async for evt in client.stream_user_sse(timeout=60):
    print(evt)   # {"heartbeat": 1779127465201}

Credits & models

credits = await client.get_credits()
total = sum(c.remain_quota for c in credits)
print(f"Credits: {total:.2f}")

models = await client.list_models()
for m in models:
    print(m.model_id, m.title)

# Active subscriptions
subs = await client.list_subscription_user_own()
print(len(subs))

# Profile
profile = await client.get_user_profile()
print(profile.language)

API reference

FlowithClient

Method Description
generate(prompt, ...) Single-turn text generation (waits for full result)
generate_stream(prompt, ...) Streaming text generation (yields token-by-token)
generate_image(prompt, ...) Image generation
generate_video(prompt, ...) Video generation
generate_title(messages) Auto-generate conversation title
upload_file(file_path, ...) Upload file to CDN
start_conversation(...) Create ConversationSession for multi-turn use
list_conversations(...) List user conversations
get_conversation(conv_id) Fetch single conversation
rename_conversation(conv_id, title) Rename conversation
delete_conversation(conv_id) Soft-delete conversation
get_flow_nodes(conv_id) Read flow-canvas node graph
get_cooperators(conv_id) List conversation collaborators
get_credits() Fetch credit balances
get_credit_type() Return credit-type string
get_auth_user() Return Supabase auth user object
get_oauth_url(...) Build Supabase OAuth authorization URL
refresh_access_token(refresh_token=None) Rotate access token
logout(scope) Revoke auth session
get_user_profile(user_id) Fetch profile row
update_user_profile(**fields) Update profile fields
list_user_upload_records(...) List uploaded file records
list_subscription_user_own(...) List active subscription grants
list_public_notices() List public notices
list_flow_projects() List flow projects
get_conv_novel_editor(conv_id) Fetch novel-editor content
list_discuss(conv_id) List discussion entries
get_oracle_stream(conv_id) Fetch oracle stream entries
get_migration_info() Fetch migration version
get_featurebase_status() Featurebase changelog status
list_models(...) Browse model catalog
list_templates(...) Browse prompt templates
get_discord_invite() Return Discord invite URL
get_daily_rewards_status() Daily rewards status
claim_daily_reward() Claim today's reward
check_campaign(model_id) Check campaign eligibility
check_banana_campaign() Banana-2025 campaign status
get_enterprise_dashboard() Enterprise dashboard info
get_online_count() Current online user count
upsert_online_session(payload) Upsert online session (RPC)
remove_online_session(session_id) Remove online session (RPC)
stream_user_stream(...) Stream live generation events from /user_stream/stream
stream_user_sse(...) Stream keepalive heartbeat events from /user_stream/sse

ConversationSession

Method / Property Description
send(prompt, ...) Send message, await full reply
send_stream(prompt, ...) Send message, stream reply token-by-token
generate_image(prompt, ...) Generate image in this conversation
generate_video(prompt, ...) Generate video in this conversation
delete() Delete the server-side conversation
.conv_id Conversation UUID (created lazily on first send)
.model Session model ID
.turn Number of turns sent so far

Return types

Type Key fields
GenerationOutput text, text_delta, node_id, conv_id, model
ImageOutput images: list[GeneratedImage], image (first item)
GeneratedImage url, model; .save(dir) downloads to directory
VideoOutput video_url, model; .save(dir) downloads to directory
FileRecord url, file_type
ConversationRecord conv_id, title, preview
FlowNode node_id, node_type, text, model
UserCredits remain_quota, init_quota, sub_type
ModelInfo model_id, title, media, tier
TemplateRecord template_id, name, category, prompt
CooperatorRecord user_id, user_name, role
UserProfile user_id, language
UserUploadRecord record_id, name, file_type, created_at
SubscriptionUserOwn record_id, remain_quota, subscription_type
PublicNotice notice_id, title, content
FlowProject project_id, title, updated_at
ConvNovelEditor conv_id, content
DiscussRecord discuss_id, conv_id, content
OracleStreamRecord record_id, workflow, is_stop
MigrationInfo version

Error handling

from flowith_webapi import (
    AuthenticationError,
    RateLimitError,
    ValidationError,
    APIError,
    TimeoutError,
    FileUploadError,
)

try:
    r = await client.generate("Hello!")
except AuthenticationError:
    print("Invalid or expired token.")
except RateLimitError as e:
    print(f"Rate limited — retry in {e.retry_after_s}s")
except TimeoutError:
    print("Generation timed out.")
except APIError as e:
    print(f"API error {e.status_code}: {e}")

Logging

import flowith_webapi
flowith_webapi.set_log_level("DEBUG")   # DEBUG | INFO | WARNING | ERROR

Related projects

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

flowith_webapi-1.0.1.tar.gz (22.7 kB view details)

Uploaded Source

Built Distribution

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

flowith_webapi-1.0.1-py3-none-any.whl (24.6 kB view details)

Uploaded Python 3

File details

Details for the file flowith_webapi-1.0.1.tar.gz.

File metadata

  • Download URL: flowith_webapi-1.0.1.tar.gz
  • Upload date:
  • Size: 22.7 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for flowith_webapi-1.0.1.tar.gz
Algorithm Hash digest
SHA256 baabec70c11e303d39660ef55fe2a36d0670faccc6178c203274c2e24f72085e
MD5 abfa6859fb569871268ec2b255a77165
BLAKE2b-256 f4309e8108269c44eccd3b7a5ef3cd848041c2c1fc0bf8800a7bccf4ab6c91e7

See more details on using hashes here.

Provenance

The following attestation bundles were made for flowith_webapi-1.0.1.tar.gz:

Publisher: python-publish.yml on cyber-wojtek/Flowith-API

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file flowith_webapi-1.0.1-py3-none-any.whl.

File metadata

  • Download URL: flowith_webapi-1.0.1-py3-none-any.whl
  • Upload date:
  • Size: 24.6 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for flowith_webapi-1.0.1-py3-none-any.whl
Algorithm Hash digest
SHA256 94370daedf7fb198a01f1b245ed2d64ade0cb978c4e9e3605ecc83e7f6b04e30
MD5 4501c3f86a7ce9f346e21783a64d1704
BLAKE2b-256 6404b59ea37cd7db80ad27865150d96c808683f584574281432263c9150f411f

See more details on using hashes here.

Provenance

The following attestation bundles were made for flowith_webapi-1.0.1-py3-none-any.whl:

Publisher: python-publish.yml on cyber-wojtek/Flowith-API

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

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