Skip to main content
Yanked

This release has been yanked by its maintainers, and will be ignored by installers, except when explicitly specified.

accrawl (Python)

Official Python client for the Accrawl Data API — a self-hosted, credential-scraping account aggregator. Run the "Connect with Accrawl" OAuth flow to obtain a scoped token, then read the normalized data contract: accounts with a two-level taxonomy + balance triple, holdings + securities, and a transaction change cursor. Zero dependencies (Python standard library only).

The API reads data Accrawl has already retrieved, and does nothing else — every call is a GET. Starting a retrieval, following one, and relaying a one-time passcode belong to the person whose accounts these are, in their own Accrawl console, so this client has no method for them. Connections refresh on their own schedule; last_synced_at on a connection and as_of on a balance tell you how current the data is.

Install

pip install accrawl

Usage

import os
from accrawl import AccrawlClient

accrawl = AccrawlClient(
    base_url="https://accrawl.example.com",  # your deployment's front door
    api_key=os.environ["ACCRAWL_API_KEY"],   # a scoped key: acck_…
)

# 1. Discover what this credential may read. Each entry names its institution, so you never have to
#    show a slug: id, institution_id, institution_name, institution_type, institution_logo_url,
#    status, nickname, last_synced_at.
connections = accrawl.list_connections()
connection_id = connections[0].id

# 2. Read. The data API (v1) is a provider-style, retrieval-neutral contract: accounts carry a
#    two-level `type`+`subtype` and a current/available/limit balance triple, investments split into
#    holdings + de-duplicated securities, and transactions expose a change cursor.
#    Each page's `items` ARE the records (the id is inline — there is no {id, data} wrapper).
accounts = accrawl.list_accounts(connection_id)
for acct in accounts.items:
    print(acct.name, acct.type, acct.subtype, acct.balance.current, acct.currency)

txns = accrawl.list_transactions(connection_id, from_="2026-01-01", to="2026-06-30", limit=100)
holdings = accrawl.list_holdings(connection_id)  # .holdings + .securities

Incremental sync

# Walk the transaction change cursor until has_more is False
cursor = None
while True:
    page = accrawl.sync_transactions(connection_id, cursor=cursor)
    #   page.added / page.modified — ContractTransaction records; page.removed — ids
    cursor = page.next_cursor
    if not page.has_more:
        break

Freshness

There is no refresh call. Read the age you actually have and show it honestly:

for c in accrawl.list_connections():
    c.last_synced_at   # 'YYYY-MM-DD' of the last successful transaction sync, or None
    c.status           # 'needs_reauth' → the owner must fix it in their Accrawl console

A credential carries the read:data scope — the only one there is — and connection grants; a call it isn't authorized for raises AccrawlApiError with .status 401 (bad key) or 403 (missing scope/grant).

from accrawl import AccrawlApiError
try:
    accrawl.list_accounts(connection_id)
except AccrawlApiError as e:
    if e.status == 403:
        ...  # not granted

Connect with Accrawl (OAuth)

AccrawlOAuthClient runs the server side of the "Connect with Accrawl" Authorization-Code + PKCE flow, so your app can obtain a scoped token for a consenting operator's data without ever seeing their Accrawl password. PKCE (S256) is generated for you and is mandatory. See docs/spec-oauth.md.

import os
from accrawl import AccrawlOAuthClient, AccrawlClient

oauth = AccrawlOAuthClient(
    base_url="https://accrawl.example.com",
    client_id=os.environ["ACCRAWL_CLIENT_ID"],
    client_secret=os.environ["ACCRAWL_CLIENT_SECRET"],  # omit for a public (PKCE-only) client
    redirect_uri="https://app.example.com/callback",
)

# 1. Start the flow: redirect the browser to `url`. Persist `state` + `code_verifier` on the user's session.
started = oauth.start_authorization(scope="read:data")
#   started.url, started.state, started.code_verifier

# 2. On your /callback: verify `state` matches, then exchange the single-use code.
tokens = oauth.exchange_code(code=code, code_verifier=started.code_verifier)
#   {"access_token": "acck_…", "refresh_token": "acrt_…", "expires_in": ..., "scope": ..., "token_type": ...}

# 3. The access token IS an Accrawl API key — read the consented data with it.
api = AccrawlClient(base_url="https://accrawl.example.com", api_key=tokens["access_token"])
accounts = api.list_accounts(connection_id)

# 4. Rotate near expiry (reuse of a consumed refresh token revokes the whole grant); revoke on disconnect.
rotated = oauth.refresh(tokens["refresh_token"])
oauth.revoke(rotated["refresh_token"], token_type_hint="refresh_token")

The token and its grant share a ~90-day window; a refresh rotates the pair within that window, it never extends it. A non-2xx raises AccrawlApiError carrying the OAuth error/error_description.

Webhooks

Subscribe to crawl.completed / crawl.failed to avoid polling. Verify each delivery against the raw request body before trusting it:

from accrawl import verify_webhook_signature, parse_webhook_payload

ok = verify_webhook_signature(
    secret=os.environ["ACCRAWL_WEBHOOK_SECRET"],
    raw_body=raw_body,                                # exactly as received
    signature=request.headers["X-Accrawl-Signature"],
    timestamp=request.headers["X-Accrawl-Timestamp"],
    tolerance_seconds=300,                            # optional replay window
)
if not ok:
    return 400

payload = parse_webhook_payload(raw_body)  # typed CrawlWebhookPayload

The normalized contract also emits sync.succeeded / sync.failed / transactions.updated / connection.status_changed (and grant.revoked for OAuth) with the same signing scheme; verify_webhook_signature is event-agnostic, so it verifies those too — decode the body with json.loads after verifying.

Development

python3 -m unittest discover -s tests -t .

License

AGPL-3.0-only.

Metadata

Release files for accrawl 0.1.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 accrawl 0.1.0
File Size Uploaded Provenance
accrawl-0.1.0.tar.gz 18.6 kB Without provenance Details

Built distribution (wheel)

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

Total release size: 35.3 kB

Release files / accrawl-0.1.0.tar.gz

Download URL accrawl-0.1.0.tar.gz
Size 18.6 kB
Tags Source
SHA-256 checksum
How to use checksums
b25bb1096e6eeb21cb43f911cf411760e730308c461a804587c11eaa65c14e32
BLAKE2b-256 checksum
How to use checksums
f23bb5e2c84f620d47bdb51d5d253b6f1d7d6144acb3b96acb1f0abfae87b490
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.12.5 {"installer":{"name":"uv","version":"0.12.5","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

Release files / accrawl-0.1.0-py3-none-any.whl

Download URL accrawl-0.1.0-py3-none-any.whl
Size 16.7 kB
Tags Python 3
SHA-256 checksum
How to use checksums
9bbf3a0db4443c83b0b2f2e520bfef53b9e09662624ffaac9bf339d32ede2bc1
BLAKE2b-256 checksum
How to use checksums
e00b50e39e5940552ff76fbf0ef849d0967dece300c1bdcc991be4b72bf3c26d
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.12.5 {"installer":{"name":"uv","version":"0.12.5","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

Security for accrawl 0.1.0

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.

No provenance found

None of the files in this release were uploaded with provenance metadata.

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Release history Release notifications | RSS feed

This release

0.1.0 This release

2 release files No provenance

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