メインコンテンツへスキップ
バージョン: 最新版

ページネーション

Omise APIページネーションを使用して、大きな結果セットを効率的にナビゲートします。limitとoffsetパラメータを使用して、管理可能なチャンクでデータを取得する方法を学びます。

概要

多くのOmise APIエンドポイントは、リソースのリスト(チャージ、顧客、送金など)を返します。レスポンスを高速で管理しやすく保つために、これらのエンドポイントはページネーションされた結果を返します。リクエストごとに取得するアイテムの数とフェッチするページを制御できます。

クイックスタート
  • limitを使用してページあたりのアイテム数を制御します(デフォルト: 20、最大: 100)
  • offsetを使用してアイテムをスキップし、ページをナビゲートします
  • totalを確認して、存在するアイテムの数を把握します
  • すべてのリストレスポンスは同じ構造を持っています

ページネーションパラメータ

limit

型: 整数 デフォルト: 20 範囲: 1-100 目的: リクエストごとに返すアイテム数

# Get 50 charges per request
curl https://api.omise.co/charges?limit=50 \
-u skey_test_5xuy4w91xqz7d1w9u0t:

offset

型: 整数 デフォルト: 0 目的: 結果を返し始める前にスキップするアイテム数

# Skip first 20 charges, return next 20
curl https://api.omise.co/charges?offset=20&limit=20 \
-u skey_test_5xuy4w91xqz7d1w9u0t:

Combined 例

# Get items 41-60 (page 3 with 20 items per page)
curl https://api.omise.co/charges?offset=40&limit=20 \
-u skey_test_5xuy4w91xqz7d1w9u0t:

リストレスポンス形式

すべてのページネーションされたエンドポイントは、一貫したリスト構造を返します:

{
"object": "list",
"data": [
{
"object": "charge",
"id": "chrg_test_5xuy4w91xqz7d1w9u0t",
"amount": 100000,
...
},
{
"object": "charge",
"id": "chrg_test_5xuy4w91xqz7d1w9u0a",
"amount": 50000,
...
}
],
"limit": 20,
"offset": 0,
"total": 142,
"from": "2025-01-01T00:00:00Z",
"to": "2025-02-07T23:59:59Z",
"order": "chronological",
"location": "/charges"
}

リストオブジェクトフィールド

フィールド説明
objectstringページネーションレスポンスの場合は常に"list"
dataarrayリソースオブジェクトの配列(チャージ、顧客など)
limitintegerページあたりのアイテム数(リクエストから)
offsetintegerスキップされたアイテム数(リクエストから)
totalintegerすべてのページにわたるアイテムの総数
fromstring (ISO 8601)クエリ期間の開始日(オプション)
tostring (ISO 8601)クエリ期間の終了日(オプション)
orderstringソート順序: "chronological"または"reverse_chronological"
locationstringAPIエンドポイントパス

ページネーションをサポートするエンドポイント

以下のエンドポイントはページネーションをサポートしています:

コアリソース

エンドポイントデフォルトの順序説明
GET /charges逆時系列すべてのチャージ
GET /customers逆時系列すべての顧客
GET /transfers逆時系列すべての送金
GET /refunds逆時系列すべての返金
GET /transactions逆時系列すべての取引
GET /disputes逆時系列すべての紛争
GET /recipients逆時系列すべての受取人
GET /events逆時系列すべてのイベント
GET /schedules逆時系列すべてのスケジュール
GET /links逆時系列すべての決済リンク

ネストされたリソース

エンドポイント説明
GET /customers/:id/cards顧客のカードをリスト
GET /charges/:id/refundsチャージの返金をリスト
GET /customers/:id/charges顧客のチャージをリスト

基本的なページネーションの例

デフォルトのページネーション

最初の20アイテムを取得します(デフォルトの動作):

curl https://api.omise.co/charges \
-u skey_test_5xuy4w91xqz7d1w9u0t:

レスポンス:

{
"object": "list",
"data": [...],
"limit": 20,
"offset": 0,
"total": 142
}

カスタムページサイズ

ページあたり50アイテムを取得:

