Kolide API Pagination

How to use Kolide's API pagination

Pagination

The Kolide API uses a cursor-based pagination strategy.

On any endpoint that returns a list or collection of entities, you may supply a
cursor url query parameter to fetch records beyond the first page of results:

curl --request GET \
  --url "https://k2.kolide.com/api/v0/audit_logs?per_page=25" \
  --header 'accept: application/json'

# ->
# {
#  pagination: {
#    next: "https://api.kolide.com/audit_logs?per_page=25&cursor=ABCDJ",
#    next_cursor: "ABCDJ",
#    current_cursor: "JDCBA",
#    count: 25,
#  }
#  data: [...]
#}

A request with a blank cursor parameter will return the first page of results.
The response will include a pagination section that includes the cursor for the
next page of results. (For your convenience, we also include a next: link that
is a pre-built URL with the next cursor already specified). See the example
above.

The default page length is 25 records, but you can include a per_page query
parameter to specify a different page length between 1 and 100.

If the values for cursor and next are blank, that indicates there are no more
records to display - You've reached the last page!

Code Examples

Here are a couple more simple examples of API clients iterating through a list of records:

require 'uri'
require 'net/http'
require 'openssl'
require 'json'

# Get your API key from https://app.kolide.com/x/settings/admin/developers/api_keys
key = ENV.fetch('APIKEY', "")
next_url = "https://api.kolide.com/audit_logs?per_page=25&cursor="
more_records = true
results = []

while more_records do
  url = URI(next_url)
  http = Net::HTTP.new(url.host, url.port)
  http.use_ssl = true

  request = Net::HTTP::Get.new(url)
  request["accept"] = 'application/json'
  request["Authorization"] = "Bearer #{key}"
  request["X-Kolide-API-Version"] = "2023-05-26"

  response = http.request(request)

  case response
  when Net::HTTPSuccess then
    resp = JSON.parse(response.read_body)
    next_url = resp["pagination"]["next"]
    results += resp["data"]
    more_records = next_url.to_s != ""
  else
    more_records = false
    puts "Unexpected response code: #{ response.code }"
  end
end

puts results
const https = require('https')

// Get your API key from https://app.kolide.com/x/settings/admin/developers/api_keys
const api_key = process.env.APIKEY

function fetchRecords(url) {
  return new Promise((resolve, reject) => {
    let options = {
      method: 'GET',
      headers: {
        accept: 'application/json',
        authorization: 'Bearer ' + api_key,
        "x-kolide-api-version": "2023-05-26",
      }
    };

    const req = https.request(url, options, (res) => {
      let data = '';
      res.on('data', (chunk) => {
        data += chunk;
      });

      res.on('end', () => {
        if (res.statusCode !== 200) {
          reject('Error response: ' + res.statusCode);
        } else {
          resolve(data);
        }
      });
    });
    req.on('error', (error) => {
      reject(error);
    });

    req.end();
  });
}

async function getAllRecords() {
  let moreRecords = true;
  let url = "https://api.kolide.com/devices?cursor=";
  let records = [];

  while (moreRecords == true) {
    try {
      let response = await fetchRecords(url);
      let resp = JSON.parse(response);
      records = records.concat(resp["data"]);
      let nextUrl = resp["pagination"]["next"];
      if(nextUrl != "" && nextUrl != null) {
        moreRecords = true;
        url = nextUrl;
      } else {
        moreRecords = false;
      }
    } catch (error) {
      console.error('ERROR:');
      console.error(error);
      moreRecords = false;
    }
  }

  console.log(records);
}

getAllRecords()