Skip to main content

awsysco — Official Python SDK

PyPI version Python versions License: MIT

What

The official Python SDK for the AWSYS.CO URL Shortener API — a typed, sync-and-async client covering every capability the platform exposes to an API key: links, analytics, QR codes, folders, bulk create, account/profile/usage, tags, trust scoring, data export, branded namespaces, UTM templates, webhooks, saved views, custom domains, affiliate programs, AgentLink, Web2App, and provider imports. Every method is documented and Pydantic-typed; every error maps to a specific, catchable exception.

Install

pip install awsysco

Requires Python 3.9+. See Supported Versions below.

Authentication

Generate an API key from your AWSYS dashboard. All API keys begin with awsys_.

from awsysco import Client

client = Client(api_key="awsys_...")

Or set AWSYS_API_KEY in your environment and omit api_key entirely:

export AWSYS_API_KEY=awsys_...
client = Client()  # picks up AWSYS_API_KEY automatically

Passing neither raises AwsysConfigurationError before any network call is made.

Quick Start

from awsysco import Client

client = Client(api_key="awsys_your_key_here")

# Shorten a URL
link = client.links.create("https://example.com/very/long/path")
print(link.short_url)  # https://awsys.co/abc123

# Get analytics
stats = client.analytics.get_stats(link.short_code)
print(f"Total clicks: {stats.total_clicks}")

# Build a QR code URL
qr_url = client.qr.get_url(link.short_code, size=400)
print(qr_url)

Core Resources

Every resource below is available on client.<resource> (sync Client) and identically on await client.<resource>.<method>(...) (AsyncClient).

link = client.links.create("https://example.com", custom_slug="my-link", max_clicks=1000)
page = client.links.list(limit=20, offset=0)
one = client.links.get("my-link")
updated = client.links.update("my-link", max_clicks=500)
client.links.delete("my-link")

# Auto-paginate every link (see Pagination below)
for link in client.links.list_all():
    print(link.short_code)

Analytics — client.analytics

stats = client.analytics.get_stats("abc123")
print(stats.total_clicks)

agg = client.analytics.get_aggregate_stats("abc123", period="30d")
print(agg.total_clicks, agg.unique_visitors, agg.country_breakdown)
if agg.upgrade_for_more:   # present on free tier; richer breakdowns need Pro+
    print(agg.upgrade_for_more.message)

# Requires the account's "Live Globe" feature flag; 403 FEATURE_DISABLED otherwise.
recent = client.analytics.get_recent_clicks(limit=10)

QR Codes — client.qr

# Client-side URL builder — no HTTP request is made.
url = client.qr.get_url("abc123", size=400, color="FF5733", bg_color="FFFFFF")

settings = client.qr.get_settings("abc123")
client.qr.update_settings("abc123", {"color": "#ff0000"})

Folders — client.folders

folder = client.folders.create("Q1 Campaign", color="#FF5733")
client.folders.assign_link("abc123", folder.id)
client.folders.update(folder.id, name="Q1 Campaign (final)")
client.folders.remove_link("abc123")
client.folders.delete(folder.id)

Bulk Create — client.bulk

result = client.bulk.create([
    {"url": "https://example.com/page-1"},
    {"url": "https://example.com/page-2", "custom_slug": "page-two"},
])
print(result.created, result.failed)

Me / Profile / Usage

Three distinct, non-overlapping views of the account:

me = client.me.get()              # subscription tier, features, limits
profile = client.profile.get()    # account profile: email, display name
usage = client.usage.get()        # live consumption counters + overage state

client.profile.update(display_name="New Name")

Tags — client.tags

client.tags.add("abc123", "promo")
client.tags.remove("abc123", "promo")

Trust Score — client.trust_score

result = client.trust_score.scan("abc123")
print(result.score, result.status, result.threats)

Data Export — client.data_export

csv_text = client.data_export.export_links()             # all links, as CSV
stats_csv = client.data_export.export_link_stats("abc123")

Namespace — client.namespace

info = client.namespace.get()
available = client.namespace.check("acme")
client.namespace.claim("acme")
client.namespace.release()

UTM Templates — client.utm_templates

client.utm_templates.create("Launch", "newsletter", "email", "sept")
for t in client.utm_templates.list():
    print(t.name)
client.utm_templates.delete(t.id)

Webhooks — client.webhooks

webhook = client.webhooks.create("https://you.example/hook", ["link.created", "link.click"])
client.webhooks.update(webhook.id, enabled=False)
client.webhooks.test(webhook.id, "link.created")
client.webhooks.delete(webhook.id)