curl https://api.omise.co/charges?limit=50 \
-u skey_test_5xuy4w91xqz7d1w9u0t:

ページ2に移動

最初の20をスキップし、次の20を取得:

curl https://api.omise.co/charges?offset=20&limit=20 \
-u skey_test_5xuy4w91xqz7d1w9u0t:

ページ3に移動

最初の40をスキップし、次の20を取得:

curl https://api.omise.co/charges?offset=40&limit=20 \
-u skey_test_5xuy4w91xqz7d1w9u0t:

最大アイテム数を取得

100アイテムを取得(許可されている最大数):

curl https://api.omise.co/charges?limit=100 \
-u skey_test_5xuy4w91xqz7d1w9u0t:

ページネーションパターン

パターン1: すべてのページを反復処理

ページを反復処理してすべてのアイテムを取得:

# Ruby - Fetch all charges
require 'omise'

Omise.api_key = ENV['OMISE_SECRET_KEY']

all_charges = []
offset = 0
limit = 100

loop do
page = Omise::Charge.list(limit: limit, offset: offset)

all_charges.concat(page.data)

# Check if we've retrieved all items
break if offset + page.data.length >= page.total

offset += limit
end

puts "Retrieved #{all_charges.length} total charges"

パターン2: 総ページ数を計算

存在するページ数を決定:

# Python - Calculate total pages
import omise
import math

omise.api_secret = os.environ['OMISE_SECRET_KEY']

# Get first page to get total count
first_page = omise.Charge.list(limit=20, offset=0)
total_items = first_page['total']
items_per_page = 20

total_pages = math.ceil(total_items / items_per_page)

print(f"Total items: {total_items}")
print(f"Total pages: {total_pages}")

パターン3: ページナビゲーションUI

UI用のページナビゲーションを構築:

// Node.js - Page navigation logic
const omise = require('omise')({
secretKey: process.env.OMISE_SECRET_KEY
});

async function getChargesPage(pageNumber, itemsPerPage = 20) {
const offset = (pageNumber - 1) * itemsPerPage;

const result = await omise.charges.list({
limit: itemsPerPage,
offset: offset
});

const totalPages = Math.ceil(result.total / itemsPerPage);

return {
charges: result.data,
pagination: {
currentPage: pageNumber,
totalPages: totalPages,
totalItems: result.total,
itemsPerPage: itemsPerPage,
hasNextPage: pageNumber < totalPages,
hasPrevPage: pageNumber > 1
}
};
}

// Usage: Get page 3
const page3 = await getChargesPage(3, 20);
console.log(page3.pagination);
// {
// currentPage: 3,
// totalPages: 8,
// totalItems: 142,
// itemsPerPage: 20,
// hasNextPage: true,
// hasPrevPage: true
// }

パターン4: カーソルベースの反復処理

すべての結果を効率的に反復処理:

<?php
// PHP - Iterate all charges

require_once 'vendor/autoload.php';

define('OMISE_SECRET_KEY', getenv('OMISE_SECRET_KEY'));

function getAllCharges($limit = 100) {
$allCharges = [];
$offset = 0;

do {
$page = OmiseCharge::retrieve([
'limit' => $limit,
'offset' => $offset
]);

$allCharges = array_merge($allCharges, $page['data']);

$offset += count($page['data']);
$hasMore = $offset < $page['total'];

} while ($hasMore);

return $allCharges;
}

$charges = getAllCharges();
echo "Retrieved " . count($charges) . " charges\n";

パターン5: 遅延ロード

オンデマンドでさらにアイテムをロード(無限スクロール):

// JavaScript - Lazy loading for UI
class ChargeLoader {
constructor(itemsPerPage = 20) {
this.itemsPerPage = itemsPerPage;
this.offset = 0;
this.allCharges = [];
this.hasMore = true;
this.total = null;
}

async loadMore() {
if (!this.hasMore) {
return { charges: [], hasMore: false };
}

const response = await fetch('/api/charges', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
limit: this.itemsPerPage,
offset: this.offset
})
});

const result = await response.json();

