Skip to main content

Official Python SDK for the in.bio URL shortener API: short links, QR codes, analytics, webhooks.

Project description

inbio — official Python SDK for INBIO (in.bio)

The official Python SDK for INBIO, the premium URL shortener with click analytics and customizable QR codes. Shorten links, generate styled QR codes, read analytics, and manage folders and tags from Python.

  • Free to startinbio.shorten() and the QR API need no account and no API key
  • Zero dependencies — pure standard library, full type hints, Python ≥ 3.9
  • Complete — covers the entire INBIO REST API: links CRUD, generator pagination, bulk create, QR codes, analytics, webhook signature verification

Install

pip install inbio

Shorten a URL — no account, no API key

import inbio

print(inbio.shorten("https://example.com/very/long/url").short_url)
# https://in.bio/x7Kp2q

Keyless links are anonymous, rate-limited (5/min per IP), and deleted after 30 days unless claimed — the result's claim_url lets you keep one.

Authenticated quickstart

Create a token under Settings → API tokens (API access requires a Pro or Business plan), then:

from inbio import Inbio

client = Inbio("YOUR_TOKEN")

link = client.links.create("https://example.com/sale", slug="spring-sale")
print(link.short_url)      # https://in.bio/spring-sale
print(link.total_clicks)   # 0

List and iterate links

page = client.links.list(status="active", per_page=50)
print(page.total, page.current_page, page.has_next)
for link in page:
    print(link.slug, link.destination_url)

# Auto-pagination: a generator that walks every page for you
for link in client.links.iterate(tag="marketing"):
    print(link.short_url)

Create with options

link = client.links.create(
    "https://example.com/launch",
    slug="launch",
    title="Product launch",
    redirect_type=301,
    folder_id=3,
    tags=["marketing", "q3"],
    expires_at="2026-12-31T23:59:59+00:00",  # Pro+
    fallback_url="https://example.com",       # shown after expiry, Pro+
    click_limit=10_000,                       # Pro+
    password="s3cret",                        # Pro+
    utm={"source": "newsletter", "medium": "email", "campaign": "launch"},
)

Update, enable, disable, delete

link = client.links.update(42, title="New title", tags=["archive"])
link = client.links.disable(42)   # active -> disabled
link = client.links.enable(42)    # disabled -> active
client.links.delete(42)           # soft-delete, stops redirecting

Enable/disable only toggle between active and disabled; any other status raises StateConflictError with the link's current status.

Bulk create (Business)

result = client.links.bulk_create([
    {"destination_url": "https://example.com/a", "slug": "a1"},
    {"destination_url": "https://example.com/b", "slug": "a1"},  # duplicate slug
])
for link in result.created:
    print("created", link.slug)
for failure in result.failed:
    print("row", failure.index, "failed:", failure.error)

QR code to a file

png = client.links.qr(42, format="png", size=512)
with open("spring-sale.png", "wb") as fh:
    fh.write(png)

svg = client.links.qr(42, format="svg")

The QR encodes the short URL, so editing the destination never invalidates printed codes.

Analytics

stats = client.links.analytics(42, from_="2026-06-01", to="2026-06-30")
print(stats.totals.clicks, stats.totals.uniques, stats.totals.bot_clicks)
for point in stats.series:
    print(point["date"], point["clicks"])
for country in stats.countries:   # top 10 by clicks
    print(country["value"], country["clicks"])
# also: stats.devices, stats.browsers, stats.referrers, stats.range.from_/.to

Folders and tags

for folder in client.folders.list():
    print(folder.id, folder.name, folder.links_count)

for tag in client.tags.list():
    print(tag.name, tag.links_count)

Account usage

usage = client.account.usage()
print(usage.plan)                          # "pro"
print(usage.usage["links_created"])        # 120
print(usage.limits["links_per_month"])     # 2000

Webhook verification

Verify the X-Inbio-Signature header against the exact raw request body (configure endpoints under Settings → Webhooks; Business plan). Example Flask handler:

from flask import Flask, request, abort
from inbio import Inbio, SignatureVerificationError

app = Flask(__name__)
client = Inbio()

@app.post("/webhooks/inbio")
def inbio_webhook():
    try:
        event = client.webhooks.verify(
            request.get_data(),                       # raw bytes, not parsed JSON
            request.headers.get("X-Inbio-Signature"),
            "whsec_YOUR_SIGNING_SECRET",
            tolerance=300,                            # reject stale timestamps
        )
    except SignatureVerificationError:
        abort(400)

    if event["event"] == "link.clicked":
        print("click on", event["data"]["slug"])
    return "", 204

verify recomputes HMAC-SHA256(secret, "<t>.<raw body>"), compares in constant time, and rejects timestamps older than tolerance seconds. webhooks.construct_event(...) is an alias.

Error handling

Every SDK error derives from inbio.InbioError (with .message, .status, .error_type, .body).

Exception HTTP Extra attributes
AuthenticationError 401
AccessError 403 error_type = plan / scope / account; required scope
NotFoundError 404
StateConflictError 409 current link status
ValidationError 422 errors — per-field messages
EntitlementError 422 (error.type=entitlement) plan feature/limit
RateLimitError 429 retry_after seconds
ServerError 5xx
SignatureVerificationError webhook verification failed
from inbio import Inbio, RateLimitError, ValidationError

try:
    client.links.create("not-a-url")
except ValidationError as err:
    print(err.errors)        # {"destination_url": ["The destination url ..."]}
except RateLimitError as err:
    print(err.retry_after)   # seconds to wait

Retries and timeouts

client = Inbio(
    "YOUR_TOKEN",
    base_url="https://in.bio",  # default
    timeout=30,                 # seconds per request, default 30
    max_retries=2,              # default 2
)

Retries apply only to idempotent GET requests (connection errors and 5xx) and to 429 responses that carry Retry-After, with exponential backoff capped at 10 seconds.

Authenticating via environment variable

# export INBIO_API_TOKEN=YOUR_TOKEN
from inbio import Inbio

client = Inbio()  # picks up INBIO_API_TOKEN

About INBIO

INBIO (in.bio) is a URL shortener and link-management platform: short links with custom slugs, real-time click analytics (countries, devices, browsers, referrers — bots filtered out), a QR code studio with dot styles, marker shapes and colors, folders, tags, UTM tools, and a REST API with webhooks. Free plan included.

License

MIT © InBio, Inc.

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

inbio-0.1.0.tar.gz (19.6 kB view details)

Uploaded Source

Built Distribution

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

inbio-0.1.0-py3-none-any.whl (16.7 kB view details)

Uploaded Python 3

File details

Details for the file inbio-0.1.0.tar.gz.

File metadata

  • Download URL: inbio-0.1.0.tar.gz
  • Upload date:
  • Size: 19.6 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.14.6

File hashes

Hashes for inbio-0.1.0.tar.gz
Algorithm Hash digest
SHA256 1d23313d6ed6c7eb9af1701c1fa963aaabc97854ee63c90e9c08d174bee9e679
MD5 c7b310eb1c3d8f281363a5c8b6f0c341
BLAKE2b-256 ddfe32ffcd94e2a9362dbdb92e9420ef5e4b23958c122c22c30f71d927e3ed67

See more details on using hashes here.

File details

Details for the file inbio-0.1.0-py3-none-any.whl.

File metadata

  • Download URL: inbio-0.1.0-py3-none-any.whl
  • Upload date:
  • Size: 16.7 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.14.6

File hashes

Hashes for inbio-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 a6f30bb10f9bada79c3ce014ecdc500c8becf0e3a065cb4202e2902840ffab77
MD5 3ffb0ea3848c167e529bdc1a652da4fe
BLAKE2b-256 479c7e453119d709b2c58b9e0061016c7fab430469ca753784e0b4a4a5142c8b

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