Skip to main content

Modern Python SDK for the Fakturoid API (port of the official PHP SDK)

Project description

Fakturoid Python SDK

PyPI Python Versions CI Status License uv Ruff Ask DeepWiki

A modern, typed, async-first Python SDK for Fakturoid API v3.

It supports OAuth2, invoices, invoice payments, correction documents, expenses, inventory resources, rate-limit helpers, and explicit handling of Fakturoid’s asynchronous PDF generation (204 No Content).

Built with httpx, asyncio, strict typing, and API-v3-focused behavior.

This is an independent Python SDK for Fakturoid API v3. It is not an official Fakturoid product.

This project was originally inspired by the official Fakturoid PHP SDK, but is designed as a Python-native async client.

Note: We highly recommend creating a new account specifically for API testing and using a separate user (created via "Settings > User account") for production usage.


Content


Installation

pip install fakturoid-sdk

or with uv:

uv add fakturoid-sdk

Development version:

uv add git+https://github.com/mrmidi/fakturoid-sdk.git

Authorization by OAuth 2.0

Authorization Code Flow

Suitable for applications where users log in with their own Fakturoid credentials.

  1. Get Authentication URL:

    from fakturoid_sdk import FakturoidClient
    
    async with FakturoidClient(
        client_id='{fakturoid-client-id}',
        client_secret='{fakturoid-client-secret}',
        user_agent='MyApp (admin@example.com)',
        redirect_uri='{your-redirect-uri}'
    ) as fakturoid:
        auth_url = fakturoid.auth.get_authentication_url(state="optional-state")
        print(f"Please authorize here: {auth_url}")
    
  2. Process callback: After the user is redirected back to your URI with a code:

    await fakturoid.auth.request_credentials(code_from_url)
    

Client Credentials Flow

Suitable for server-to-server communication where a user context is not required.

from fakturoid_sdk import AuthType, FakturoidClient

async with FakturoidClient(
    client_id='{fakturoid-client-id}',
    client_secret='{fakturoid-client-secret}',
    user_agent='MyApp (admin@example.com)',
    account_slug='{fakturoid-account-slug}'
) as fakturoid:
    await fakturoid.auth.auth(AuthType.CLIENT_CREDENTIALS_CODE_FLOW)

Credentials Callback

The SDK automatically refreshes expired tokens. Use a callback to persist updated credentials:

def on_credentials_changed(credentials):
    if credentials:
        # Save to DB: credentials.access_token, credentials.refresh_token, etc.
        print(f"New access token obtained: {credentials.access_token}")

fakturoid.auth.set_credentials_callback(on_credentials_changed)

Usage

Basic Usage

from fakturoid_sdk import AuthType, FakturoidClient

async with FakturoidClient(
    client_id="{fakturoid-client-id}",
    client_secret="{fakturoid-client-secret}",
    user_agent="MyApp (admin@example.com)",
    account_slug="{fakturoid-account-slug}",
) as fManager:
    await fManager.auth.auth(AuthType.CLIENT_CREDENTIALS_CODE_FLOW)

    # Create a subject
    subject = await fManager.subjects.create({'name': 'Firma s.r.o.', 'email': 'aloha@pokus.cz'})

    # Create an invoice with lines
    lines = [{'name': 'Big sale', 'quantity': 1, 'unit_price': 1000}]
    invoice = await fManager.invoices.create({'subject_id': subject['id'], 'lines': lines})

    # Send by email
    await fManager.invoices.create_message(invoice['id'], {'email': 'aloha@pokus.cz'})

    # Mark as paid
    await fManager.invoices.create_payment(invoice['id'], {'paid_on': '2026-04-21'})

    # Lock invoice
    await fManager.invoices.fire_action(invoice["id"], event="lock")

Switch account

You can change the account context without re-authenticating (provided the user has access to both).

# Initial account
fManager.set_account_slug('company-a')
await fManager.bank_accounts.list()

# Switch to another account
fManager.set_account_slug('company-b')
await fManager.bank_accounts.list()

Downloading an invoice PDF

Non-JSON endpoints return raw bytes.

Important: If you request a PDF immediately after creating an invoice, you might receive a 204 No Content (empty body) because the PDF isn't generated yet.

You can use get_pdf_or_none() to explicitly handle this, or wait_for_pdf() for built-in polling.

import asyncio

# Polling manually
while True:
    pdf = await fManager.invoices.get_pdf_or_none(invoice_id)
    if pdf:
        with open(f"invoice_{invoice_id}.pdf", "wb") as f:
            f.write(pdf)
        break
    await asyncio.sleep(1)

# Or using the helper
pdf = await fManager.invoices.wait_for_pdf(invoice_id)

Correction Documents

Create a correction document for an existing invoice:

await fManager.invoices.create_correction(invoice_id, {
    'lines': [{'name': 'Discount', 'quantity': 1, 'unit_price': -100}]
})

Using custom_id

Store your application's internal record ID in Fakturoid using the custom_id attribute.

