Skip to main content

Lightweight Python SDK (unofficial) for the Loops API

Project description

Unofficial Loops.so Python Library (pyloops-so)

PyPI version CI Python versions

pyloops-so is a, unofficial lightweight Python SDK for the Loops API, designed for production usage with minimal dependencies.

[!IMPORTANT] Read the full API guide before integrating: API Guide

Why this library

  • Complete support for Loops.so endpoints
  • Typed request/response models via Pydantic
  • Optional raw JSON mode when you want plain dictionaries
  • Lightweight runtime dependencies (pydantic, requests)
  • Small, composable client structure (contacts, events, transactional, etc.)

Loops API docs

Official Loops API reference: https://loops.so/docs/api-reference

Endpoint docs covered by this SDK:

Installation

Install from PyPI:

uv add pyloops-so

For local development:

uv sync --extra dev

Examples

Authentication

Loops uses Bearer auth for all endpoints:

Authorization: Bearer {api_key}

Create a client once and reuse it:

from loops_py import LoopsClient

client = LoopsClient(api_key="loops_api_key")

If your network/provider applies strict bot filtering, set an explicit user agent:

client = LoopsClient(api_key="loops_api_key", user_agent="my-app/1.0")

Usage model

The SDK supports two call styles:

  1. Top-level convenience methods (client.create_contact(...)) for compatibility.
  2. Grouped service methods (client.contacts.create_contact(...)) for clearer organization.

Both call styles use the same underlying implementation.

Typed mode (default)

By default, responses are returned as Pydantic models.

from loops_py import ContactRequest, LoopsClient

client = LoopsClient(api_key="loops_api_key")

created = client.contacts.create_contact(
    ContactRequest(
        email="ada@example.com",
        first_name="Ada",
        user_id="usr_123",
        mailing_lists={"cll2pyfrx0000mm080fwnwdg0": True},
    )
)

print(created.success)
print(created.id)

JSON mode

If you prefer raw dict/list responses, use response_mode="json".

from loops_py import LoopsClient

client = LoopsClient(api_key="loops_api_key", response_mode="json")
raw = client.account.verify_api_key()
print(raw["teamName"])

You can override per call:

typed = client.account.verify_api_key(as_json=False)
raw = client.account.verify_api_key(as_json=True)

Error handling

HTTP errors from Loops raise LoopsAPIError with status code and parsed response payload.

from loops_py import LoopsAPIError

try:
    client.contacts.find_contact({"email": "missing@example.com"})
except LoopsAPIError as exc:
    print(exc.status_code)
    print(exc.response)

Rate limit handling and retries

Loops applies request rate limits (baseline 10 requests/second/team) and can return 429. This SDK retries 429 responses automatically with exponential backoff.

Default retry behavior:

  • max_retries=3 (up to 4 total attempts)
  • retry_backoff_base=0.25 seconds
  • retry_backoff_max=4.0 seconds
  • retry_jitter=0.1 (10% random jitter)
  • Retry-After header is honored when present

Configure it:

from loops_py import LoopsClient

client = LoopsClient(
    api_key="loops_api_key",
    max_retries=5,
    retry_backoff_base=0.2,
    retry_backoff_max=6.0,
    retry_jitter=0.2,
)

Disable retries by setting max_retries=0.

Endpoint mapping

  • contacts
    • create_contact -> POST /contacts/create
    • update_contact -> POST /contacts/update
    • find_contact -> GET /contacts/find
    • delete_contact -> POST /contacts/delete
    • create_contact_property -> POST /contacts/properties
    • list_contact_properties -> GET /contacts/properties
  • mailing_lists
    • list_mailing_lists -> GET /lists
  • events
    • send_event -> POST /events/send
  • transactional
    • send_transactional_email -> POST /transactional
    • list_transactional_emails -> GET /transactional
  • account
    • verify_api_key -> GET /api-key
    • list_dedicated_sending_ips -> GET /dedicated-sending-ips

Idempotency support

For endpoints that support idempotency, you can optionally pass idempotency_key:

client.events.send_event(
    {"email": "user@example.com", "eventName": "signup"},
    idempotency_key="signup-user@example.com-2026-02-28",
)

Build and publish

Build sdist + wheel:

uv build

Artifacts:

  • dist/*.tar.gz
  • dist/*.whl

Publish (requires PyPI token):

export UV_PUBLISH_TOKEN="pypi-..."
uv publish

TestPyPI:

uv publish --publish-url https://test.pypi.org/legacy/

Development

uv run ruff check .
uv run pytest

License

MIT

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

pyloops_so-0.1.4.tar.gz (49.3 kB view details)

Uploaded Source

Built Distribution

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

pyloops_so-0.1.4-py3-none-any.whl (14.9 kB view details)

Uploaded Python 3

File details

Details for the file pyloops_so-0.1.4.tar.gz.

File metadata

  • Download URL: pyloops_so-0.1.4.tar.gz
  • Upload date:
  • Size: 49.3 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for pyloops_so-0.1.4.tar.gz
Algorithm Hash digest
SHA256 cb871f3ec247b1ffbce06ce462a609f43553ee41986067e21c822fd42d543c9b
MD5 29657db3f98c1f76b6e9bc57ed5c60a9
BLAKE2b-256 23a421ab4c6bfda52282f8206053937727c6ac4c035f340b157854cc41654511

See more details on using hashes here.

Provenance

The following attestation bundles were made for pyloops_so-0.1.4.tar.gz:

Publisher: publish.yml on annjawn/loops-py

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

File details

Details for the file pyloops_so-0.1.4-py3-none-any.whl.

File metadata

  • Download URL: pyloops_so-0.1.4-py3-none-any.whl
  • Upload date:
  • Size: 14.9 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for pyloops_so-0.1.4-py3-none-any.whl
Algorithm Hash digest
SHA256 699f0f2221845546dc2fcae63b0ee43e4f4fe79f36f4a3e9b1cbb8b701b333bc
MD5 ce99e7ae1e7bf76fa96569a69ff3242a
BLAKE2b-256 c7e37a2b5901515eebceeace020d5553f77fc116913661a516d01982808f912d

See more details on using hashes here.

Provenance

The following attestation bundles were made for pyloops_so-0.1.4-py3-none-any.whl:

Publisher: publish.yml on annjawn/loops-py

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