owlstack
Official Python SDK for the OwlStack API. Schedule and publish to 31 social platforms from your code.
pip install owlstack
Requires Python 3.10 or newer. Built on httpx and pydantic v2, fully typed.
0.x release. The public API is still settling and minor versions may contain breaking changes. Pin an exact version if that matters to you.
Quick start
from owlstack import Owlstack
client = Owlstack(api_key="osk_...") # or set OWLSTACK_API_KEY
workspace = client.workspaces.list()[0]
accounts = client.accounts.list(workspace_id=workspace.id)
post = client.posts.create(
workspace_id=workspace.id,
content="Hello from Python 🦉",
accounts=[a.id for a in accounts],
)
client.posts.publish(post.id)
content takes a string for a single block, or a list for a thread:
client.posts.create(
workspace_id=workspace.id,
content=[
"First post in the thread",
{
"text": "Second one, with an image",
"media": [
{"type": "image", "name": "chart.png", "url": "https://.../chart.png"},
],
},
],
accounts=[a.id for a in accounts],
)
Scheduling
status is "draft" or "scheduled"; a scheduled post needs schedule_at. To send something out now, create it and call publish.
from datetime import datetime, timezone
client.posts.create(
workspace_id=workspace.id,
status="scheduled",
schedule_at=datetime(2026, 9, 1, 10, 0, tzinfo=timezone.utc),
content="Scheduled with the SDK",
accounts=[accounts[0].id],
)
Pagination
posts.list returns one page and iterates over its items directly. posts.iter walks every page for you:
page = client.posts.list(workspace_id=workspace.id, status="published", per_page=50)
print(f"{page.meta.total} published posts")
for post in page:
print(post.id, post.status)
# Every post, one page fetched at a time
for post in client.posts.iter(workspace_id=workspace.id):
print(post.id)
# Or page by page, when you want the meta
for page in client.posts.iter_pages(workspace_id=workspace.id):
print(page.meta.current_page, len(page))
AI features
balance = client.ai.credits()
print(f"{balance.credits_remaining} of {balance.credits_total} credits left")
result = client.ai.generate_caption(
current_caption="shipping a new feature",
platforms=["twitter", "linkedin"],
)
print(result.caption)
rewrite and repurpose_url are wired the same way:
rewrites = client.ai.rewrite(
content="Long article-style draft...",
platforms=["twitter", "linkedin", "bluesky"],
)
for variant in rewrites.results:
print(variant.platform, variant.content)
repurposed = client.ai.repurpose_url(
url="https://example.com/blog/post",
platforms=["twitter", "linkedin", "bluesky", "threads"],
)
API keys reach
creditsandgenerate_caption.rewriteandrepurpose_urlcurrently require a signed-in dashboard session and answer401to an API key. They are here so the surface is complete once the server opens them up.
Configuration
Owlstack(
api_key="osk_...", # or OWLSTACK_API_KEY
base_url="https://api.owlstack.app/api/v1", # override for a dev server
timeout=30.0, # seconds, or an httpx.Timeout
max_retries=3, # total attempts on a 429
http_client=my_httpx_client, # bring your own transport
)
| Env var | Used for |
|---|---|
OWLSTACK_API_KEY |
API key, when not passed to the constructor |
The client is a context manager, and closes its transport on exit:
with Owlstack() as client:
client.posts.list(workspace_id=workspace.id)
A 429 is retried automatically, waiting for the interval the API asks for in
Retry-After (delta-seconds or an HTTP date, capped at 60s). max_retries
counts total attempts, so the default of 3 means two retries.
Error handling
Every non-2xx response raises OwlstackError or one of its subclasses, carrying
the API's status, code, and message.
from owlstack import Owlstack, OwlstackError, PaymentRequiredError, RateLimitError
try:
client.posts.publish("9b2f6c1e-...")
except PaymentRequiredError as err:
print(f"Out of credits — upgrade at {err.upgrade_url}")
except RateLimitError as err:
print(f"Rate limited, retry in {err.retry_after}s")
except OwlstackError as err:
print(f"API {err.status} ({err.code}): {err.message}")
| Status | Exception |
|---|---|
| 401 | AuthenticationError |
| 402 | PaymentRequiredError |
| 403 | PermissionDeniedError |
| 404 | NotFoundError |
| 429 | RateLimitError |
| other | OwlstackError |
Resources
| Namespace | Methods |
|---|---|
posts |
list, iter, iter_pages, get, create, update, delete, publish, cancel, retry, preflight, deliveries |
accounts |
list, get, health |
workspaces |
list, get |
labels |
list |
ai |
credits, generate_caption, rewrite, repurpose_url |
For an endpoint the SDK does not wrap yet, client.request sends an
authenticated call and hands back the decoded body:
client.request("GET", "/analytics/summary", params={"workspace_id": workspace.id})
Example
examples/create_post.py creates a post against a
running API:
export OWLSTACK_API_KEY=osk_...
export OWLSTACK_BASE_URL=http://localhost:8080/api/v1
python examples/create_post.py "Hello from the Python SDK" --publish
Contributing
Issues and pull requests are welcome at owlstacks/owlstack-python.
uv sync --group dev
uv run pytest
uv run ruff check .
uv run mypy
License
MIT
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file owlstack-0.1.0.tar.gz.
File metadata
- Download URL: owlstack-0.1.0.tar.gz
- Upload date:
- Size: 19.9 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
43af8442982c936ded15ee90fff0e0b6139d1ac2584d9cc60529288ee13fab5b
|
|
| MD5 |
6a01bed99301a0af22fa53ae3ce60579
|
|
| BLAKE2b-256 |
e12da8415e171483e4c24d6541fc8f6781d19cf3033d3e907767dc286afc853e
|
Provenance
The following attestation bundles were made for owlstack-0.1.0.tar.gz:
Publisher:
release.yml on owlstacks/owlstack-python
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
owlstack-0.1.0.tar.gz -
Subject digest:
43af8442982c936ded15ee90fff0e0b6139d1ac2584d9cc60529288ee13fab5b - Sigstore transparency entry: 2450430261
- Sigstore integration time:
-
Permalink:
owlstacks/owlstack-python@7c734ca18503b011aef858b633096f9924889f17 -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/owlstacks
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@7c734ca18503b011aef858b633096f9924889f17 -
Trigger Event:
push
-
Statement type:
File details
Details for the file owlstack-0.1.0-py3-none-any.whl.
File metadata
- Download URL: owlstack-0.1.0-py3-none-any.whl
- Upload date:
- Size: 18.3 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
a2c6d88ded39f029808bee87da62bb11f5a7d86d304615e6f7f75749ab9b6075
|
|
| MD5 |
50d6322cd604b8f95915f77c1be989c1
|
|
| BLAKE2b-256 |
be28ce252c263eea628c1b21f2e453a66da2d86e8cb4819b520576fa8181a23f
|
Provenance
The following attestation bundles were made for owlstack-0.1.0-py3-none-any.whl:
Publisher:
release.yml on owlstacks/owlstack-python
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
owlstack-0.1.0-py3-none-any.whl -
Subject digest:
a2c6d88ded39f029808bee87da62bb11f5a7d86d304615e6f7f75749ab9b6075 - Sigstore transparency entry: 2450430853
- Sigstore integration time:
-
Permalink:
owlstacks/owlstack-python@7c734ca18503b011aef858b633096f9924889f17 -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/owlstacks
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@7c734ca18503b011aef858b633096f9924889f17 -
Trigger Event:
push
-
Statement type: