Handle rate limits and pagination

Coris API rate limits, concurrency limits, 429 handling, and offset and cursor pagination for list endpoints.

The standard limit is 1,000 requests per minute, per user. Page through lists with limit and offset, or with cursors on the accounts list.

  1. StartGET /accounts/?limit=50&sort_by=created&sort_order=desc
  2. ReturnsPage of accounts
  3. CheckMore?
  • YesGET /accounts/?limit=50&sort_by=created&sort_order=desc&after_id=<last id>
  • NoDone

Over limit: 429 + Retry-After.

Page through a list

  1. Set your base URL and API key.
export CORIS_BASE_URL="https://sandbox-api.coris.ai/api/v0_2"
export CORIS_API_KEY="YOUR_API_KEY"
import requests

BASE_URL = "https://sandbox-api.coris.ai/api/v0_2"
HEADERS = {"Authorization": "Token YOUR_API_KEY"}
const BASE_URL = "https://sandbox-api.coris.ai/api/v0_2";
const HEADERS = {
  Authorization: "Token YOUR_API_KEY",
  "Content-Type": "application/json",
};
  1. Fetch a page with totals. With metadata, the body is an object, not a plain array.
curl -s "$CORIS_BASE_URL/accounts/?limit=50&return_pagination_metadata=true" \
  -H "Authorization: Token $CORIS_API_KEY" | jq '.pagination_metadata.total'
page = requests.get(
    f"{BASE_URL}/accounts/",
    params={"limit": 50, "return_pagination_metadata": "true"},
    headers=HEADERS,
).json()
print(page["pagination_metadata"]["total"], len(page["data"]))
const pageResponse = await fetch(
  `${BASE_URL}/accounts/?limit=50&return_pagination_metadata=true`,
  { headers: HEADERS },
);
const page = await pageResponse.json();
console.log(page.pagination_metadata.total, page.data.length);

Try GET /accounts/ in the API explorer.

{
  "pagination_metadata": { "total": 142 },
  "data": [
    { "id": "c47f0e21-6a9b-4d38-a2e5-1b8d3f90c6a7", "reference_id": "acme-plumbing-001" }
  ]
}

pagination_metadata.total counts the items matching your filters, and can change during writes.

  1. Walk the list with cursors, which suit large exports. Send sort_by=created&sort_order=desc on every request, and use the last id on each page as after_id.
AFTER=""
while : ; do
  PAGE=$(curl -s "$CORIS_BASE_URL/accounts/?limit=50&sort_by=created&sort_order=desc${AFTER:+&after_id=$AFTER}" \
    -H "Authorization: Token $CORIS_API_KEY")
  [ "$(printf '%s' "$PAGE" | jq 'length')" = "0" ] && break
  printf '%s' "$PAGE" | jq -r '.[].id'
  AFTER=$(printf '%s' "$PAGE" | jq -r '.[-1].id')
done
accounts, after_id = [], None

while True:
    params = {"limit": 50, "sort_by": "created", "sort_order": "desc"}
    if after_id:
        params["after_id"] = after_id
    rows = requests.get(f"{BASE_URL}/accounts/", params=params, headers=HEADERS).json()
    if not rows:
        break
    accounts.extend(rows)
    after_id = rows[-1]["id"]

print(len(accounts))
const accounts = [];
let afterId = null;

while (true) {
  const query = new URLSearchParams({ limit: "50", sort_by: "created", sort_order: "desc" });
  if (afterId) query.set("after_id", afterId);
  const rows = await (await fetch(`${BASE_URL}/accounts/?${query}`, { headers: HEADERS })).json();
  if (rows.length === 0) break;
  accounts.push(...rows);
  afterId = rows[rows.length - 1].id;
}
console.log(accounts.length);

Cursor rules:

  • A first page with no sort_by has no set order, so rows repeat or go missing.
  • before_id, after_id and offset cannot be combined.
  • An invalid cursor returns 400.

Pagination parameters

ParameterMeaningDefault
limitItems per page.10
offsetItems to skip.0
afterItems created after this epoch time.None
beforeItems created before this epoch time.None
return_pagination_metadataSend any value, even false, to get the object above. Leave it out to get a plain array.Off

These lists ignore limit and return every row: /account_tags/, /settings/custom_list/, /accounts/{account_id}/persons/, /accounts/{account_id}/websites/ and /accounts/{account_id}/metadata/.

Rate limits

LimitValueApplies to
Authenticated requests1,000 per minute, per userEndpoints using the standard throttle
Concurrent synchronous data requests25 by default, per workspaceGET requests that fetch data live

POST requests and GET requests with request_id skip the concurrency limit, so use the async flow for bulk work.

What to do next

SituationAction
429 with Retry-AfterWait that many seconds, then retry.
429 with no Retry-AfterYou hit the concurrency limit. Back off for a few seconds and reduce parallel calls.
Your workload needs more concurrencyAsk for a higher limit.

FAQ

What is the maximum limit?

Most lists enforce no maximum, but stay near 100 so that responses stay fast.

Do all lists support cursors?

No. Only the accounts list uses them. Other lists use offset.

Should I fetch pages in parallel?

No. Fetch them in order, which also keeps your results consistent.

Next: Track API versions


Did this page help you?