Przewodniki
Paginacja i limity zapytań
Listy zwracają strony od najnowszych obiektów, a każdy klucz ma limit 100 zapytań na minutę z czytelnymi nagłówkami.
Listy
Endpointy list (/v1/invoices, /v1/events, /v1/webhook_endpoints) zwracają obiekty od najnowszych w kopercie:
{ "object": "list", "url": "/v1/invoices", "has_more": true, "data": [ ... ] }
| Parametr | Znaczenie |
|---|---|
limit |
Ile obiektów na stronie: 1-100, domyślnie 10. |
starting_after |
Id ostatniego obiektu z poprzedniej strony. |
Gdy has_more ma wartość true, poproś o następną stronę z starting_after równym id ostatniego elementu data.
AFTER=""
while :; do
PAGE=$(curl -s "https://app.ksefshop.pl/v1/invoices?limit=100${AFTER:+&starting_after=$AFTER}" \
-H "Authorization: Bearer $KSEF_API_KEY")
echo "$PAGE" | jq -r '.data[] | [.number, .ksef_number, .status] | @tsv'
[ "$(echo "$PAGE" | jq -r .has_more)" = "true" ] || break
AFTER=$(echo "$PAGE" | jq -r '.data[-1].id')
done<?php
function all_invoices(): Generator
{
$after = null;
do {
$query = http_build_query(array_filter(['limit' => 100, 'starting_after' => $after]));
$ch = curl_init('https://app.ksefshop.pl/v1/invoices?' . $query);
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . getenv('KSEF_API_KEY')],
]);
$page = json_decode(curl_exec($ch), true);
curl_close($ch);
yield from $page['data'];
$after = $page['data'] ? end($page['data'])['id'] : null;
} while ($page['has_more']);
}
foreach (all_invoices() as $invoice) {
echo $invoice['number'], ' ', $invoice['ksef_number'], PHP_EOL;
}async function* allInvoices() {
let after = null;
while (true) {
const url = new URL("https://app.ksefshop.pl/v1/invoices");
url.searchParams.set("limit", "100");
if (after) url.searchParams.set("starting_after", after);
const res = await fetch(url, {
headers: { Authorization: `Bearer ${process.env.KSEF_API_KEY}` },
});
const page = await res.json();
if (!res.ok) throw new Error(`${page.error.code}: ${page.error.message}`);
yield* page.data;
if (!page.has_more) return;
after = page.data.at(-1).id;
}
}
for await (const invoice of allInvoices()) {
console.log(invoice.number, invoice.ksef_number);
}import os
import requests
def all_invoices():
after = None
while True:
params = {"limit": 100}
if after:
params["starting_after"] = after
page = requests.get(
"https://app.ksefshop.pl/v1/invoices",
headers={"Authorization": f"Bearer {os.environ['KSEF_API_KEY']}"},
params=params,
timeout=30,
).json()
yield from page["data"]
if not page["has_more"]:
return
after = page["data"][-1]["id"]
for invoice in all_invoices():
print(invoice["number"], invoice["ksef_number"])Filtry
GET /v1/invoices?external_id=SO-1001zwraca fakturę jednego zamówienia (z korektami).GET /v1/events?type=invoice.rejectedzwraca tylko zdarzenia jednego typu.
Limity zapytań
Każdy klucz może wykonać 100 zapytań na minutę. Każda odpowiedź ma nagłówki:
| Nagłówek | Znaczenie |
|---|---|
RateLimit-Limit |
Limit w oknie (100). |
RateLimit-Remaining |
Ile zapytań zostało. |
RateLimit-Reset |
Za ile sekund okno się odnowi. |
Po przekroczeniu dostaniesz 429 rate_limited i nagłówek Retry-After z liczbą sekund do odczekania. Kod ponawiania jest w przewodniku idempotencja.
Jak nie wpaść w limit
- Zamiast odpytywać każdą fakturę co kilka sekund, odbieraj webhooki.
- Odpytując, rób to co 3-5 sekund i przestań po stanie końcowym.
- Do raportów pobieraj strony po 100 obiektów, a nie pojedyncze faktury.
- Testowe i produkcyjne klucze mają osobne limity.