this.allCharges.push(...result.data);
this.offset += result.data.length;
this.total = result.total;
this.hasMore = this.offset < this.total;

return {
charges: result.data,
hasMore: this.hasMore,
total: this.total,
loaded: this.offset
};
}

reset() {
this.offset = 0;
this.allCharges = [];
this.hasMore = true;
this.total = null;
}
}

// Usage
const loader = new ChargeLoader(20);

// Load first page
const page1 = await loader.loadMore();
console.log(`Loaded ${page1.charges.length} charges`);

// Load second page (user scrolls down)
const page2 = await loader.loadMore();
console.log(`Loaded ${page2.charges.length} more charges`);

パターン6: 逆ページネーション(最新順)

最新のアイテムを最初に取得:

// Go - Get newest charges first
package main

import (
"fmt"
"github.com/omise/omise-go"
"github.com/omise/omise-go/operations"
)

func getRecentCharges(limit int) ([]*omise.Charge, error) {
client, _ := omise.NewClient(
os.Getenv("OMISE_PUBLIC_KEY"),
os.Getenv("OMISE_SECRET_KEY"),
)

// Default order is reverse_chronological (newest first)
charges, err := client.Charges().List(&operations.ListCharges{
Limit: limit,
Offset: 0,
})

if err != nil {
return nil, err
}

return charges.Data, nil
}

func main() {
// Get 50 most recent charges
charges, _ := getRecentCharges(50)

for i, charge := range charges {
fmt.Printf("%d. %s - %d %s\n",
i+1,
charge.ID,
charge.Amount,
charge.Currency,
)
}
}

高度なページネーションテクニック

フィルターとの組み合わせ

ページネーションは日付フィルターやその他のパラメータと連携します:

# Get charges from January 2025, page 2
curl "https://api.omise.co/charges?from=2025-01-01T00:00:00Z&to=2025-01-31T23:59:59Z&limit=20&offset=20" \
-u skey_test_5xuy4w91xqz7d1w9u0t:
# Ruby - Filter + pagination
charges = Omise::Charge.list(
from: '2025-01-01T00:00:00Z',
to: '2025-01-31T23:59:59Z',
limit: 50,
offset: 0
)

効率的な大規模データセットの取得

非常に大きなデータセットの場合は、より大きなページサイズを使用:

# Python - Efficient bulk retrieval
import omise

omise.api_secret = os.environ['OMISE_SECRET_KEY']

def fetch_all_charges_efficiently():
all_charges = []
offset = 0
limit = 100 # Maximum allowed

while True:
page = omise.Charge.list(limit=limit, offset=offset)

if not page['data']:
break

all_charges.extend(page['data'])

# Check if we got all items
if offset + len(page['data']) >= page['total']:
break

offset += limit

# Optional: Rate limiting
time.sleep(0.1)

return all_charges

charges = fetch_all_charges_efficiently()
print(f"Retrieved {len(charges)} charges")

進行状況の追跡

大規模なデータセットを取得する際の進行状況を表示:

// Node.js - Progress tracking
async function fetchAllChargesWithProgress(onProgress) {
const limit = 100;
let offset = 0;
let allCharges = [];

// Get first page to know total
const firstPage = await omise.charges.list({ limit, offset });
const total = firstPage.total;

allCharges.push(...firstPage.data);
offset += firstPage.data.length;

onProgress({
loaded: offset,
total: total,
percentage: Math.round((offset / total) * 100)
});

// Fetch remaining pages
while (offset < total) {
const page = await omise.charges.list({ limit, offset });

allCharges.push(...page.data);
offset += page.data.length;

onProgress({
loaded: offset,
total: total,
percentage: Math.round((offset / total) * 100)
});
}

return allCharges;
}

// Usage
const charges = await fetchAllChargesWithProgress((progress) => {
console.log(`Loading: ${progress.percentage}% (${progress.loaded}/${progress.total})`);
});

並列ページ取得

複数のページを同時に取得(レート制限に注意して使用):

