qodev-qonto-api
Async Python client for Qonto Business API with full type safety.
Features
- Async-first design built on httpx
- Full Pydantic v2 models with
extra="allow"for forward compatibility - Context manager support for clean resource management
- Sandbox support via
QONTO_BASE_URLenvironment variable orbase_url=kwarg - ~40 API methods across 15+ resource types (organizations, memberships, labels, bank accounts, transactions, attachments, clients, invoices, quotes, credit notes, supplier invoices, products, SEPA transfers, bulk transfers, internal transfers, beneficiaries)
- Multipart attachment upload helper for receipts and documents
- Typed exceptions —
AuthenticationError,NotFoundError,ValidationError,RateLimitError,APIError - Generic
PaginatedResponse[T]with Qonto meta-envelope flattening - Rate-limit header tracking via
client.rate_limit_status - py.typed marker for downstream type checking
Installation
pip install qodev-qonto-api
Or with uv:
uv add qodev-qonto-api
Quick Start
import asyncio
from qodev_qonto_api import QontoClient
async def main():
async with QontoClient() as client: # reads QONTO_LOGIN + QONTO_SECRET_KEY
org = await client.get_organization()
print(org.legal_name)
accounts = await client.list_bank_accounts()
for account in accounts:
txns = await client.list_transactions(
bank_account_id=account.id, per_page=10,
)
print(f"{account.iban}: {len(txns.items)} recent")
asyncio.run(main())
Configuration
Credentials
Set the two required environment variables:
export QONTO_LOGIN="your-org-login"
export QONTO_SECRET_KEY="your-secret-key"
Or pass directly:
async with QontoClient(login="your-org-login", secret_key="your-secret-key") as client:
...
Important: The Qonto
Authorizationheader is the literal string{login}:{secret}with a real colon. This is not HTTP Basic Authentication — there is no Base64 encoding.
Sandbox
Switch to the staging sandbox via env var:
export QONTO_BASE_URL="https://thirdparty-sandbox.staging.qonto.co/v2"
Or via kwarg:
async with QontoClient(base_url="https://thirdparty-sandbox.staging.qonto.co/v2") as client:
...
Timeout
Customize request timeout (default 30 seconds):
async with QontoClient(timeout=60.0) as client:
...
Rate Limiting
Qonto enforces these limits (per IP):
- 1,000 requests / 10 seconds
- 10,000 requests / 10 minutes
Rate-limit headers are captured defensively (Qonto does not officially document header names — the client captures any ratelimit-* or x-ratelimit-* header) and exposed as a raw dict:
async with QontoClient() as client:
await client.list_bank_accounts()
print(client.rate_limit_status) # {"x-ratelimit-remaining": "998", ...}
API Methods
Organization
org = await client.get_organization()
Memberships & Labels
members = await client.list_memberships()
labels = await client.list_labels()
Bank Accounts
accounts = await client.list_bank_accounts()
Transactions
GET /transactions requires either bank_account_id or iban:
txns = await client.list_transactions(bank_account_id="acc_123", per_page=50)
txn = await client.get_transaction("txn_456")
Attachments
attachment = await client.upload_attachment(
file_path="invoice.pdf",
idempotency_key="unique-key-123",
)
await client.attach_to_transaction(transaction_id="txn_456", attachment_ids=[attachment.id])
Clients
clients = await client.list_clients()
client_obj = await client.create_client(type="company", name="Acme Corp", email="billing@acme.com")
Client Invoices
invoices = await client.list_client_invoices()
invoice = await client.create_client_invoice(client_id="cl_1", items=[...])
Quotes & Credit Notes
quotes = await client.list_quotes()
credit_notes = await client.list_credit_notes()
Supplier Invoices
supplier_invoices = await client.list_supplier_invoices()
si = await client.get_supplier_invoice("si_123")
Products
products = await client.list_products()
SEPA Transfers
# VoP (Verification of Payee) must be called manually — pass the resulting
# vop_proof_token via extra_fields. See roadmap.
transfer = await client.create_sepa_transfer(
debit_account_id="acc_1",
beneficiary_id="ben_1",
amount="100.00",
currency="EUR",
reference="Invoice INV-001",
vop_proof_token="token-from-verify-payee",
)
Bulk Transfers & Internal Transfers
bulk = await client.create_bulk_transfer(debit_account_id="acc_1", transfers=[...])
internal = await client.create_internal_transfer(
debit_account_id="acc_1",
credit_account_id="acc_2",
amount="50.00",
)
Beneficiaries
beneficiaries = await client.list_beneficiaries()
Error Handling
from qodev_qonto_api import (
QontoClient,
QontoError,
AuthenticationError,
NotFoundError,
ValidationError,
RateLimitError,
APIError,
)
try:
async with QontoClient() as client:
await client.get_transaction("does-not-exist")
except AuthenticationError:
print("Invalid login / secret key")
except NotFoundError:
print("Resource not found")
except ValidationError as e:
for err in e.errors:
print(f"{err.get('source', {}).get('pointer')}: {err.get('detail')}")
except RateLimitError as e:
print(f"Rate limited. Retry after {e.retry_after} seconds")
except APIError as e:
print(f"API error: {e} (status: {e.status_code})")
except QontoError as e:
print(f"Qonto client error: {e}")
Development
make install # Install dependencies
make check # Lint, format, typecheck, typos
make test # Run tests with coverage
Roadmap (Out of Scope for v0.1.0)
- OAuth2 / Qonto Connect — partner app integrations
- PSD2 QSeal certificates — regulatory signing
- Cards API — virtual/physical card management
- Webhooks — event subscriptions
- Onboarding API — account creation flows
- VoP integration — Verification of Payee for SEPA transfers (currently manual via
extra_fields) - Payment links
- Embed iframe features
License
MIT
Metadata
Release files for qodev-qonto-api 0.1.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| qodev_qonto_api-0.1.0.tar.gz | 65.1 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| qodev_qonto_api-0.1.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 81.2 kB
Release files / qodev_qonto_api-0.1.0.tar.gz
| Download URL | qodev_qonto_api-0.1.0.tar.gz |
|---|---|
| Size | 65.1 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
e1fbffffbdd16e4315c91b3436581b8a6fe52bc3d9f83b08b6615ad04ecbc3f1
|
|
BLAKE2b-256 checksum How to use checksums |
b44276fd4bfa9a32ada2a8dc6e92987a557b7d3c1b2246084339dc605ea5d2e7
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.12
|
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 Apr 7, 2026.
Transparency logRelease files / qodev_qonto_api-0.1.0-py3-none-any.whl
| Download URL | qodev_qonto_api-0.1.0-py3-none-any.whl |
|---|---|
| Size | 16.1 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
9e6782de12f986abf69124f492df5ea14434dfbaa756147793895028a7adff92
|
|
BLAKE2b-256 checksum How to use checksums |
3e96c2a9c009944dd64cb04bf89e4df7aa269bfa8f9dc4c8edce949f2c949167
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.12
|
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 Apr 7, 2026.
Transparency log