Saved Views — client.saved_views

view = client.saved_views.create("My View", {"tag": "promo"})
client.saved_views.update(view.id, name="Renamed View")
client.saved_views.delete(view.id)

Custom Domains — client.custom_domains

client.custom_domains.add("go.example.com")
client.custom_domains.verify("go.example.com")
client.custom_domains.update("go.example.com", default_redirect="https://example.com/")
# .activate() is deprecated — Firebase-only, unreachable with an API key. Use the dashboard.

Affiliate — client.affiliate

program = client.affiliate.create_program("Partner Program", "cpa_return", cpa_rate=15)
client.affiliate.discover(limit=20)
client.affiliate.join(program.id, partner_code="LAUNCH")
stats = client.affiliate.get_program_stats(program.id, period="30d")
client.agentlink.subscribe("dev@example.com")          # public, no auth required
stats = client.agentlink.get_account_stats(period_days=7)

Web2App — client.web2app

Sessions are single-use (consumed on read) with a 24-hour TTL.

session = client.web2app.consume_session("0123456789abcdef0123456789abcdef")
print(session.link_id, session.utm_params, session.country)

Imports — client.imports

job = client.imports.start(provider="bitly", access_token="<bitly-token>")
done = client.imports.wait_for_completion(job.id, poll_interval=5.0, timeout=300.0)
print(done.status, done.counts.written, done.counts.errored)

csv_map = client.imports.get_redirect_map_csv(job.id)
json_map = client.imports.get_redirect_map_json(job.id)

Pagination

