Skip to main content

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 httpx2, 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 httpx2
  • 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.

Metadata

Release files for fakturoid-sdk 0.2.3

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for fakturoid-sdk 0.2.3
File Size Uploaded
fakturoid_sdk-0.2.3.tar.gz 27.0 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for fakturoid-sdk 0.2.3
File Interpreter ABI Platform
fakturoid_sdk-0.2.3-py3-none-any.whl Python 3 none any Details

Total release size: 79.3 kB

Release files / fakturoid_sdk-0.2.3.tar.gz

Download URL fakturoid_sdk-0.2.3.tar.gz
Size 27.0 kB
Tags Source
SHA-256 checksum
How to use checksums
fc1afa756f05f12ad6cbc821705ab9376fa3ed6c94e33d06a5982f03b34e7612
BLAKE2b-256 checksum
How to use checksums
e6053d7b54bc26455c27fa36754088a78319a6b62bedd1fab51f56635bcc8670
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via uv/0.11.26 {"installer":{"name":"uv","version":"0.11.26","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}

Release files / fakturoid_sdk-0.2.3-py3-none-any.whl

Download URL fakturoid_sdk-0.2.3-py3-none-any.whl
Size 52.3 kB
Tags Python 3
SHA-256 checksum
How to use checksums
eb155af55b9753c7c1c2e425fc61d005095217557a3410ccd58bc6fff45f171d
BLAKE2b-256 checksum
How to use checksums
ff8e916ea72225dd16780dad0de6bc1aeaf408953891ad1c02a76f2085e6786a
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via uv/0.11.26 {"installer":{"name":"uv","version":"0.11.26","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}

Release history Release notifications | RSS feed

This release

0.2.3 This release

2 release files

0.2.2

2 release files

0.2.1

2 release files

0.2.0

2 release files

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