Deze referentie is alleen in het Engels. Naar het Nederlandse REST API-overzicht

Diese Referenz gibt es nur auf Englisch. Zur deutschen REST-API-Übersicht

Cette référence existe uniquement en anglais. Aller à la présentation de l'API REST en français

Esta referencia solo está en inglés. Ir a la introducción de la API REST en español

Pagination

Pagination

Cursor paging by default, page numbers on collections that count their rows, page sizes, totals, and how to walk a whole collection.

View as Markdown

Every collection read returns one page of rows at a time. This page shows how to read the page state, move between pages, and walk a whole collection.

Paths are relative to https://<your-url>/api/v2, where <your-url> is the address you sign in to plnnrly on, such as acme.plnnrly.com, or your organisation's own domain.

The envelope

A collection answers with data, meta.page and links. The first page of GET /projects:

JSON
{
  "data": [
    // 25 projects in the summary view
  ],
  "meta": {
    "page": {
      "per_page": 25,
      "next_cursor": "<opaque cursor>",
      "prev_cursor": null,
      "total": 184,
      "current_page": null,
      "last_page": null
    }
  },
  "links": {
    "self": "https://<your-url>/api/v2/projects?sort=title&trashed=without&per_page=25&view=summary",
    "next": "https://<your-url>/api/v2/projects?sort=title&trashed=without&per_page=25&cursor=<opaque cursor>&view=summary",
    "prev": null
  }
}
  • data holds the rows.
  • meta.page holds the page state. current_page and last_page are present on collections that support page numbers, and null until you send page.
  • links holds ready-made URLs. next and prev repeat every parameter that shaped the page, including the defaults, so following them keeps your query intact.

meta.warnings appears next to meta.page when there is something to report. See Views and includes. Every member of the envelope is described on the pagination schema page.

Page size

per_page sets the number of rows. It defaults to 25 and goes up to 100.

When include names a relation that holds several records, such as labels on a work item, the maximum is 25. Above that you get 400 invalid-query-parameter with code out_of_range. See Views and includes.

All limits are listed together in Limits at a glance.

Cursor paging

Cursor paging is the default. Send no paging parameter and you get the first page.

To get the next page, request links.next as it is. It is already URL-encoded. You can also send the cursor yourself, as cursor; it is the same value as meta.page.next_cursor:

Terminal
curl -G "https://<your-url>/api/v2/projects" \
  -H "Authorization: Bearer <token>" \
  --data-urlencode "cursor=<cursor from links.next>"

On the last page, links.next and next_cursor are null.

A cursor is tied to the query that produced it: its sort, its filters, q, trashed and view. Change any of those and the cursor no longer fits, which is 400 invalid-cursor. Start again from the first page when you change the query. You may change per_page between pages.

Never build or edit a cursor yourself. Use a cursor to fetch the next page, not to resume a job days later. To pick up where a sync left off, filter on updated_at instead; see Keeping a copy in sync.

With page numbers, a row added to an earlier page shifts every later row by one, so you read one row twice. A cursor is not thrown off by rows added or deleted elsewhere: it continues after the last row you read. A row whose sort value changes while you page can still move past the cursor, so sort by id when you must not miss a row.

Page numbers

Some collections also accept page, counted from 1 up to 1000. They are the collections that count their rows on every request, because a page number is only useful when you know the last page:

Sprints
/sprints

On any other collection, page is 400 unsupported-parameter. The operation page lists page when a collection accepts it.

Send page and the response switches to page mode. GET /projects?page=2:

JSON
{
  "data": [
    // 25 projects in the summary view
  ],
  "meta": {
    "page": {
      "per_page": 25,
      "next_cursor": null,
      "prev_cursor": null,
      "total": 184,
      "current_page": 2,
      "last_page": 8
    }
  },
  "links": {
    "self": "https://<your-url>/api/v2/projects?sort=title&trashed=without&per_page=25&page=2&view=summary",
    "next": "https://<your-url>/api/v2/projects?sort=title&trashed=without&per_page=25&page=3&view=summary",
    "prev": "https://<your-url>/api/v2/projects?sort=title&trashed=without&per_page=25&page=1&view=summary"
  }
}

In page mode, links.next and links.prev are page-number links, and both cursors are null. page and cursor cannot be combined: sending both is 400 with code mutually_exclusive.

Paging past the end

links.next is null on the last page. If you ask for a page beyond last_page, you get an empty data list, not an error, and current_page repeats the number you sent. A page above 1000 is 400 with code out_of_range; use cursors to go further.

Lists people reorder

Some numbered collections are sorted by default on a position that changes whenever someone drags a row in plnnrly, such as labels, sprints, subtasks and work types. If someone reorders the list between two of your requests, page numbers can skip a row or show one twice. The page parameter on those operation pages says so.

For a walk that cannot skip rows, send sort=id and follow links.next without page.

Totals

meta.page.total is the number of rows that match your query.

  • On collections that accept page, total is always a number. They do not accept with_total: sending it is 400 unsupported-parameter.
  • On every other collection, total is null unless you send with_total=1. Counting is the expensive part of those queries, so with_total=1 costs 2 more rate units: 3 in place of 1, or 5 together with view=detail. See Rate limits.

The operation page lists page or with_total, so you can see which kind a collection is before you call it.

Terminal
curl -G "https://<your-url>/api/v2/work-items" \
  -H "Authorization: Bearer <token>" \
  --data-urlencode "with_total=1"

Walking a whole collection

Follow links.next until it is null. This exports every work item you can read, 100 at a time. It uses jq to read the JSON:

Terminal
set -euo pipefail
url="https://<your-url>/api/v2/work-items?per_page=100&sort=id"
while [ -n "$url" ]; do
  body=$(curl -sS --fail-with-body "$url" -H "Authorization: Bearer <token>")
  echo "$body" | jq -c '.data[]'
  url=$(echo "$body" | jq -r '.links.next // empty')
done

The script stops on an error response. On a 429, wait as Retry-After says and request the same URL again; see Rate limits.

Sort by id for an export. It never changes, so a row cannot move between pages while you read. Sorting by updated_at moves a row every time someone saves it, which is what you want for a sync and not for an export.