カーソルAPI

にメンテナンス済み

kintone の REST API でレコードを複数件取得する場合、通常の records.json エンドポイントでは offset の上限が 10,000 件 に制限されています。そのため、10,000 件を超えるレコードを扱うアプリでは、通常のクエリだけではすべてのレコードを取得できません。

この問題を解決するのがカーソル API です。カーソル API を使えば、offset の制限を気にすることなく、大量のレコードを安全かつ効率的に取得できます。

この記事では、カーソル API の基本的な使い方から、全件取得の汎用関数、運用上の注意点まで解説します。

レコードの取得(複数件)
kintone REST APIを使用して、レコードを複数件取得する方法を紹介します。レコードIDを指定して、レコード情報を取得することができます。
レコードIDを使った一括取得
kintoneにはAPIを使ってレコード情報を取得する方法が複数用意されています。今回はその中でもシーク法と呼ばれる、レコードIDをもとに一括取得する方法を紹介します。

カーソル API の概要

カーソル API は、以下の 3 つのエンドポイントで構成されています。

エンドポイントメソッド説明
/k/v1/records/cursor.jsonPOSTカーソルを作成する
/k/v1/records/cursor.jsonGETカーソルからレコードを取得する
/k/v1/records/cursor.jsonDELETEカーソルを削除する

処理の流れ

  1. カーソルを作成する — 取得条件(アプリ ID、クエリ、フィールド)を指定してカーソルを作成
  2. レコードを取得する — カーソル ID を指定して、500 件ずつレコードを取得(next: true の間は繰り返す)
  3. カーソルを削除する — 全件取得後、カーソルを削除してリソースを解放
自動削除について

すべてのレコードを取得し終えた時点(next: false が返された時点)でカーソルは自動的に削除されます。途中でカーソルの使用を中断する場合のみ、手動で削除する必要があります。

kintone から利用する場合

カーソルの作成

create-cursor.js
/**
 * カーソルを作成する
 * @param { Object } params
 * @param { string | number } params.app - アプリID
 * @param { string[] } [params.fields] - 取得するフィールドコード
 * @param { string } [params.query] - クエリ条件
 * @param { number } [params.size] - 1回の取得件数(最大500、デフォルト100)
 * @returns { Promise<{ id: string; totalCount: string }> } カーソルIDと総件数
 */
const createCursor = (params) => {
  const { app, fields, query, size } = params;
  return kintone.api(kintone.api.url('/k/v1/records/cursor.json', true), 'POST', {
    app,
    fields,
    query,
    size: size || 500,
  });
};
チェック

size パラメータには 1〜500 の値を指定できます。パフォーマンスを最大化するために、500 を指定することをおすすめします。

カーソルからレコードを取得

get-cursor-records.js
/**
 * カーソルからレコードを取得する
 * @param { Object } params
 * @param { string } params.id - カーソルID
 * @returns { Promise<{ records: Record<string, any>[]; next: boolean }> } レコードと次ページ有無
 */
const getCursorRecords = (params) => {
  return kintone.api(kintone.api.url('/k/v1/records/cursor.json', true), 'GET', {
    id: params.id,
  });
};

カーソルの削除

delete-cursor.js
/**
 * カーソルを削除する
 * @param { Object } params
 * @param { string } params.id - カーソルID
 * @returns { Promise<{}> }
 */
const deleteCursor = (params) => {
  return kintone.api(kintone.api.url('/k/v1/records/cursor.json', true), 'DELETE', {
    id: params.id,
  });
};

全件取得の汎用関数

カーソルの作成からレコード取得、カーソルの削除までを一貫して行う汎用関数を定義すると便利です。

get-all-records-with-cursor.js
/**
 * カーソル API を使用して、条件に一致する全レコードを取得する
 * @param { Object } params
 * @param { string | number } params.app - アプリID
 * @param { string[] } [params.fields] - 取得するフィールドコード
 * @param { string } [params.query] - クエリ条件(order by, limit, offset は指定不要)
 * @returns { Promise<Record<string, any>[]> } 全レコード
 */
const getAllRecordsWithCursor = async (params) => {
  const { app, fields, query } = params;

  // Step 1: カーソルを作成
  const cursor = await kintone.api(kintone.api.url('/k/v1/records/cursor.json', true), 'POST', {
    app,
    fields,
    query,
    size: 500,
  });

  const allRecords = [];

  try {
    // Step 2: レコードを繰り返し取得
    let hasNext = true;
    while (hasNext) {
      const result = await kintone.api(
        kintone.api.url('/k/v1/records/cursor.json', true),
        'GET',
        { id: cursor.id }
      );
      allRecords.push(...result.records);
      hasNext = result.next;
    }
  } catch (error) {
    // Step 3: エラー時はカーソルを削除してリソースを解放
    await kintone.api(kintone.api.url('/k/v1/records/cursor.json', true), 'DELETE', {
      id: cursor.id,
    });
    throw error;
  }

  return allRecords;
};

使用例

usage-example.js
(() => {
  'use strict';

  kintone.events.on(['app.record.index.show'], async (event) => {
    const appId = kintone.app.getId();

    // 全件取得
    const allRecords = await getAllRecordsWithCursor({ app: appId });
    console.log(`全 ${allRecords.length} 件取得しました`);

    // クエリ付きで全件取得
    const filteredRecords = await getAllRecordsWithCursor({
      app: appId,
      query: '契約状況 in ("契約中")',
      fields: ['会社名', '契約状況', '契約日'],
    });
    console.log(`条件に一致: ${filteredRecords.length} 件`);

    return event;
  });
})();
クエリの指定について

カーソル API の query パラメータでは、order bylimitoffset を指定できません。これらはカーソル API が内部的に管理するため、絞り込み条件のみを指定してください。

