Zendesk API カーソルページネーション:完全な開発者ガイド

Stevia Putri
Written by

Stevia Putri

Reviewed by

Stanley Nicholas

Last edited 2026 3月 2

Expert Verified

Zendesk API カーソルページネーションのバナー画像:完全な開発者ガイド

Zendesk との連携を構築している場合、API がチケットデータベース全体を一度に渡さないことにすぐに気づくでしょう。代わりに、ページネーションと呼ばれるメカニズムを通じて、データを小さく分割して提供します。これは仕様によるものです。ページネーションは、API の応答性を維持し、数千(または数百万)のサポートチケットを処理する際のタイムアウトを防ぎます。

しかし、ここからが興味深いところです。Zendesk は、データをページネーションする 2 つの異なる方法を提供しています。カーソルページネーションとオフセットページネーションです。1 つは高速で、最新であり、事実上無制限です。もう 1 つは 10,000 レコードに制限されており、古い API 設計からのいくつかの遺産を引き継いでいます。2023 年 8 月以降、Zendesk はオフセットメソッドに厳格な制限を課すことで、開発者をカーソルページネーションに移行させています。

このガイドでは、動作するコード例を使用して、両方のページネーションアプローチについて説明します。各メソッドをいつ使用するか、エッジケースを処理する方法、および避けるべき落とし穴について学習します。また、ページネーションロジックを自分で記述せずに Zendesk データを操作する簡単な方法があるはずだと考えている場合は、それについても説明します。

Zendesk ページネーションアプローチの詳細については、Zendesk チケットをページネーションするための完全なガイド も役立つ場合があります。

大規模なデータセットを取得するためのリクエスト/レスポンスループを示すカーソルページネーションフローチャート
大規模なデータセットを取得するためのリクエスト/レスポンスループを示すカーソルページネーションフローチャート

Zendesk API でのページネーションの理解

コードに入る前に、ページネーションが存在する理由と、それが高度なレベルでどのように機能するかを明確にしましょう。

Zendesk API は、パフォーマンス上の理由から、大規模な結果セットをより小さなページに分割します。単一の API 呼び出しで 100,000 件のチケットをリクエストすると、ほとんどのシステムでタイムアウトまたはクラッシュが発生します。代わりに、ページ 1 をリクエストし、それらの結果を処理してから、ページ 2 をリクエストし、すべてを取得するまで続行します。

エンドポイントが異なると、デフォルトのページサイズも異なります。

  • チケットとユーザー: 1 ページあたり 100 アイテム
  • ヘルプセンターの記事: 1 ページあたり 30 アイテム
  • 検索結果: 最大 1 ページあたり 100 アイテム

通常、制限内でページサイズを調整できますが、特定のエンドポイントの最大値を超えることはできません。

すべての API リクエストは、レート制限に対してカウントされます。Zendesk では、プランに応じて 1 分あたりのリクエスト数が決まっており、大規模なデータセットを取得する場合、ページネーションによってそのクォータがすぐに消費される可能性があります。良いニュースは、カーソルページネーションの方がオフセットページネーションよりも効率的であるため、メソッドを切り替えることで、API の使用量を実際に削減できることです。

知っておくべき重要な変更点の 1 つは、2023 年 8 月 15 日以降、最初の 10,000 レコード(100 ページ)を超えるオフセットベースのページネーションリクエストは、400 Bad Request エラーを返すことです。10,000 件を超えるレコードが必要な場合、カーソルページネーションが唯一のオプションになります。この変更の詳細については、Zendesk オフセットページネーションのお知らせ を参照してください。

カーソルページネーション vs オフセットページネーション

これらの 2 つのアプローチの違いを分析して、ユースケースに適したものを選択できるようにしましょう。

特徴カーソルページネーションオフセットページネーション
レコード制限無制限最大 10,000
パフォーマンス一貫性がある(深いページでも高速)ページ番号が増加するにつれて低下する
利用可能な合計数いいえはい(count プロパティ)
任意のページへのジャンプ不可能可能
ページネーション中のデータの一貫性より良い重複/見逃しレコードが発生しやすい
Zendesk の推奨事項推奨レガシーサポートのみ

次の場合にカーソルページネーションを使用します。

  • 10,000 件を超えるレコードが必要な場合
  • パフォーマンスが重要な場合(特に大規模なデータセットの場合)
  • ジャンプする必要なく、データを順番に処理する場合