// Node.js - Parallel fetching (advanced)
async function fetchMultiplePagesParallel(startPage, endPage, itemsPerPage = 20) {
const pageNumbers = Array.from(
{ length: endPage - startPage + 1 },
(_, i) => i + startPage
);

const pagePromises = pageNumbers.map(async (pageNum) => {
const offset = (pageNum - 1) * itemsPerPage;
return omise.charges.list({ limit: itemsPerPage, offset });
});

const pages = await Promise.all(pagePromises);

// Flatten all pages into single array
const allCharges = pages.flatMap(page => page.data);

return {
charges: allCharges,
total: pages[0].total
};
}

// Fetch pages 1-5 in parallel
const result = await fetchMultiplePagesParallel(1, 5, 20);
console.log(`Fetched ${result.charges.length} charges`);
レート制限

並列リクエストはレート制限にカウントされます。このテクニックは慎重に使用し、リクエスト間に遅延を実装することを検討してください。


エッジケースの処理

空の結果

クエリに一致するアイテムがない場合:

{
"object": "list",
"data": [],
"limit": 20,
"offset": 0,
"total": 0,
"location": "/charges"
}
// Check for empty results
const charges = await omise.charges.list({ limit: 20 });

if (charges.data.length === 0) {
console.log('No charges found');
} else {
console.log(`Found ${charges.data.length} charges`);
}

合計を超えるオフセット

利用可能なアイテムを超えるオフセットをリクエスト:

# Total is 50, but requesting offset 100
curl "https://api.omise.co/charges?offset=100&limit=20" \
-u skey_test_...:

レスポンス:

{
"object": "list",
"data": [],
"limit": 20,
"offset": 100,
"total": 50
}

最後のページの部分的な結果

最後のページはlimitより少ないアイテムを持つ可能性があります:

# Total is 145, requesting items 141-160
curl "https://api.omise.co/charges?offset=140&limit=20" \
-u skey_test_...:

レスポンス:

{
"object": "list",
"data": [
{ "object": "charge", "id": "chrg_141" },
{ "object": "charge", "id": "chrg_142" },
{ "object": "charge", "id": "chrg_143" },
{ "object": "charge", "id": "chrg_144" },
{ "object": "charge", "id": "chrg_145" }
],
"limit": 20,
"offset": 140,
"total": 145
}
// Detect last page
const page = await omise.charges.list({ offset: 140, limit: 20 });

const isLastPage = page.offset + page.data.length >= page.total;
console.log(`Is last page: ${isLastPage}`); // true

動的な合計の変更

新しいアイテムが作成されると、リクエスト間でtotalカウントが変わる可能性があります:

// First request
const page1 = await omise.charges.list({ limit: 20, offset: 0 });
console.log(`Total: ${page1.total}`); // 100

// New charges created...

// Second request
const page2 = await omise.charges.list({ limit: 20, offset: 20 });
console.log(`Total: ${page2.total}`); // 105 (changed!)

// Handle this by checking total on each request

ベストプラクティス

1. 適切なページサイズを使用

// ✅ Good - Choose size based on use case

// For UI display (don't overwhelm users)
const charges = await omise.charges.list({ limit: 20 });

// For bulk processing (maximize efficiency)
const chargesForExport = await omise.charges.list({ limit: 100 });

// For real-time updates (minimize latency)
const recentCharges = await omise.charges.list({ limit: 10 });

2. 可能な場合は結果をキャッシュ

# ✅ Good - Cache paginated results
require 'redis'

redis = Redis.new

def get_charges_page(page_num, cache_ttl = 300)
cache_key = "charges:page:#{page_num}"

# Try cache first
cached = redis.get(cache_key)
return JSON.parse(cached) if cached

# Fetch from API
offset = (page_num - 1) * 20
charges = Omise::Charge.list(limit: 20, offset: offset)

# Cache for 5 minutes
redis.setex(cache_key, cache_ttl, charges.to_json)

charges
end

3. ページネーションエラーを処理

# ✅ Good - Handle pagination errors
import omise

def safe_list_charges(offset=0, limit=20, max_retries=3):
for attempt in range(max_retries):
try:
return omise.Charge.list(offset=offset, limit=limit)

