Pagination
List endpoints page by page number. The request is the same almost everywhere; the response envelope comes in two shapes, so read the field names on the endpoint's own page.
Responses from the original MagicDoor application call it currentPage. Responses from the newer services call it page. Both carry the same value.
This is a scar from moving the API out of one application into several, and it is the single most likely thing to break a client that assumes one shape. Every endpoint page in the reference shows its own response fields — trust those over this page.
Almost every list endpoint takes these two query parameters.
pageintegerWhich page to return, starting at 1 rather than 0.
pageSizeintegerHow many items to return, between 1 and 100. The default is usually 50 — each endpoint page states its own.
The HOA architectural-request and survey endpoints are the exception: they name the parameter currentPage, default pageSize to 20, and do not enforce the 1–100 range.
curl --request GET \
--url 'https://api.portal.magicdoor.com/company-app/properties?page=2&pageSize=50' \
--header "Authorization: Bearer $MAGICDOOR_TOKEN"Same five values either way — only the name of the page-number field differs.
{
"items": [],
"currentPage": 2,
"totalPages": 3,
"pageSize": 50,
"totalCount": 137
}itemsT[]requiredThis page of results. The element shape is documented on each endpoint's own page.
totalCountintegerrequiredHow many records match across every page, not just this one.
totalPagesintegerHow many pages there are at the pageSize you asked for. A few endpoints omit it — the HOA lists do.
pageintegerThe page you are looking at, on the newer services. Echoed back so a response is self-describing.
currentPageintegerThe same value, on endpoints served by the original application — anything under /company-app.
pageSizeintegerrequiredThe page size that was applied, which is not always the one you asked for.
A few endpoints return every matching record in one response, with totalCount simply equal to the number of items — the insurance lists and HOA board members among them. Their pages say so.
Stop when you have seen totalCount records. Counting records rather than comparing page numbers works against both envelopes and against the endpoints that omit totalPages.
async function* everyProperty(token: string) {
let page = 1;
let seen = 0;
let total = Infinity;
while (seen < total) {
const url = new URL("https://api.portal.magicdoor.com/company-app/properties");
url.searchParams.set("page", String(page));
url.searchParams.set("pageSize", "100");
const response = await fetch(url, {
headers: { Authorization: `Bearer ${token}` },
});
if (!response.ok) throw new Error(`Listing failed: ${response.status}`);
const body = await response.json();
total = body.totalCount;
// An empty page means the data shifted under us; stop rather than loop forever.
if (body.items.length === 0) return;
seen += body.items.length;
yield* body.items;
page += 1;
}
}Paging is by offset, so anything created or deleted between two requests moves the records after it. A new record on page 1 pushes one record from page 1 onto page 2 — and if you have already read page 1, you will see that record twice; a deletion can make you miss one entirely.
For a full export, sweep quickly at pageSize=100, and de-duplicate by id rather than assuming each record appears once. If you only need what changed, prefer an endpoint that filters by date over walking everything.
Most endpoints allow 100 requests per minute — see rate limits. At pageSize=100 that is 10,000 records a minute from one endpoint, which is usually plenty. If you do hit 429, wait for the number of seconds in retry_after rather than retrying straight away.
There is no cursor, nextCursor, or hasMore anywhere in this API. If you are porting from an API that has them, page numbers are the whole story here.