Skip to main content

Python SDK for the Rangler API.

Project description

ranglerpy

ranglerpy is a Python SDK for the Rangler API.

It is designed around the actual Rangler product shape:

  • polling-first event consumption
  • optional webhook verification helpers
  • thin wrappers for the developer control plane

The goal is to make Rangler easy to integrate whether you want to:

  • poll event feeds with a cursor
  • consume companies and filings directly
  • manage event destinations and market/Connect event subscriptions
  • verify Rangler webhook signatures in a receiver

It is designed to give you one integration surface across:

  • polling feeds
  • webhook delivery
  • developer control-plane operations

Installation

With uv:

uv add ranglerpy

With pip:

pip install ranglerpy

For local development:

pip install -e .[dev]

For release builds:

python scripts/update_version.py 0.1.1
python -m unittest discover -s tests -v
uv build --no-sources
UV_PUBLISH_TOKEN=pypi-your-project-token uv publish

For local checks with the PyPA tools:

pip install -e .[build]
python -m build
python -m twine check dist/*

Quick start

Data plane: polling events

from ranglerpy import InMemoryCursorStore, PollingConsumer, RanglerClient

client = RanglerClient(
    api_key="rgl_test_your_api_key",
    environment="sandbox",
)

consumer = PollingConsumer(
    client.v1.events,
    cursor_store=InMemoryCursorStore(),
    stream="market-wide-filings",
)

for event in consumer.poll(event_types=["filing.new"], limit=100):
    print(event.id, event.type, event.occurred_at.isoformat())

Async polling

from ranglerpy import AsyncPollingConsumer, AsyncRanglerClient, FileCursorStore

async with AsyncRanglerClient(
    api_key="rgl_test_your_api_key",
    environment="sandbox",
) as client:
    consumer = AsyncPollingConsumer(
        client.v1.events,
        cursor_store=FileCursorStore(".rangler-cursors.json"),
        stream="issuer-monitoring",
    )

    for event in await consumer.poll(
        limit=100,
        event_types=["filing.new"],
    ):
        print(event.id, event.type)

Control plane: manage event destinations

from ranglerpy import RanglerClient

client = RanglerClient(
    bearer_token="your_portal_bearer_token",
    environment="live",
)

organization = client.organizations.create(
    name="Rangler Demo Org",
    billing_email="billing@example.com",
)

destination = client.event_destinations.create(
    organization["id"],
    url="https://example.com/rangler/webhooks",
)

print(destination["id"])
subscription = client.subscriptions.create_connect(
    organization["id"],
    destination_id=destination["id"],
    name="All Connect events",
    event_types=["connect.sync.completed", "connect.item.login_required"],
    environment="live",
)

print(subscription["id"])

Webhook verification

from ranglerpy import InMemoryIdempotencyStore, Webhook

idempotency_store = InMemoryIdempotencyStore()

event = Webhook.construct_event(
    headers=headers,
    raw_body=raw_body,
    secret=webhook_secret,
    idempotency_store=idempotency_store,
)

print(event.type, event.display.title, event.data.object.id)

FastAPI webhook handler

from fastapi import FastAPI, HTTPException, Request

from ranglerpy import DuplicateEventError, InMemoryIdempotencyStore, InvalidSignatureError, Webhook

app = FastAPI()
store = InMemoryIdempotencyStore()


@app.post("/rangler/webhooks")
async def rangler_webhook(request: Request):
    raw_body = await request.body()

    try:
        event = Webhook.construct_event(
            headers=request.headers,
            raw_body=raw_body,
            secret="whsec_your_secret",
            idempotency_store=store,
        )
    except InvalidSignatureError as exc:
        raise HTTPException(status_code=400, detail=str(exc)) from exc
    except DuplicateEventError:
        return {"duplicate": True}

    return {"received": event.id}

Flask webhook handler

from flask import Flask, jsonify, request

from ranglerpy import DuplicateEventError, InMemoryIdempotencyStore, InvalidSignatureError, Webhook

app = Flask(__name__)
store = InMemoryIdempotencyStore()


@app.post("/rangler/webhooks")
def rangler_webhook():
    raw_body = request.get_data()

    try:
        event = Webhook.construct_event(
            headers=request.headers,
            raw_body=raw_body,
            secret="whsec_your_secret",
            idempotency_store=store,
        )
    except InvalidSignatureError as exc:
        return jsonify({"error": str(exc)}), 400
    except DuplicateEventError:
        return jsonify({"duplicate": True}), 200

    return jsonify({"received": event.id})

Authentication modes

Rangler has two auth modes:

  • data plane uses X-API-Key
  • developer control plane uses Authorization: Bearer ...

ranglerpy supports both on the same client. Resource methods choose the correct auth mode internally.

Current SDK scope

Data plane

  • companies
  • filings
  • funds
  • event feeds

Control plane

  • organizations
  • API keys
  • usage and billing status
  • event destinations
  • webhook deliveries
  • market event subscriptions
  • Connect event subscriptions

Helpers

  • cursor-based event iteration
  • Stripe-style client.v1.* namespace
  • list responses with data
  • dynamic object access for webhook/API payloads, e.g. event.data.object.id
  • polling consumer with checkpoint persistence
  • Rangler webhook signature verification
  • Webhook.construct_event(...) for webhook receivers
  • in-memory idempotency helper for duplicate webhook handling
  • async client support

Example scripts

The repo includes runnable examples in examples/:

These are intentionally small. They are meant to be copied into a real worker, cron job, or receiver and then adapted to your own storage and job queue.

Notes

This first version is intentionally thin. It aims to give Rangler customers one integration surface across polling and webhooks instead of pushing everyone straight into raw HTTP and receiver boilerplate.

You can override base_url explicitly if you need to point the SDK at a different Rangler environment.

The package version is tracked in VERSION, pyproject.toml, and src/ranglerpy/_version.py. The API contract version is tracked separately in src/ranglerpy/_api_version.py.

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

ranglerpy-0.1.2.tar.gz (21.2 kB view details)

Uploaded Source

Built Distribution

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

ranglerpy-0.1.2-py3-none-any.whl (27.8 kB view details)

Uploaded Python 3

File details

Details for the file ranglerpy-0.1.2.tar.gz.

File metadata

  • Download URL: ranglerpy-0.1.2.tar.gz
  • Upload date:
  • Size: 21.2 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.8.17

File hashes

Hashes for ranglerpy-0.1.2.tar.gz
Algorithm Hash digest
SHA256 c3f2c98bbba6b33a14f4e572a8131bdcfdb8be17397c6055e64eaed1c944f778
MD5 a61f20c7a06cbabc8c47dc60715352a7
BLAKE2b-256 be32ef999af04f115035098781ddf45d1e399cc064159dfda550a5f4217c6c0e

See more details on using hashes here.

File details

Details for the file ranglerpy-0.1.2-py3-none-any.whl.

File metadata

  • Download URL: ranglerpy-0.1.2-py3-none-any.whl
  • Upload date:
  • Size: 27.8 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.8.17

File hashes

Hashes for ranglerpy-0.1.2-py3-none-any.whl
Algorithm Hash digest
SHA256 5a06d3cce6bc4a738096705305f563d5afa95b44bdff8281be913f778967cca4
MD5 1efd04ab58f5ba5bdb650f8a3fd274e1
BLAKE2b-256 ad2e9c2f0387f2aca31390bde2de036fb5a1a11d67b60142aa5024330b6108cd

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