次の場合にオフセットページネーションを使用します。

  • レコードの合計数が必要な場合
  • ユーザーが特定のページにジャンプできる UI を構築する場合
  • 小さなデータセット(10,000 レコード未満)を操作しており、シンプルさが重要な場合

現在オフセットページネーションを使用しており、10,000 レコードの制限に達している場合は、カーソルページネーションへの移行は簡単です。主な変更点は次のとおりです。

  1. page パラメーターを page[size] に置き換えます
  2. next_page が null かどうかではなく、meta.has_more を確認します
  3. next_page の代わりに、次のページの URL に links.next を使用します

eesel AI では、Zendesk と直接統合し、すべてのページネーションの複雑さを自動的に処理します。ただし、カスタム統合を構築する場合は、これらの違いを理解することが不可欠です。

カーソルページネーションの仕組み

カーソルページネーションは、毎回最初からレコードをカウントするのではなく、データセット内の位置を追跡するポインター(カーソル)を使用します。これにより、API がページを見つけるためにレコードをカウントしてスキップする必要がないため、大規模なデータセットでは大幅に高速になります。

カーソルページネーションを有効にするには、page[size] パラメーターをリクエストに追加します。これにより、カーソルモードを使用することと、ページごとに返すアイテム数(ほとんどのエンドポイントで最大 100)を指定することを Zendesk に伝えます。詳細については、Zendesk カーソルページネーションのドキュメント を参照してください。

レスポンスには、次の 2 つのキーオブジェクトが含まれています。

  • meta: has_more(ブール値)、after_cursor、および before_cursor が含まれています
  • links: 隣接するページの next および prev URL が含まれています

一般的なレスポンスは次のようになります。

{
  "tickets": [
    { "id": 1, "subject": "Sample ticket" },
    { "id": 2, "subject": "Another ticket" }
  ],
  "meta": {
    "has_more": true,
    "after_cursor": "eyJvIjoibmljZV9pZCIsInYiOiJhV2tCQUFBQUFBQUEifQ==",
    "before_cursor": "eyJvIjoibmljZV9pZCIsInYiOiJhUzRCQUFBQUFBQUEifQ=="
  },
  "links": {
    "next": "https://example.zendesk.com/api/v2/tickets.json?page[size]=100&page[after]=eyJvIjoibmljZV9pZCIsInYiOiJhV2tCQUFBQUFBQUEifQ==",
    "prev": "https://example.zendesk.com/api/v2/tickets.json?page[size]=100&page[before]=eyJvIjoibmljZV9pZCIsInYiOiJhUzRCQUFBQUFBQUEifQ=="
  }
}

meta.has_more が false になるまでページネーションを続けます。その時点で、すべてのレコードを取得しました。

Python でのカーソルページネーションの実装

Python と requests ライブラリを使用した完全な動作例を次に示します。

import requests
import time

ZENDESK_SUBDOMAIN = 'your-subdomain'
ZENDESK_EMAIL = 'your-email@example.com'
ZENDESK_API_TOKEN = 'your-api-token'

BASE_URL = f'https://{ZENDESK_SUBDOMAIN}.zendesk.com/api/v2/tickets.json'
auth = (f'{ZENDESK_EMAIL}/token', ZENDESK_API_TOKEN)

def fetch_all_tickets():
    tickets = []
    url = BASE_URL
    params = {'page[size]': 100}  # Use cursor pagination with 100 items per page

    while url:
        response = requests.get(url, auth=auth, params=params)

        # Handle rate limiting
        if response.status_code == 429:
            retry_after = int(response.headers.get('Retry-After', 60))
            print(f'Rate limited. Waiting {retry_after} seconds...')
            time.sleep(retry_after)
            continue

        response.raise_for_status()
        data = response.json()

        # Process tickets from this page
        for ticket in data['tickets']:
            tickets.append(ticket)
            print(f"Fetched ticket {ticket['id']}: {ticket['subject']}")

        # Check if there are more pages
        if data['meta']['has_more']:
            url = data['links']['next']
            params = None  # Next URL already includes all parameters
        else:
            url = None

    return tickets

if __name__ == '__main__':
    all_tickets = fetch_all_tickets()
    print(f'\nTotal tickets fetched: {len(all_tickets)}')

