ギャップやループなしでURLバックリンクチェッカーをページネーション
1回の呼び出しあたりの行数、1クエリあたりのページ数、そして50,000のハード上限。これら3つを尊重し、いつ停止すべきかを教えてくれるループです。

1回の呼び出しで最大1,000行が返されます。各行の数にかかわらず、各呼び出しは1回課金されます。ディープページネーションは50,000行に制限され、これは1,000行ずつの50回の呼び出しに相当します。
ソートがページネーションより優先される理由
ソートされていないリストでのページネーションは重複の発生原因となります。sortとorderをpageとperと一緒に送信し、実行中のすべてのリクエストで3ページ目が同じ意味を持つようにしてください。
refdomainsの取得は1呼び出しあたり2クレジットなので、50呼び出しで100クレジット、つまり1セントになります。最悪の場合でもコストは低く、真のコストは終了しないループにあります。
ソートカラムの選択
ソート対象の列は質問によって異なります。監査では通常、最大の値を先頭に持ってくる選択が好まれます。これにより、関心の高い部分ではなく、長いテール部分がカットオフされるためです。
質問に関連のないフィールドでソートすると、カットオフによって必要な行が除外される可能性があります。まず候補となる各ソートで10行ずつ取得し、どのスライスがカットオフ後も残るかどうかを確認してください。
レポートにこの選択を明記し、次に誰かがそのカットオフの理由を理解できるようにしてください。
実行中に移動するレコード
月次リビルドが実行中に完了しない限り、データは実行中に移動しません。稀なケースですが、実際に発生し得ます。月初の直前に実行される夜間ジョブは、古いインデックスからの1ページと新しいインデックスからの次のページを返す可能性があります。ログに開始時刻を記録し、比較してください。
ログの先頭に開始時刻を、各行にページ番号を記載してください。そうすれば、出力の不一致がタイミングに起因するものなのか、パースエラーに起因するものなのかを判断できます。
ジョブがスケジュールされている場合、おそらくリビルドが行われる直前の時刻に開始しないようにしてください。ログでは、それ以外が一致する2回の実行間で合計値がシフトしている場合にリビルドが示されます。したがって、ログの各行に合計値を含めてください。
リビルドをまたぐ実行は、必ずしも誤りではありません。一貫性がないだけなので、ログにそれらの実行をマークし、レポートに重要な場合は影響を受けるスライスだけを再実行してください。
停止するページングループ
Node 20ワーカー用のTypeScriptです。最初の短いページ、カットオフ、または ok が false である場合に停止します。
const BASE = "https://seomasters.dev/api.php";
const CAP = 50_000;
const PER = 1000;
export async function pullAll(domain: string, key: string) {
const rows: unknown[] = [];
for (let page = 1; rows.length < CAP; page++) {
const u = new URL(BASE);
u.search = new URLSearchParams({
action: "refdomains", q: domain,
page: String(page), per: String(PER),
sort: "dr", order: "desc",
}).toString();
const res = await fetch(u, { headers: { Authorization: `Bearer ${key}` } });
if (res.status === 401 || res.status === 402) throw new Error(`stop: HTTP ${res.status}`);
const body = await res.json();
if (!body.ok) throw new Error(JSON.stringify(body).slice(0, 300));
const chunk = findRows(body); // see note below
rows.push(...chunk);
if (chunk.length < PER) break; // short page means we are done
}
return rows;
}ページング実行のトラブルシューティング
| 確認できる内容 | 考えられる理由 | 対処法 |
|---|---|---|
| ページ51が空またはエラーで返ってくる | 50,000件の上限に達しました | sortを別のアクションや値に変更して絞り込み、idでマージしてください |
| 同一ドメインが2つのページに表示されている | ソートされていないクエリ、または実行中にインデックスが再構築された | sortとorderを送信し、ドメインキーで重複を除外してください |
| ダッシュボードの合計よりも行数が少ない | 合計値はグラフ全体のもので、取得は上限に達しています | UIに「上位50,000件」を表示する |
| ループが終了しない | 条件はrows.lengthのみをチェックしている | 上記のように、短いページでループを抜ける |
| 想定より高い請求額 | 200以外のステータスコードで毎回リトライする | ネットワーク障害のみリトライする。エラーを参照 |

