Pagination
Traverse Daily collections safely with opaque, stable cursors.
Every collection uses cursor pagination. The initial request omits cursor; subsequent requests pass the exact page.next_cursor returned by the previous response.
| Field or parameter | Behavior |
|---|---|
limit | Integer from 1 to 100; default 50 |
cursor | Opaque string, maximum 512 characters |
page.has_more | true when another page was observed |
page.next_cursor | Next cursor when has_more is true; otherwise null |
Do not decode, edit, cache permanently, or reuse a cursor with a different resource. Malformed, mismatched, or invalid cursors return 400 invalid_filter.
Ordering and stability
Customers, suppliers, leads, products, documents, payments, inventory levels, and tasks are ordered by updated_at descending and then ID descending. Inventory movements are ordered by created_at descending and then ID descending. The cursor carries both ordering values, which prevents same-timestamp rows from being skipped within one traversal.
For repeatable synchronization, capture the synchronization start time before page 1 and persist a high-water mark only after every page succeeds. Because rows can change during traversal, downstream writes should be idempotent by resource ID.
cURL loop
set -euo pipefail
: "${DAILY_API_KEY:?DAILY_API_KEY is required}"
cursor=""
while :; do
url='https://api.godaily.co.il/v1/customers?limit=100'
if [ -n "$cursor" ]; then
encoded_cursor="$(node -e 'process.stdout.write(encodeURIComponent(process.argv[1]))' "$cursor")"
url="${url}&cursor=${encoded_cursor}"
fi
body="$(curl --fail-with-body --silent --show-error \
--header "Authorization: Bearer ${DAILY_API_KEY}" \
--header 'Accept: application/json' \
"$url")"
BODY="$body" node -e '
const page = JSON.parse(process.env.BODY);
for (const customer of page.data) console.log(customer.id, customer.name);
'
cursor="$(BODY="$body" node -e '
const page = JSON.parse(process.env.BODY);
process.stdout.write(page.page.next_cursor ?? "");
')"
[ -n "$cursor" ] || break
doneTypeScript loop
type Page<T> = {
data: T[];
page: { has_more: boolean; next_cursor: string | null };
request_id: string;
};
const apiKey = process.env.DAILY_API_KEY;
if (!apiKey) throw new Error('DAILY_API_KEY is required');
let cursor: string | null = null;
do {
const url = new URL('https://api.godaily.co.il/v1/customers');
url.searchParams.set('limit', '100');
if (cursor) url.searchParams.set('cursor', cursor);
const response = await fetch(url, {
headers: { Accept: 'application/json', Authorization: `Bearer ${apiKey}` },
});
const body = await response.json() as Page<{ id: string; name: string }> & {
error?: { code: string };
};
if (!response.ok) {
throw new Error(`Daily API ${response.status} (${body.request_id}): ${body.error?.code ?? 'unknown_error'}`);
}
for (const customer of body.data) console.log(customer.id, customer.name);
cursor = body.page.next_cursor;
} while (cursor);Python loop
import os
import requests
api_key = os.environ.get("DAILY_API_KEY")
if not api_key:
raise RuntimeError("DAILY_API_KEY is required")
cursor = None
with requests.Session() as session:
session.headers.update({"Accept": "application/json", "Authorization": f"Bearer {api_key}"})
while True:
params = {"limit": 100}
if cursor:
params["cursor"] = cursor
response = session.get(
"https://api.godaily.co.il/v1/customers",
params=params,
timeout=30,
)
body = response.json()
if not response.ok:
code = body.get("error", {}).get("code", "unknown_error")
raise RuntimeError(f"Daily API {response.status_code} ({body.get('request_id')}): {code}")
for customer in body["data"]:
print(customer["id"], customer["name"])
cursor = body["page"]["next_cursor"]
if not cursor:
breakEach page is a separate request and counts toward rate limits. Apply 429 retry handling around the page request rather than restarting the entire traversal.

