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 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.

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.3.tar.gz (27.0 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.3-py3-none-any.whl (52.3 kB view details)

Uploaded Python 3

File details

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

File metadata

  • Download URL: fakturoid_sdk-0.2.3.tar.gz
  • Upload date:
  • Size: 27.0 kB
  • Tags: Source
  • Uploaded using 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}

File hashes

Hashes for fakturoid_sdk-0.2.3.tar.gz
Algorithm Hash digest
SHA256 fc1afa756f05f12ad6cbc821705ab9376fa3ed6c94e33d06a5982f03b34e7612
MD5 462dac0224b6df8b50840ac2d00d2eb6
BLAKE2b-256 e6053d7b54bc26455c27fa36754088a78319a6b62bedd1fab51f56635bcc8670

See more details on using hashes here.

File details

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

File metadata

  • Download URL: fakturoid_sdk-0.2.3-py3-none-any.whl
  • Upload date:
  • Size: 52.3 kB
  • Tags: Python 3
  • Uploaded using 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}

File hashes

Hashes for fakturoid_sdk-0.2.3-py3-none-any.whl
Algorithm Hash digest
SHA256 eb155af55b9753c7c1c2e425fc61d005095217557a3410ccd58bc6fff45f171d
MD5 541b0a9633d0e8e625a1f981579c3aee
BLAKE2b-256 ff8e916ea72225dd16780dad0de6bc1aeaf408953891ad1c02a76f2085e6786a

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