# Find existing invoice by custom_id to ensure idempotency
invoice = await fManager.invoices.find_by_custom_id("repflow-invoice-123")
if invoice is None:
    # Create only if missing
    invoice_json = await fManager.invoices.create({
        "custom_id": "repflow-invoice-123",
        "subject_id": subject_id,
        "lines": [{"name": "Repair", "quantity": 1, "unit_price": 1000}],
    })

# You can also filter other resources:
response = await fManager.subjects.list(custom_id='10')
if response:
    subject = response[0]

Inventory Resources

Inventory Items:

# List items with filters
await fManager.inventory_items.list(sku='SKU1234', article_number='IAN321')

# Search items
await fManager.inventory_items.search(query='Item name')

# CRUD
await fManager.inventory_items.create({
    'name': 'Item name',
    'sku': 'SKU12345',
    'track_quantity': True,
    'quantity': 100
})
await fManager.inventory_items.archive(item_id)

Inventory Moves:

# Create stock-in move
await fManager.inventory_moves.create(item_id, {
    'direction': 'in',
    'quantity_change': 5,
    'purchase_price': '249.99'
})

Recurring Generators

# Pause a generator
await fManager.recurring_generators.pause(generator_id)

# Activate with a specific next occurrence
await fManager.recurring_generators.activate(generator_id, {
    'next_occurrence_on': '2026-05-01'
})

Rate Limiting

The SDK provides methods to inspect your rate limit status directly from the response or via exceptions:

from fakturoid_sdk import ClientError

try:
    # Use the dispatcher directly to get the Response object
    resp = await fManager.dispatcher.get("/accounts/{accountSlug}/account.json")
    print(f"Quota: {resp.get_rate_limit_quota()}")
    print(f"Remaining: {resp.get_rate_limit_remaining()}")

except ClientError as e:
    if e.is_rate_limit_exceeded():
        # Reset time in seconds
        reset_time = e.response.get_rate_limit_reset()
        print(f"Rate limit exceeded. Try again in {reset_time}s.")

Handling Errors

The library raises ClientError for 4xx statuses and ServerError for 5xx.

from fakturoid_sdk import ClientError, ServerError

try:
    await fManager.subjects.create({'name': ''})
except ClientError as e:
    print(f"Status: {e.status_code}") # 422
    # The API returns error details in the body
    error_data = e.response.get_body(return_json_as_dict=True)
    print(error_data['errors'])
except ServerError as e:
    print("Fakturoid is temporarily unavailable.")

Why this SDK?

  • Async-first Python API built on httpx
  • OAuth2 authorization code and client credentials flows
  • Required User-Agent support for Fakturoid API v3
  • Invoice, payment, correction, expense, inventory, and recurring generator resources
  • Explicit PDF readiness handling with get_pdf_or_none() and wait_for_pdf()
  • Rate-limit header helpers
  • Typed enums and model helpers where useful
  • Raw JSON access remains available for full API flexibility

Behavior Guarantees

  • No blocking HTTP calls.
  • user_agent is sent on all API and OAuth requests.
  • API errors retain request/response context for debugging.
  • Fire actions send event as a query parameter (not a JSON body).
  • Raw event strings via fire_action(event=...) remain available for future API actions.

Development

Setup with uv

git clone https://github.com/mrmidi/fakturoid-sdk.git
cd fakturoid-sdk
uv sync

Testing & Linting

uv run pytest       # Run tests
uv run ruff check . # Lint
uv run mypy src     # Type check

License

This project is licensed under the MIT License - see the LICENSE file for details.

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

fakturoid_sdk-0.2.1.tar.gz (26.4 kB view details)

Uploaded Source

Built Distribution

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

fakturoid_sdk-0.2.1-py3-none-any.whl (51.8 kB view details)

Uploaded Python 3

File details

Details for the file fakturoid_sdk-0.2.1.tar.gz.

File metadata

  • Download URL: fakturoid_sdk-0.2.1.tar.gz
  • Upload date:
  • Size: 26.4 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: uv/0.11.8 {"installer":{"name":"uv","version":"0.11.8","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for fakturoid_sdk-0.2.1.tar.gz
Algorithm Hash digest
SHA256 e87b7843480a03b718bd0bd1393899f61963fec14ae53449b5fbe3a36a91439b
MD5 961c75d4c94a2417752e13325e17fd58
BLAKE2b-256 39510e5f43abf78d5ace0180ee61baed2033f053f8485e0f060e5188f4c91b37

See more details on using hashes here.

File details

Details for the file fakturoid_sdk-0.2.1-py3-none-any.whl.

File metadata

  • Download URL: fakturoid_sdk-0.2.1-py3-none-any.whl
  • Upload date:
  • Size: 51.8 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: uv/0.11.8 {"installer":{"name":"uv","version":"0.11.8","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for fakturoid_sdk-0.2.1-py3-none-any.whl
Algorithm Hash digest
SHA256 058435c874f8e5b5b39f72acadb8f4ab67edea5e7f589979e4e388ae650940ed
MD5 54841d4a18e80c476e9e6f02ce59b6d8
BLAKE2b-256 b5ca8c573ec888535e7c026611a1a931cf67ee0a40fdb9f7863f9f647adf005b

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