フィールド名を推測せずに行を読み取る
上記のfindRowsヘルパーは意図的に開放されています。実際のAPI呼び出しを1回行い、JSONを確認してから、チュートリアルで示されたものではなく、実際に確認した内容に基づいてアクセサを書いてください。
このタスクのより安価な代替手段として、ページネーションなしで複数のドメインを一度にスコアリングする場合は、bulk DR scriptをお読みください。
レスポンスを1つ固定する
その後、テストで固定します。レスポンスの1つをフィクスチャファイルとして保存し、行の合計数と最初の行のキーについてアサートしてください。これにより、将来の変更が顧客のチャートではなくCIで失敗するようになります。
フィクスチャをテストの隣にバージョン管理に保持し、レビューアーが何も実行せずにコードが期待する構造を確認できるようにします。
APIが後からフィールドを追加した場合、フィクスチャの差分は、アクセサーがそれを検知したのか、それとも無視したのかを示します。
フィクスチャには、そのクエリ由来のアクション、ドメイン、ソート順などを名前に含めてください。response1.json というファイル名では、それが何を証明するものかが次の読者には分かりません。
保存した行数を数え上げる
保存した行と、ループが報告した合計数を比較してください。ギャップ(差)はページがドロップされたことを、重複はページが2回フェッチされ、1つの行にアップサートされたことを意味します。
このチェックは最初のフェッチだけでなく、すべてのテストフェッチの後に実行してください。カウントのズレは、ソート順の変更や実行中の再ビルドの最初の兆候であることがほとんどです。
カウントをページ番号と同じログ行に保持し、両方を一目で確認できるようにしてください。
上限を超えた作業の分割
一部のクエリは上限を超えます。上限を超えるリストについては、より多くのページネーションを行うのではなく、問題を分割してください。1つのソート順で上位50,000件を取得し、他のアクションに「ハビヒッター(上位)」について問い合わせてください。これらも2クレジットのコストがかかりますが、異なる質問に答えるものです。ページネーションを強化しても、カットオフを超えられません。
1つのソート順で上位50,000件を取得する
APIの上限を事実として受け入れ、1つのソート順で上位50,000行を取得し、保存してください。レポートでは、その結果を「トップスライス」としてラベル付けしてください。
制限されたカウントを完全な合計のように見せるダッシュボードは、それを読む人を誤解させます。ラベルはコストがかからず、url backlink checkerレポートはそれがある方がより誠実な印象を与えます。
スライスのサイズを実行前に決定し、レポートヘッダーに書き込んでください。そうすれば、数字が実際よりも大きく見えることはありません。ヘッダー行はコストがかからず、後で欠落した行に気づいた読者は、それによって感謝するでしょう。
ハビヒッターに異なる質問を投げる
anchors と toppages は、どのアンカーテキストが支配的か、どのページがリンクを集めているかなど、他の質問に答えます。ドメインが上限を超えている場合、これらの回答は、同じリストの次のページよりも重要になることがあります。
それらの結果をドメインをキーとする独自のテーブルに保持してください。そうすれば、同じドメインの後のフェッチで、トップスライスの行に触れることなくそれらを更新できます。
各追加アクションは独自のクレジットを消費するため、請求書が届いてからではなく、実行前に価格を確認してください。
最も重いドメインの短いリストを月1回取得することは、毎晩取得する長いリストよりも、より有用なコンテキストを提供することがよくあります。
ドメインキーでマージする
2つのフェッチが重複する場合、ドメインキーでマージし、新しいタイムスタンプを保持してください。マージは、どちらのフェッチが先に完了したかに依存しなくなります。
各行でどのフェッチが勝ったかをログに記録してください。ある数字が2つのレポート間で移動した場合、そのログは、インデックスが変更されたのか、マージが変更されたのかを示します。
マージは2つのフェッチが出会う唯一の場所であるため、テスト付きの単一関数に保持してください。
クラッシュ後の再開
クラッシュは発生します。デプロイでワーカーが再起動したり、ラップトップがスリープしたり、VPNが切断されたりすると、ループは50ページ中の31ページで死亡します。
呼び出しのたびにページを保存する
各呼び出しの後に、最後に完了したページ番号を永続化してください。トップからではなく、その番号の次のページから再開すれば、全体を再購入する代わりに、ページあたり2クレジットの支払いで済みます。
レスポンスが解析された後に保存してください。読み込みに失敗したページは再フェッチされるべきであり、保存されたページ番号は常に「あなたが持っている行」を意味していなければなりません。
1回の実行ではなく1分ごとの保存は、クラッシュがクレジットの重複消費を引き起こす可能性があります。書き込みを小さく保つことで、毎回実行しても安価にしてください。
各保存にかかった時間と、各呼び出しにかかった時間の両方をログに記録してください。保存が呼び出し自体に匹敵するほど時間がかかる場合、それはストレージ層を指しており、ループが成長する前に修正する価値があります。
ドメインキーでアップサートする
冪等な書き込みを保持してください。ドメインキーで各行をアップサートすることで、2回フェッチされたページが2つの行ではなく1つの行になります。
手動修復スクリプトを含むすべての書き込みパスで同じキーを使用してください。2つのキーを持つ2つの書き込みパスは、再開時に重複が残る原因になります。
修復スクリプトは、実行失敗後に急いで書かれることが多いものです。その際に2つ目のキーが忍び込むため、メインループと同様に注意深くレビューしてください。
ソート順が変更された場合、再開を拒否する
ページ番号 alongside ソート順と順序をログに記録してください。クラッシュと再起動の間に sort が変更された場合、保存されたページ番号は同じスライスを意味しなくなります。再開を拒否し、クリーンスタートしてください。
ソート順、順序、ページ、時刻を含む小さな再開レコードを保存してください。ループはまずそのレコードを読み取り、実行しようとしているリクエストのどのフィールドも一致しなくなった場合、継続を拒否します。
再開の拒否は失敗ではありません。それはループが、保存されたページがもはや同じ行を意味しないことを示しており、クリーンスタートの方がより少ないコストのミスです。
本番環境でのループ実行
- 1
小さなドメインから始める
数百行のドメインに対してループを実行し、短ページ停止が発動して、終了パスが機能していることを確認してください。
- 2
1ページを目視で確認する
保存されたレスポンスの1つを開き、アクセサーがまだ行を読み込めていることを確認してください。目を通していないパーサーを信頼しないでください。
- 3
制限値を構成ファイルに保持する
上限とページサイズを1つの構成ファイルに配置してください。変更がレビューで表示され、関数の中に隠れることがなくなります。
- 4
ループが終了した理由をログに記録する
実行が短ページ、上限、またはエラーで終了したかを記録してください。それらの3つの終了は、ログを読む人にとって異なる意味を持ちます。