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.2.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.2-py3-none-any.whl (52.3 kB view details)

Uploaded Python 3

File details

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

File metadata

  • Download URL: fakturoid_sdk-0.2.2.tar.gz
  • Upload date:
  • Size: 27.0 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.2.tar.gz
Algorithm Hash digest
SHA256 636f2a3c8b6fa2cbae4a147eccf03b01c50e75077984c5991c3081952440c029
MD5 b2c8476e19307a8781ba63cea90a3d7d
BLAKE2b-256 eb5a74486537d81454ec91d5bf79e373003140f1d24a2c1f346b50757eaf291e

See more details on using hashes here.

File details

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

File metadata

  • Download URL: fakturoid_sdk-0.2.2-py3-none-any.whl
  • Upload date:
  • Size: 52.3 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.2-py3-none-any.whl
Algorithm Hash digest
SHA256 6ccc22039da3cda62d7f24fdd93edb15ea04ec646261679050af5584c8408e79
MD5 b65b47793f160385cbd5474d13ba149f
BLAKE2b-256 c797ba0d44abfc671181daf06aba2d5f3eb337081267b6a0217aaeb18edd8795

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