plnnrly does not push changes to you: there are no webhooks. To keep your own copy of the data up to date, you poll. This page shows a polling loop that does not miss changes and stays well inside your rate budget.
In the examples, <your-url> is the address you sign in to plnnrly on, such as acme.plnnrly.com, or your organisation's own domain.
The high-water mark
Every collection accepts the filter filter[updated_at][gte] and the sort key updated_at. That is all a sync needs:
- Read the collection sorted by
updated_at, starting 60 seconds before your stored mark. - Follow
links.nextuntil it isnull. - Insert or update each row in your copy by its
id. - Store the highest
updated_atyou processed as your new mark.
curl -G https://<your-url>/api/v2/projects \
-H "Authorization: Bearer <token>" \
-H "Accept: application/json" \
--data-urlencode "filter[updated_at][gte]=2026-10-15T09:00:00Z" \
--data-urlencode "sort=updated_at,id" \
--data-urlencode "per_page=100"links.next in each response is the full URL of the next page, with your filter and sort carried over. Request it as it is:
curl "<links.next from the previous response>" \
-H "Authorization: Bearer <token>"For the very first run, leave the filter out and read the whole collection the same way.
Things to know:
- Overlap is intended. Start each run a little before your stored mark, for example 60 seconds before it. A change is stamped when it is made, not when it is saved for good. It can therefore appear after a read that has already passed its timestamp, and the overlap picks it up. Updating your copy by
idmakes the repeats harmless. - Use the exact format. A timestamp in a filter is UTC with a
Zand no fractions of a second, such as2026-10-15T09:00:00Z. See Data formats. - A row can appear twice in one run. When a row changes while you are paging, it moves to the end of the sort order and comes back later in the same run.
- Related lists are collections of their own.
updated_atbelongs to the row itself. Poll lists that belong to a record, such as a work item's comments, separately. - Some collections default to your own rows.
/worklogs, for example, returns only your own entries unless you name other users infilter[user_id], which needs an extra permission. The operation page says so. See Filtering and sorting.
Cheap re-checks
Small lists that you re-read in full, such as labels or workflow statuses, have a cheaper check. Send the page's weak ETag back as If-None-Match:
curl -i https://<your-url>/api/v2/labels \
-H "Authorization: Bearer <token>" \
-H 'If-None-Match: W/"a1b2c3d4e5f6"'An unchanged page answers 304 Not Modified with no body, and costs 1 unit. A single record works the same way with its own ETag. See Concurrency and ETags.
Deletions
Records disappear in two ways. Deleting and restoring lists which resources do what.
Archived records. On collections whose rows carry deleted_at, a delete archives the record. Add trashed=with to your sync query. Archiving and restoring both move updated_at, so the change arrives through your high-water mark, with deleted_at set or cleared.
Permanent deletes leave no trace to poll for. That includes a project deleted with force=true. Find them with a periodic id scan: read the whole collection by id, and remove from your copy every row the scan did not return.
curl -G https://<your-url>/api/v2/projects \
-H "Authorization: Bearer <token>" \
--data-urlencode "sort=id" \
--data-urlencode "trashed=with" \
--data-urlencode "per_page=100" \
--data-urlencode "view=summary"Keep trashed=with in the scan on collections that archive, or archived rows look deleted. A row you are no longer allowed to see also drops out of the scan.
What it costs
Take a sync of projects, customers and work items every 5 minutes, plus a nightly id scan. Costs follow Rate limits:
| Step | Requests | Units |
|---|---|---|
| A run where nothing changed: one first page per collection | 3 | 3 |
| A run that finds 250 changed work items: three pages of up to 100 rows, plus one page each for projects and customers | 5 | 5 |
The same run with include=labels on the work items: a to-many path limits the page to 25 rows and adds 3 units per page | 12 | 42 |
| A nightly id scan of 12,000 work items, at 100 per page | 120 | 120 |
Every step stays far below the 600 read units a token may spend per minute. Keep includes out of the sync loop unless you need them: they cost more than they look.
A separate read-only token for the sync gives it its own per-token budget. The organisation's budget is still shared with every other token. See API tokens.
Do not sync over MCP
The MCP server is made for an AI assistant answering questions. It allows 60 requests per minute per user, lists default to 10 rows, and an answer is cut off at 30,000 characters: see Tools. Sync with the REST API and a token instead.