この例で注目すべき点は次のとおりです。

  • page[size]=100 で開始して、カーソルページネーションを有効にします
  • レート制限(HTTP 429)を確認し、Retry-After ヘッダーを尊重します
  • 最初の要求の後、後続のページで links.next URL を直接使用します
  • ループは has_more が false になるまで続行されます

Node.js でのカーソルページネーションの実装

axios を使用した Node.js での同じロジックを次に示します。

const axios = require('axios');

const ZENDESK_SUBDOMAIN = 'your-subdomain';
const ZENDESK_EMAIL = 'your-email@example.com';
const ZENDESK_API_TOKEN = 'your-api-token';

const BASE_URL = `https://${ZENDESK_SUBDOMAIN}.zendesk.com/api/v2/tickets.json`;
const auth = Buffer.from(`${ZENDESK_EMAIL}/token:${ZENDESK_API_TOKEN}`).toString('base64');

async function fetchAllTickets() {
  const tickets = [];
  let url = BASE_URL;
  let params = { 'page[size]': 100 };

  try {
    do {
      const response = await axios.get(url, {
        headers: { Authorization: `Basic ${auth}` },
        params: params
      });

      const data = response.data;

      // Process tickets from this page
      for (const ticket of data.tickets) {
        tickets.push(ticket);
        console.log(`Fetched ticket ${ticket.id}: ${ticket.subject}`);
      }

      // Prepare for next page
      if (data.meta.has_more) {
        url = data.links.next;
        params = null; // Next URL includes all parameters
      } else {
        url = null;
      }

    } while (url);

    console.log(`\nTotal tickets fetched: ${tickets.length}`);
    return tickets;

  } catch (error) {
    if (error.response && error.response.status === 429) {
      const retryAfter = error.response.headers['retry-after'] || 60;
      console.log(`Rate limited. Retry after ${retryAfter} seconds`);
    } else {
      console.error('Error fetching tickets:', error.message);
    }
    throw error;
  }
}

fetchAllTickets();

一般的な落とし穴とその回避方法

動作するコードがあっても、本番環境ではエッジケースが発生します。最も一般的な問題の処理方法を次に示します。

レート制限の処理

レート制限を超えると、Zendesk は HTTP 429 を返します。レスポンスには、待機する秒数を示す Retry-After ヘッダーが含まれています。API を叩きつけるのではなく、常に指数バックオフを実装してください。

import time

def make_request_with_backoff(url, auth, max_retries=5):
    for attempt in range(max_retries):
        response = requests.get(url, auth=auth)

        if response.status_code == 429:
            retry_after = int(response.headers.get('Retry-After', 60))
            # Exponential backoff: wait longer with each retry
            wait_time = retry_after * (2 ** attempt)
            time.sleep(wait_time)
            continue

        return response

    raise Exception('Max retries exceeded')

空のページの処理

has_more が true であっても、次のリクエストがレコードを返さない空のページが発生する場合があります。これは、前のページの最後のレコードがデータセット全体の最後のレコードであった場合に発生する可能性があります。この場合、以前の after_cursor 値を将来の使用のために保存します。

カーソルの有効期限

Export Search Results エンドポイントの場合、カーソルは 1 時間後に期限切れになります。処理にそれ以上の時間がかかる場合は、エクスポートを再開するか、データをより迅速に処理する必要があります。

Search API の制限事項

Search API には、独自のページネーションの癖があります。

  • クエリあたりの最大結果数は合計 1,000 件
  • 1 ページあたりの最大結果数は 100 件
  • オフセットページネーションのみを使用
  • ページ 11(1 ページあたり 100 件の結果)をリクエストすると、422 エラーが返されます

1,000 件を超える検索結果が必要な場合は、代わりに Export Search Results エンドポイントを使用してください。このエンドポイントはカーソルページネーションをサポートし、1 ページあたり最大 1,000 件のレコードを返します。詳細については、Zendesk Search API のドキュメント を参照してください。

ページネーション中のデータの一貫性

ページネーションされたデータは、データのリアルタイムな性質のために不正確になる可能性があります。1 つまたは複数のアイテムが、リクエスト間でデータベースインスタンスから追加または削除される可能性があります。ページネーションの不正確さを軽減する方法の 1 つ(ただし、完全に排除するわけではありません)は、リソースでサポートされている場合、id や作成日など、変更できない属性でレコードをソートすることです。

eesel AI では、当社の AI Agent がこれらの複雑さを自動的に処理します。レート制限、カーソルの有効期限、およびデータの一貫性の問題を管理するため、自分でそのロジックを記述する必要はありません。

