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 レコードの制限に達している場合は、カーソルページネーションへの移行は簡単です。主な変更点は次のとおりです。
pageパラメーターをpage[size]に置き換えますnext_pageが null かどうかではなく、meta.has_moreを確認しますnext_pageの代わりに、次のページの URL にlinks.nextを使用します
eesel AI では、Zendesk と直接統合し、すべてのページネーションの複雑さを自動的に処理します。ただし、カスタム統合を構築する場合は、これらの違いを理解することが不可欠です。
カーソルページネーションの仕組み
カーソルページネーションは、毎回最初からレコードをカウントするのではなく、データセット内の位置を追跡するポインター(カーソル)を使用します。これにより、API がページを見つけるためにレコードをカウントしてスキップする必要がないため、大規模なデータセットでは大幅に高速になります。
カーソルページネーションを有効にするには、page[size] パラメーターをリクエストに追加します。これにより、カーソルモードを使用することと、ページごとに返すアイテム数(ほとんどのエンドポイントで最大 100)を指定することを Zendesk に伝えます。詳細については、Zendesk カーソルページネーションのドキュメント を参照してください。
レスポンスには、次の 2 つのキーオブジェクトが含まれています。
- meta:
has_more(ブール値)、after_cursor、およびbefore_cursorが含まれています - links: 隣接するページの
nextおよびprevURL が含まれています
一般的なレスポンスは次のようになります。
{
"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.nextURL を直接使用します - ループは
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
主な変更点は次のとおりです。
page[size]パラメーターを追加して、カーソルモードを有効にしますnext_pageが null かどうかではなく、meta.has_moreを確認しますnext_pageの代わりに、次のページの URL にlinks.nextを使用しますcountプロパティへの依存関係を削除します(カーソルページネーションでは使用できません)
eesel AI でページネーションの複雑さをスキップする
適切なページネーション、エラー処理、およびレート制限を備えたカスタム API 統合を構築するには、多大な開発努力が必要です。コードを記述し、Zendesk が API を更新する際にそれを維持し、これまで説明したすべてのエッジケースを処理する必要があります。
多くのチームにとって、より簡単な道があります。eesel AI は、Zendesk アカウント に直接接続し、すべてのデータ取得を自動的に処理します。ページネーションロジックを記述する代わりに、ビジュアルインターフェイスを通じて実行したいことを構成します。
仕組みは次のとおりです。
- 数分で eesel AI を Zendesk アカウントに接続します
- AI は、過去のチケット、ヘルプセンターの記事、およびマクロから学習します
- プレーンな英語で自動化ルールを定義します(コードは不要)
- eesel AI は、すべての API 呼び出し、ページネーション、およびデータ処理を処理します
チケットのルーティングを自動化したり、エージェントの応答を起草したり、受信チケットにタグを付けて優先順位を付けたり、レポートを生成したりできます。すべて、ページネーションコードを 1 行も記述せずに。
カスタム統合が必要なチームにとって、Zendesk API は依然として適切な選択肢です。ただし、ワークフローを自動化し、効率を向上させることが目標である場合は、eesel AI のようなツールを使用すると、より迅速に目標を達成できます。カスタムソリューションの構築と維持と比較して、当社の 価格 を確認してください。

よくある質問
この記事を共有

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.



