Modern Python SDK for the Fakturoid API (port of the official PHP SDK)
Project description
Fakturoid Python SDK
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.
-
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}")
-
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.
# Filter by custom_id
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-Agentsupport for Fakturoid API v3 - Invoice, payment, correction, expense, inventory, and recurring generator resources
- Explicit PDF readiness handling with
get_pdf_or_none()andwait_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_agentis sent on all API and OAuth requests.- API errors retain request/response context for debugging.
- Fire actions send
eventas 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
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file fakturoid_sdk-0.2.0.tar.gz.
File metadata
- Download URL: fakturoid_sdk-0.2.0.tar.gz
- Upload date:
- Size: 26.1 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
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
25fe2a7ee3faa0bef29a20e15c4ebdcc067096821c8b2870a11bc1dbb1ac5183
|
|
| MD5 |
7578d111bd3ac61ef1ff1f0b251cdebb
|
|
| BLAKE2b-256 |
41c2817050aa86ec55176d9006f3920c071349fd0b26f36b500f088df1a8723c
|
File details
Details for the file fakturoid_sdk-0.2.0-py3-none-any.whl.
File metadata
- Download URL: fakturoid_sdk-0.2.0-py3-none-any.whl
- Upload date:
- Size: 51.5 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
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
d320cf935091f7bda5516c2e1b4c71a20bd2d67e06d2db0f4f8a1828bcd930d1
|
|
| MD5 |
fd756c35f4598b0109a520c9f5934d37
|
|
| BLAKE2b-256 |
8d73b2b7b06e84b34763f0449512b988791b0ab3f4ffe1e0e316894402953395
|