Wprowadzenie
Python sprawdzi się, gdy faktury KSeF wysyłasz z usługi backendowej, zadania cron albo potoku danych. Notaly odbiera słownik JSON, sam tworzy plik FA(3), sprawdza go względem oficjalnego schematu XSD i przekazuje do KSeF — Ty nie dotykasz XML-a ręcznie. Przykład opiera się na popularnej bibliotece requests, którą zainstalujesz przez pip install requests.
Przygotowanie
Po założeniu darmowego konta sandbox skopiuj klucz API z panelu. W sandboksie NIP sprzedawcy musi być równy 2745013933; każda inna wartość zostanie odrzucona z kodem 422 seller_nip_not_authorized, zanim faktura zostanie wliczona do limitu trzech darmowych faktur testowych. Klucz trzymaj w zmiennej środowiskowej (NOTALY_API_KEY), zamiast wpisywać go wprost w kodzie.
Wysyłka faktury
import os
import requests
NOTALY_API_KEY = os.environ["NOTALY_API_KEY"]
faktura = {
"number": "FV/2026/10/001",
"issueDate": "2026-10-12",
"currency": "PLN",
"seller": {"nip": "2745013933", "name": "Example Sp. z o.o. (FICTIONAL)", "address": {"line1": "ul. Testowa 1, 00-001 Warszawa", "country": "PL"}},
"buyer": {"nip": "1234563218", "name": "Test Customer S.A. (FICTIONAL)", "address": {"line1": "ul. Probna 2, 30-001 Kraków", "country": "PL"}},
"items": [
{"name": "SaaS subscription - October", "unit": "pcs.", "quantity": 1, "unitNetPrice": 1000, "vatRate": 23},
{"name": "Support", "unit": "hrs.", "quantity": 2, "unitNetPrice": 150.5, "vatRate": 8},
],
}
response = requests.post(
"https://api.getnotaly.com/invoices",
headers={"Authorization": f"Bearer {NOTALY_API_KEY}"},
json=faktura,
timeout=30,
)
response.raise_for_status()
utworzona = response.json()
print(f"Faktura {utworzona['id']} ma status {utworzona['status']}")Argument json=faktura sam serializuje słownik i ustawia nagłówek Content-Type: application/json. timeout=30 chroni przed zawieszeniem żądania. W razie błędu HTTP raise_for_status() rzuca wyjątek, który możesz złapać i ponowić wysyłkę.
Odpowiedź i statusy
Serwer zwraca obiekt JSON z polami id i status. status to submitted, gdy KSeF przyjął zgłoszenie, albo draft, gdy KSeF był chwilowo niedostępny i faktura czeka na ponowienie. Zaakceptowana faktura zyskuje pola upo i pdfUrl. Notaly weryfikuje i wysyła fakturę; stawki VAT i klasyfikacja podatkowa pozostają po Twojej stronie.
Najczęstsze problemy
Zamiast analizować komunikaty, sprawdzaj pole error. validation_failed (422) przynosi listę details z informacją, które pole jest niepoprawne. quota_exceeded (402) oznacza, że limit trzech darmowych faktur sandbox został wyczerpany. ksef_unavailable (503) jest przejściowe — ponów z backoffem. Błędy uwierzytelniania to 401 (brak klucza lub błędny klucz) i 404 (poprawny klucz, ale cudzy zasób).
Śledzenie do końca
Status sprawdzisz przez GET /invoices/{id} z tym samym nagłówkiem Authorization albo przez webhooki. Faktura przechodzi kolejno draft → submitted → accepted lub rejected; odrzucona zawiera kod error, który warto zapisać w logu.
Źródła
Źródło: getnotaly.com