Guides
Pagination
Lists return one page at a time. Ask for a page with limit, and while pagination. is true call again with the nextCursor you were given.
The loop
Link to this section: The loopGET /api/v1 /orders?limit=50 limitdefaults to 50 and is at most 500. AddincludeTotal=trueto getpagination..total dataandpagination { hasMore: true, nextCursor }While
hasMoreistrue:GET /api/v1 /orders?limit=50&cursor=<nextCursor> The same
limitandsort: a cursor is bound to both. The cursor is opaque; do not build it.Next page
hasMore: false: you have read every page.
Request the first page
Send the list request with
limit: 1 to 500, 50 by default. Do not send a cursor.Read the page
Take the records from
data.pagination.tells you whether more results exist.hasMore Ask for the next one
While
hasMoreistrue, call again withcursorset topagination., and the samenextCursor limitand filters.Stop
On the last page
hasMoreisfalseandnextCursorisnull.
# Needs curl and jq. Prints one order per line.
base="${FILMO_BASE_URL:-https://app.filmo.io}"
cursor=""
while :; do
# Keep limit and sort identical on every page.
set -- --data-urlencode "limit=100" --data-urlencode "sort=createdAt"
[ -n "$cursor" ] && set -- "$@" --data-urlencode "cursor=$cursor"
page="$(curl -sS --fail-with-body -G "$base/api/v1/orders" \
-H "Authorization: Bearer $FILMO_API_KEY" "$@")" || { echo "$page" >&2; exit 1; }
echo "$page" | jq -c '.data[]'
cursor="$(echo "$page" | jq -r 'if .pagination.hasMore then .pagination.nextCursor else "" end')"
[ -n "$cursor" ] || break
doneconst base = process.env.FILMO_BASE_URL ?? "https://app.filmo.io";
const headers = { Authorization: `Bearer ${process.env.FILMO_API_KEY}` };
const orders = [];
let cursor = null;
do {
const url = new URL("/api/v1/orders", base);
url.searchParams.set("limit", "100"); // keep limit and sort identical on every page
url.searchParams.set("sort", "createdAt");
if (cursor) url.searchParams.set("cursor", cursor);
const res = await fetch(url, { headers });
if (!res.ok) throw new Error(res.status + " " + (await res.text()));
const page = await res.json();
orders.push(...page.data);
cursor = page.pagination.hasMore ? page.pagination.nextCursor : null;
} while (cursor);
console.log(orders.length + " orders");import os
import requests
base = os.environ.get("FILMO_BASE_URL", "https://app.filmo.io")
headers = {"Authorization": f"Bearer {os.environ['FILMO_API_KEY']}"}
orders, cursor = [], None
while True:
# Keep limit and sort identical on every page.
params = {"limit": 100, "sort": "createdAt"}
if cursor:
params["cursor"] = cursor
res = requests.get(base + "/api/v1/orders", headers=headers, params=params, timeout=30)
res.raise_for_status()
page = res.json()
orders += page["data"]
if not page["pagination"]["hasMore"]:
break
cursor = page["pagination"]["nextCursor"]
print(len(orders), "orders")The pagination object
Link to this section: The pagination object| Property | Type | Meaning |
|---|---|---|
nextCursor | string or null | Opaque cursor of the next page. null when there are no more results. |
hasMore | boolean | Whether more results exist beyond this page. |
total | integer | The number of results that match the filters across all pages. Present only when the request sets includeTotal to true. |
- Treat the cursor as opaque. Do not build it, parse it or store it for long; it only continues the pass it came from. A cursor is at most 200 characters long, and one that was altered, or that does not match the
limitit is sent with, is rejected. - Some lists are not paginated: they return everything in one response and take no
limit,cursororsort. They areGET /api,/v1 /tags GET /api,/v1 /task-types GET /api,/v1 /crm /pipelines GET /api,/v1 /custom-fields GET /api,/v1 /project-types GET /api,/v1 /contacts /{contactId} /related-contacts GET /apiand/v1 /planner /services GET /api. The other lists take/v1 /planner /resources limitandcursor. - A cursor is bound to the
limitand thesortit was issued with. Keep both identical on every page of a pass. See Filtering and sorting. - Every list has a deterministic, stable order: newest first unless the operation says otherwise, with
idas the tie-breaker. The same request gives the same order, but the cursor is a position: records created or deleted while you page can make one repeat or be skipped, so deduplicate byid. - Ask for
totalonly when you need it, withincludeTotal. It is omitted otherwise. - A larger page means fewer requests against the limit of 120 per minute. At the maximum of 500, reading 10,000 records takes 20 requests. See Rate limits.
When records change while you page
Link to this section: When records change while you pageFollowing the cursor can skip records
The cursor marks a position in the result. If a record you have already read is updated or deleted while you are still paging, the records after it shift, and one at the boundary of a page can be missed; it is not returned again in that pass. A pass that tolerates this, such as a one-off export, can follow the cursor. The cursor is a position, not a snapshot: if you sort by updatedAt and a record is updated while you page, it moves to the end of an ascending pass (and the following records may be skipped) or to the front of a descending one (and you may read a record twice). To keep another system in step with Filmo, do not rely on the cursor: read time windows with the updated* filters instead. Syncing data explains how.
Where to go next
Link to this section: Where to go next- Filtering and sorting narrows what a pass has to read.
- Errors covers what to do when a page request fails.
Related pages
Link to this section: Related pagesGuides
Filtering and sorting
Most lists accept
sortand thecreated*andupdated*time filters, many accept list-valued filters such asstatuses, and some acceptqfor free-text search. The examples use the orders list.Guides
Syncing data
Filmo v1 does not send webhooks, so you keep another system in step by polling: read what changed in half-open time windows, and run a separate sweep to find what was deleted. Do not follow the pagination cursor to decide what changed or what disappeared.
Guides
Rate limits
Each API key has 120 requests per minute, and the three availability checks share their own quota of 30 per minute. Past a limit the API answers 429 with
Retry-After.
Was this page helpful?