except omise.errors.BaseError as e:
if attempt == max_retries - 1:
raise

if e.http_status >= 500:
# Server error - retry
time.sleep(2 ** attempt)
continue
else:
# Client error - don't retry
raise

charges = safe_list_charges(offset=0, limit=50)

4. ページネーションパラメータを検証

<?php
// ✅ Good - Validate parameters

function getChargesPage($pageNum, $itemsPerPage) {
// Validate page number
if ($pageNum < 1) {
throw new InvalidArgumentException('Page number must be >= 1');
}

// Validate items per page
if ($itemsPerPage < 1 || $itemsPerPage > 100) {
throw new InvalidArgumentException('Items per page must be 1-100');
}

$offset = ($pageNum - 1) * $itemsPerPage;

return OmiseCharge::retrieve([
'limit' => $itemsPerPage,
'offset' => $offset
]);
}

5. ユーザーにページネーション状態を表示

// ✅ Good - Clear pagination UI
function renderPagination(currentPage, totalPages) {
return `
<div class="pagination">
<button ${currentPage === 1 ? 'disabled' : ''}>
Previous
</button>

<span>Page ${currentPage} of ${totalPages}</span>

<button ${currentPage === totalPages ? 'disabled' : ''}>
Next
</button>

<span class="total-info">
Showing ${(currentPage - 1) * 20 + 1}-${Math.min(currentPage * 20, totalItems)}
of ${totalItems} items
</span>
</div>
`;
}

6. 大規模データセット用に最適化

# ✅ Good - Stream large datasets
def export_all_charges_to_csv
CSV.open('charges.csv', 'wb') do |csv|
csv << ['ID', 'Amount', 'Currency', 'Status', 'Created']

offset = 0
limit = 100

loop do
page = Omise::Charge.list(limit: limit, offset: offset)

page.data.each do |charge|
csv << [
charge.id,
charge.amount,
charge.currency,
charge.status,
charge.created_at
]
end

break if offset + page.data.length >= page.total

offset += limit

# Rate limiting
sleep(0.5)
end
end
end

7. ページ番号を正しく計算

// ✅ Good - Correct page calculations

function calculatePageInfo(offset, limit, total) {
const currentPage = Math.floor(offset / limit) + 1;
const totalPages = Math.ceil(total / limit);
const itemsOnPage = Math.min(limit, total - offset);

return {
currentPage,
totalPages,
itemsOnPage,
firstItemNum: offset + 1,
lastItemNum: offset + itemsOnPage,
hasNextPage: offset + limit < total,
hasPrevPage: offset > 0
};
}

// Example
const info = calculatePageInfo(40, 20, 142);
console.log(info);
// {
// currentPage: 3,
// totalPages: 8,
// itemsOnPage: 20,
// firstItemNum: 41,
// lastItemNum: 60,
// hasNextPage: true,
// hasPrevPage: true
// }

パフォーマンスの考慮事項

リクエスト効率

ページあたりのアイテム数1000アイテムに必要なAPIリクエスト推奨用途
10100リクエストリアルタイムUIアップデート
20(デフォルト)50リクエスト一般的なUI表示
5020リクエスト管理ダッシュボード
100(最大)10リクエストバルク処理、エクスポート

メモリ管理

# ✅ Good - Process in chunks to manage memory
def process_all_charges():
offset = 0
limit = 100

while True:
# Fetch page
page = omise.Charge.list(limit=limit, offset=offset)

if not page['data']:
break

# Process this page
for charge in page['data']:
process_charge(charge)

# Don't keep all charges in memory
offset += len(page['data'])

if offset >= page['total']:
break

# ❌ Bad - Loads everything into memory
def process_all_charges_bad():
all_charges = []
offset = 0

# This could use gigabytes of RAM for large datasets
while True:
page = omise.Charge.list(limit=100, offset=offset)
all_charges.extend(page['data'])
offset += 100
if offset >= page['total']:
break

for charge in all_charges:
process_charge(charge)

レート制限の認識