Only links.list() is paginated (offset/limit; the platform ignores offsets on every other list endpoint, so the SDK doesn't fake them). limit is clamped to the platform max of 100 client-side.

page1 = client.links.list(limit=20, offset=0)
page2 = client.links.list(limit=20, offset=20)
print(page1.has_more)   # None if the platform didn't send pagination info

# Or auto-paginate everything:
for link in client.links.list_all(limit=100):
    ...

# Async equivalent:
async for link in async_client.links.list_all():
    ...

The iterator stops when the platform reports has_more=False, or a page comes back shorter than the requested limit (including empty) — this guards against a response that omits has_more entirely.

Errors

All errors inherit from AwsysError.

from awsysco import (
    Client,
    AwsysError,
    AwsysConfigurationError,
    AwsysAuthError,
    AwsysForbiddenError,
    AwsysNotFoundError,
    AwsysConflictError,
    AwsysValidationError,
    AwsysRateLimitError,
    AwsysServerError,
    AwsysNetworkError,
    AwsysTimeoutError,
)

try:
    link = client.links.get("nonexistent")
except AwsysNotFoundError:
    print("Link not found")
except AwsysRateLimitError as e:
    print(f"Rate limited (code={e.code}). Retry after {e.retry_after}s, resets {e.resets_at}")
except AwsysServerError as e:
    print(f"Platform error {e.status}: {e.message}")
except AwsysTimeoutError:
    print("Request timed out")
except AwsysError as e:
    print(f"API error {e.status}: {e.message}")
Exception HTTP status When raised
AwsysConfigurationError – Missing API key or invalid base_url, before any network call
AwsysValidationError 400 Invalid request parameters
AwsysAuthError 401 Missing or invalid API key
AwsysForbiddenError 403 Insufficient permissions / tier / feature flag
AwsysNotFoundError 404 Resource does not exist
AwsysConflictError 409 Custom slug already taken
AwsysRateLimitError 429 Too many requests (.code, .retry_after, .resets_at)
AwsysServerError 5xx Platform-side error
AwsysNetworkError – Connection failure (no HTTP response at all)
AwsysTimeoutError – Request exceeded its timeout (subclass of AwsysNetworkError)
AwsysError any Base class; catches everything above

Every exception exposes .message, .code, .status, and .raw (the parsed — or raw text — response body). The platform emits several error-body shapes ({error:true,code,message}, {error:"<string>",code}, {success:false,...}, and occasionally non-JSON bodies); the SDK tolerates all of them.

Configuration

client = Client(
    api_key="awsys_...",                    # or omit to read AWSYS_API_KEY
    base_url="https://staging.awsys.co",    # or omit to read AWSYS_BASE_URL, default https://awsys.co
    timeout=30.0,                           # per-request default, in seconds
    max_retries=3,                          # retry attempts for 429s / retryable 5xx / transport errors
)

base_url must start with http:// or https:// (anything else raises AwsysConfigurationError); a plain http:// URL is accepted but emits a warning. The API key and base URL are never included in repr(client), log output, or any exception — repr() shows a redacted awsys_...last4 form.

Advanced

Async: every resource and method is mirrored on AsyncClient.

import asyncio
from awsysco import AsyncClient

async def main():
    async with AsyncClient(api_key="awsys_...") as client:
        link = await client.links.create("https://example.com")
        print(link.short_url)

asyncio.run(main())

Retries: 429 is retried for every HTTP method (except quota-exhaustion 429s — HOURLY_LIMIT_EXCEEDED/DAILY_LIMIT_EXCEEDED/MONTHLY_LIMIT_EXCEEDED — which raise immediately, since waiting a few seconds can't help). 502/503/504 and transport-level failures (connection reset/refused, DNS) are retried only for idempotent methods (GET/PUT/DELETE) — the platform has no idempotency keys, so a retried POST/PATCH could create duplicates. Backoff uses the Retry-After header when present (seconds or an HTTP-date), otherwise 1s × 2^attempt capped at 30s, with full jitter. Set max_retries=0 to disable retries entirely.

Timeouts: 30s default per attempt, configurable at the client level (Client(timeout=...)) or per call via each resource method's underlying request.

Context managers: with Client(...) as client: / async with AsyncClient(...) as client: close the underlying HTTP connection pool automatically.

Models

Every response is parsed into a Pydantic v2 model. All fields are Optional (the platform's response shapes vary by tier/feature flags), unknown fields from the platform are preserved rather than rejected, and timestamp fields accept both plain ISO-8601 strings and Firestore's {_seconds,_nanoseconds} shape (normalized to an ISO-8601 string — models expose timestamps as str, not a native datetime, in the 1.x series; native datetime support is planned for 2.0).

Supported Versions

Python 3.9, 3.10, 3.11, 3.12, and 3.13 are tested in CI on every push. Runtime dependencies: httpx>=0.27,<1, pydantic>=2,<3.

Development Setup

git clone https://github.com/AlphaWaveSystems/awsysco-python-sdk.git
cd awsysco-python-sdk

pip install -e ".[dev]"

# Configure test credentials (staging recommended)
cp .env.example .env.test
# Edit .env.test — add your AWSYS_API_KEY

# Unit + contract tests — no network required
pytest -m "not integration"

# Full suite, including live staging integration tests
pytest

# Lint and type-check
ruff check .
mypy awsysco

# Coverage
pytest --cov=awsysco --cov-report=term-missing

Contributing

  1. Fork the repository
  2. Create a feature branch (git checkout -b feat/my-feature)
  3. Make your changes and add tests (see tests/test_contract.py if you're touching request/response shapes — it's driven by tests/contracts/sdk-contract.json)
  4. Run pytest -m "not integration", ruff check ., and mypy awsysco — all must pass
  5. Open a pull request

Security

See SECURITY.md for the secret-hygiene policy (never commit API keys) and SECURITY-REVIEW.md for the SDK's own security review (dependency audit, redaction guarantees, TLS/base-URL validation). Report vulnerabilities to security@awsys.co — do not open a public issue.

License

MIT License — see LICENSE for details.

Metadata

Release files for awsysco 1.4.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for awsysco 1.4.0
File Size Uploaded
awsysco-1.4.0.tar.gz 121.7 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for awsysco 1.4.0
File Interpreter ABI Platform
awsysco-1.4.0-py3-none-any.whl Python 3 none any Details

Total release size: 182.7 kB

Release files / awsysco-1.4.0.tar.gz

Download URL awsysco-1.4.0.tar.gz
Size 121.7 kB
Tags Source
SHA-256 checksum
How to use checksums
87a84423c833f5f3b56bb977cfe7ca938f185ab9e63d8bfae115fbd4f4829de3
BLAKE2b-256 checksum
How to use checksums
c4daf02dc4e1826857f72792152a17a358cbf87d51f6a47816bcb880e891c850
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 10, 2026.

Transparency log

Release files / awsysco-1.4.0-py3-none-any.whl

Download URL awsysco-1.4.0-py3-none-any.whl
Size 61.0 kB
Tags Python 3
SHA-256 checksum
How to use checksums
3871bb105c015cf09d7611ef5049a928ff8bb7bfdf2e7cbf1b120f646816092b
BLAKE2b-256 checksum
How to use checksums
1838bcd169e88cc574698f344a5b924425dad615a895e4888a2d904556131a9a
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 10, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

1.4.0 This release

2 release files

1.3.0

2 release files

1.1.0

2 release files

1.0.0

2 release files

0.1.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page