オフセットからカーソルページネーションへの移行

現在オフセットページネーションを使用している場合は、主な変更点を示す簡単な移行ガイドを次に示します。

移行前(オフセットページネーション):

url = f'https://{subdomain}.zendesk.com/api/v2/tickets.json'
while url:
    response = requests.get(url, auth=auth)
    data = response.json()
    # Process tickets...
    url = data['next_page']  # null when done

移行後(カーソルページネーション):

url = f'https://{subdomain}.zendesk.com/api/v2/tickets.json?page[size]=100'
params = {'page[size]': 100}
while url:
    response = requests.get(url, auth=auth, params=params)
    data = response.json()
    # Process tickets...
    if data['meta']['has_more']:
        url = data['links']['next']
        params = None
    else:
        url = None

主な変更点は次のとおりです。

  1. page[size] パラメーターを追加して、カーソルモードを有効にします
  2. next_page が null かどうかではなく、meta.has_more を確認します
  3. next_page の代わりに、次のページの URL に links.next を使用します
  4. count プロパティへの依存関係を削除します(カーソルページネーションでは使用できません)

eesel AI でページネーションの複雑さをスキップする

適切なページネーション、エラー処理、およびレート制限を備えたカスタム API 統合を構築するには、多大な開発努力が必要です。コードを記述し、Zendesk が API を更新する際にそれを維持し、これまで説明したすべてのエッジケースを処理する必要があります。

多くのチームにとって、より簡単な道があります。eesel AI は、Zendesk アカウント に直接接続し、すべてのデータ取得を自動的に処理します。ページネーションロジックを記述する代わりに、ビジュアルインターフェイスを通じて実行したいことを構成します。

仕組みは次のとおりです。

  • 数分で eesel AI を Zendesk アカウントに接続します
  • AI は、過去のチケット、ヘルプセンターの記事、およびマクロから学習します
  • プレーンな英語で自動化ルールを定義します(コードは不要)
  • eesel AI は、すべての API 呼び出し、ページネーション、およびデータ処理を処理します

チケットのルーティングを自動化したり、エージェントの応答を起草したり、受信チケットにタグを付けて優先順位を付けたり、レポートを生成したりできます。すべて、ページネーションコードを 1 行も記述せずに。

カスタム統合が必要なチームにとって、Zendesk API は依然として適切な選択肢です。ただし、ワークフローを自動化し、効率を向上させることが目標である場合は、eesel AI のようなツールを使用すると、より迅速に目標を達成できます。カスタムソリューションの構築と維持と比較して、当社の 価格 を確認してください。

透明性のある一般公開されているコストを示す eesel AI の価格ページ
透明性のある一般公開されているコストを示す eesel AI の価格ページ

よくある質問

カーソルページネーションでは、ハードリミットはありません。チケットデータベース全体を取得できます。オフセットページネーションでは、10,000 レコード(1 ページあたり 100 レコードで 100 ページ)に制限されます。
HTTP 429 レスポンスに注意し、Retry-After ヘッダーを尊重してください。レート制限に繰り返し達するのを避けるため、指数バックオフを実装してください。試行ごとにリトライの間隔を長くしてください。
ほとんどのリストエンドポイントは、チケット、ユーザー、組織など、カーソルページネーションをサポートしています。確認するには、特定のエンドポイントドキュメントを確認してください。カーソルページネーションについて言及されていない場合、そのエンドポイントはオフセットページネーションのみをサポートしている可能性があります。
カーソルページネーションのレスポンスには、総数は含まれていません。レコードの総数が必要な場合は、カウントエンドポイント(/api/v2/tickets/count.json など)への別の呼び出しを行うことができます。
はい、カーソルページネーションとオフセットページネーションは、ユーザー、組織、チケットなど、ほとんどの Zendesk API エンドポイントで一貫して機能します。同じ page[size] パラメーターにより、サポートされているすべてのエンドポイントでカーソルページネーションが有効になります。
標準の Search API はオフセットページネーションのみを使用し、最大 1,000 件の結果を返します。より大きな結果セットの場合は、カーソルページネーションをサポートする Export Search Results エンドポイントを使用してください。

この記事を共有

Stevia undefined

Article by

Stevia Putri

Stevia Putri is a marketing generalist at eesel AI, where she helps turn powerful AI tools into stories that resonate. She’s driven by curiosity, clarity, and the human side of technology.