// ✅ Good - Respect rate limits
async function fetchAllChargesSafely() {
const charges = [];
let offset = 0;
const limit = 100;

while (true) {
try {
const page = await omise.charges.list({ limit, offset });

charges.push(...page.data);

if (offset + page.data.length >= page.total) {
break;
}

offset += limit;

// Small delay to avoid rate limits
await new Promise(resolve => setTimeout(resolve, 100));

} catch (error) {
if (error.statusCode === 429) {
// Rate limited - wait longer
await new Promise(resolve => setTimeout(resolve, 5000));
continue;
}
throw error;
}
}

return charges;
}

ページネーションのテスト

エッジケースをテスト

# Test pagination edge cases
describe 'Charge Pagination' do
it 'handles first page' do
charges = Omise::Charge.list(limit: 20, offset: 0)
expect(charges.offset).to eq(0)
expect(charges.data.length).to be <= 20
end

it 'handles last page' do
first_page = Omise::Charge.list(limit: 20, offset: 0)
total = first_page.total

# Calculate last page offset
last_offset = (total / 20) * 20

last_page = Omise::Charge.list(limit: 20, offset: last_offset)
expect(last_page.data.length).to be <= 20
end

it 'handles empty results' do
# Filter that returns no results
charges = Omise::Charge.list(
from: '2030-01-01T00:00:00Z',
to: '2030-01-02T00:00:00Z'
)
expect(charges.data).to be_empty
expect(charges.total).to eq(0)
end

it 'handles offset beyond total' do
charges = Omise::Charge.list(limit: 20, offset: 999999)
expect(charges.data).to be_empty
end
end

よくある落とし穴

❌ してはいけないこと: オフセット値をハードコード

// ❌ Bad - Assumes static data
const page1 = await omise.charges.list({ offset: 0, limit: 20 });
const page2 = await omise.charges.list({ offset: 20, limit: 20 });
// If items were added/removed, page2 might have duplicates or gaps
// ✅ Good - Calculate offset dynamically
let offset = 0;
const limit = 20;

const page1 = await omise.charges.list({ limit, offset });
offset += page1.data.length; // Dynamic offset

const page2 = await omise.charges.list({ limit, offset });

❌ してはいけないこと: 総数を無視

# ❌ Bad - Doesn't check if more pages exist
offset = 0
while True:
page = omise.Charge.list(limit=20, offset=offset)
process(page['data'])
offset += 20
# This loops forever if you don't check total!
# ✅ Good - Check total
offset = 0
limit = 20

while True:
page = omise.Charge.list(limit=limit, offset=offset)
process(page['data'])

offset += len(page['data'])
if offset >= page['total']:
break

❌ してはいけないこと: 100を超えるLimitを使用

# ❌ Bad - Limit too high
curl "https://api.omise.co/charges?limit=500" \
-u skey_test_...:
# Error: limit must be <= 100
# ✅ Good - Use maximum of 100
curl "https://api.omise.co/charges?limit=100" \
-u skey_test_...:

クイックリファレンス

ページネーションパラメータ

パラメータデフォルト範囲説明
limitinteger201-100ページあたりのアイテム数
offsetinteger00-∞スキップするアイテム数

リストレスポンスフィールド

{
"object": "list",
"data": [...], // アイテムの配列
"limit": 20, // ページあたりのアイテム数
"offset": 0, // スキップされたアイテム数
"total": 142, // 総アイテム数
"from": "...", // 開始日(オプション)
"to": "...", // 終了日(オプション)
"order": "...", // ソート順序
"location": "..." // エンドポイントパス
}

ページを計算

// Current page number
const currentPage = Math.floor(offset / limit) + 1;

// Total pages
const totalPages = Math.ceil(total / limit);

// Has next page
const hasNextPage = offset + limit < total;

// Has previous page
const hasPrevPage = offset > 0;

// Next page offset
const nextOffset = offset + limit;

// Previous page offset
const prevOffset = Math.max(0, offset - limit);

関連リソース


次へ: べき等性について学習し、操作を重複させずにリクエストを安全に再試行します。