kintone 以外から利用する場合

Node.js を使用した場合

nodejs-cursor.js
const BASE_URL = 'https://__your_subdomain__.cybozu.com';
const API_TOKEN = 'YOUR_API_TOKEN';

/**
 * カーソル API を使用して全レコードを取得する
 * @param { Object } params
 * @param { string | number } params.app - アプリID
 * @param { string[] } [params.fields] - 取得するフィールドコード
 * @param { string } [params.query] - クエリ条件
 * @returns { Promise<Record<string, any>[]> } 全レコード
 */
const getAllRecordsWithCursor = async (params) => {
  const { app, fields, query } = params;

  // カーソルを作成
  const createRes = await fetch(`${BASE_URL}/k/v1/records/cursor.json`, {
    method: 'POST',
    headers: {
      'X-Cybozu-API-Token': API_TOKEN,
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({ app, fields, query, size: 500 }),
  });
  const cursor = await createRes.json();

  const allRecords = [];

  try {
    let hasNext = true;
    while (hasNext) {
      const getRes = await fetch(`${BASE_URL}/k/v1/records/cursor.json?id=${cursor.id}`, {
        method: 'GET',
        headers: { 'X-Cybozu-API-Token': API_TOKEN },
      });
      const result = await getRes.json();
      allRecords.push(...result.records);
      hasNext = result.next;
    }
  } catch (error) {
    // エラー時にカーソルを削除
    await fetch(`${BASE_URL}/k/v1/records/cursor.json`, {
      method: 'DELETE',
      headers: {
        'X-Cybozu-API-Token': API_TOKEN,
        'Content-Type': 'application/json',
      },
      body: JSON.stringify({ id: cursor.id }),
    });
    throw error;
  }

  return allRecords;
};

Python を使用した場合

cursor_api.py
import requests

BASE_URL = 'https://__your_subdomain__.cybozu.com'
API_TOKEN = 'YOUR_API_TOKEN'
HEADERS = {
    'X-Cybozu-API-Token': API_TOKEN,
    'Content-Type': 'application/json',
}

def get_all_records_with_cursor(app, query='', fields=None):
    """カーソル API を使用して全レコードを取得する"""

    # カーソルを作成
    body = {'app': app, 'size': 500}
    if query:
        body['query'] = query
    if fields:
        body['fields'] = fields

    create_res = requests.post(
        f'{BASE_URL}/k/v1/records/cursor.json',
        headers=HEADERS,
        json=body,
    )
    cursor = create_res.json()

    all_records = []

    try:
        has_next = True
        while has_next:
            get_res = requests.get(
                f'{BASE_URL}/k/v1/records/cursor.json',
                headers=HEADERS,
                params={'id': cursor['id']},
            )
            result = get_res.json()
            all_records.extend(result['records'])
            has_next = result['next']
    except Exception as e:
        # エラー時にカーソルを削除
        requests.delete(
            f'{BASE_URL}/k/v1/records/cursor.json',
            headers=HEADERS,
            json={'id': cursor['id']},
        )
        raise e

    return all_records

カーソル API の制約と注意点

カーソル API を利用する際には、いくつかの重要な制約を把握しておく必要があります。

同時カーソル数の上限

項目制限値
ドメインあたりの同時カーソル数10 個
1 カーソルあたりの有効期間10 分
1 回の取得レコード数(最大)500 件
同時カーソル数に注意

ドメイン全体で同時に使用できるカーソルは 10 個 までです。上限を超えると新しいカーソルを作成できなくなります。カーソルの使用が終わったら、速やかに削除するか、全件取得して自動削除させてください。

カーソルの有効期間

カーソルは作成後 10 分間 有効です。10 分以内に取得を完了できない場合、カーソルは無効になります。

大量のレコードを処理しながら取得する場合は、取得後にまとめて処理するようにしてください。

推奨パターン
// ✅ 推奨: まず全件取得してから処理する
const allRecords = await getAllRecordsWithCursor({ app: appId });
for (const record of allRecords) {
  // 各レコードに対する処理
}

// ❌ 非推奨: 取得しながら重い処理を行う(タイムアウトのリスク)

カーソル API とシーク法の比較

比較項目カーソル APIシーク法(レコード ID 昇順取得)
実装の容易さ高いやや複雑
offset 上限の回避
ドメイン全体の同時実行数制限あり(10 個)なし
有効期限あり(10 分)なし
ソート順の指定query で order by 指定不可$id 昇順のみ
途中からの再開できない最後の ID を記録すれば可能
チェック

同時実行数や有効期限の制約が問題になる場合は、シーク法(レコード ID を使った一括取得)の利用を検討してください。

レコードIDを使った一括取得
kintoneにはAPIを使ってレコード情報を取得する方法が複数用意されています。今回はその中でもシーク法と呼ばれる、レコードIDをもとに一括取得する方法を紹介します。

まとめ

  • カーソル API は、records.jsonoffset 上限(10,000 件)を超えるレコード を取得するための手段
  • カーソルの作成 → 繰り返し取得 → 削除(または自動削除)の 3 ステップで利用する
  • size パラメータは最大 500 を指定してパフォーマンスを最大化する
  • ドメインあたりの同時カーソル数は 10 個 まで。不要なカーソルは速やかに削除する
  • カーソルの有効期間は 10 分 。取得中に重い処理を挟まないよう注意する
  • query パラメータに order bylimitoffset は指定できない

練習問題

カーソル API で 1 回のリクエストで取得できるレコード数の最大値はいくつですか?

カーソル API の query パラメータで指定できないものはどれですか?
ドメインあたりの同時カーソル数の上限はいくつですか?
#kintone #JavaScript #TypeScript #REST API