Bläddra i en URL backlink checker utan luckor eller loopar
Rader per anrop, sidor per fråga och ett hårt tak på 50 000. Här är en loop som respekterar alla tre och berättar när du ska sluta.

Upp till 1 000 rader returneras per anrop. Varje anrop faktureras en gång, oavsett hur många rader det innehåller. Djup paginering är begränsad till 50 000 rader, vilket är 50 anrop på 1 000 rader vardera.
Varför sortering kommer före paginering
Paginering över en osorterad lista är hur dupliceringar uppstår. Skicka alltid sort och order tillsammans med page och per, så att sida 3 betyder samma sak vid varje begäran i körningen.
Ett refdomains-drag kostar 2 krediter per anrop, så 50 anrop blir 100 krediter, eller en cent. Även det värsta fallet är billigt; den verkliga kostnaden är en loop som inte terminerar.
Att välja sorteringsfält
Vilket fält du sorterar efter beror på frågan. Störst först är det vanliga valet för revisioner, eftersom taket då klipper av den långa svansen istället för den del du bryr dig om.
Sortera efter ett fält som inte betyder något för din fråga, och avskärningen kan ta bort de rader du ville ha. Dra tio rader per kandidat sortering först och se vilken skiva som överlever taket.
Skriv valet i rapporten, så att nästa person vet varför avskärningen hamnade där den gjorde.
Poster som flyttas under en körning
Datamaterialet flyttas inte under en körning om inte den månatliga ombyggnaden landar mitt i den. Sällsynt, men verkligt. En nattjobb som sträcker sig över den första i månaden kan returnera en sida från det gamla indexet och nästa från det nya, så registrera starttiden i ditt logg och jämför.
Skriv starttiden högst upp i loggen och sidnumret på varje rad. Då pekar en mismatch i outputen på timing, inte på en parseringsbugg.
Om jobbet är schemalagt, undvik en starttid precis innan en sannolik ombyggnad. Dina loggar visar en ombyggnad som en förändring i totaler mellan två körningar som annars matchar, så behåll totalerna i varje loggrad.
En körning som sträcker sig över en ombyggnad är inte felaktig som standard. Den är bara inkonsistent, så markera dessa körningar i loggen och kör om den påverkade skivan när det är viktigt för en rapport.
En pagineringloop som stannar
TypeScript för en Node 20-worker. Den stannar vid den första korta sidan, vid taket eller vid någon ok som inte är sann.
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;
}Felsökning av en pagineringskörning
| Vad du ser | Sannolik orsak | Vad du ska göra |
|---|---|---|
| Sida 51 returneras tom eller ger fel | Du träffade 50 000-taket | Begränsa med en annan åtgärd eller en annan sort, och slå sedan ihop via id |
| Samma domän på två sidor | Osorterad fråga, eller indexet ombyggdes mitt under körningen | Skicka sort och order; deduplicera på domännamnsnyckeln |
| Färre rader än dashboard-totalen | Totalen gäller för hela grafen, draget är begränsat | Visa "topp 50 000" i UI:t |
| Loopen tar aldrig slut | Villkorkontrollen kontrollerar endast rows.length | Avbryt vid en kort sida, som ovan |
| Fakturan högre än förväntat | Försök igen vid varje icke-200 | Försök endast vid nätverksfel, se fel |

Att läsa rader utan att gissa fältnamn
Hjälpen findRows ovan är avsiktligt öppen. Gör ett riktigt anrop, titta på JSON:et och skriv åtkomsten mot det du ser snarare än vad en handledning lovade.
För den billigare kusinen till detta jobb, att skatta många domäner samtidigt utan någon paginering alls, läs bulk DR-skriptet.
Frys ett svar som en fixture
Frys det sedan med ett test. Spara ett svar som en fixture-fil och gör en assertion på radantalet och den första radens nycklar, så att en framtida förändring misslyckas i CI och inte i en kunds diagram.
Behåll fixturen i versionskontroll bredvid testet, så att en granskare ser formen koden förväntar sig utan att köra något.
När API:t lägger till ett fält senare berättar fixture-diffen dig om din åtkomst noterade det eller tyst ignorerade det.
Döp fixturen efter frågan den kom från, till exempel åtgärden, domänen och sorteringen. En fil som heter response1.json berättar ingenting för nästa läsare om vad den bevisar.
Räkna raderna du sparade
Jämför raderna du sparade med totalen loopen rapporterade. En lucka betyder att en sida tappades, och en upprepning betyder att en sida hämtades två gånger och upsertades till en rad.
Kör den kontrollen efter varje testdrag, inte bara efter det första. Avvikelse i antalet är vanligtvis det första symptomet på en ändrad sortering eller en ombyggnad som landade mitt under körningen.
Behåll antalet i samma loggrad som sidnumret, så att de kan läsas tillsammans på en glimt.
Att dela upp arbete ovanför taket
Vissa frågor växer ur taket. För listor ovanför taket, dela upp problemet istället för att paginera hårdare. Dra de topp 50 000 av en sort, och fråga sedan andra åtgärder om de tunga spelarna, eftersom dessa också kostar 2 krediter och svarar på andra frågor. Att paginera hårdare kommer aldrig förbi avskärningen.
Dra de topp 50 000 av en sort
Ta taket som en fakta i API:t. Dra de topp 50 000 raderna av en sort, lagra dem och märk resultatet som den övre skivan i alla rapporter.
Ett dashboard som visar en begränsad räknare som om det vore den fulla totalen vilseleder de som läser det. Etiketten kostar ingenting, och en url backlink checker-läsning blir mer ärlig med den.
Bestäm skivstorleken innan körningen och skriv den i rapporthuvudet, så att talet aldrig ser större ut än det är. En rad i huvudet kostar ingenting, och en läsare som hittar en saknad rad senare kommer att tacka dig för det.
Ställ en annan fråga till de tunga spelarna
anchors och toppages svarar på andra frågor, till exempel vilken ankertext som dominerar eller vilka sidor som lockar länkarna. För en domän som passerat taket kan dessa svar vara viktigare än nästa sida i samma lista.
Behåll deras resultat i sina egna tabeller, nycklade efter domänen, så att ett senare drag av samma domän kan ersätta dem utan att påverka raderna i den övre skivan.
Varje extra åtgärd kostar sina egna krediter, så prissätt det innan körningen, inte efter att fakturan kommer.
En kort lista över de tyngsta domänerna, dragen en gång i månaden, ger ofta mer användbar kontext än en längre lista dragen varje natt.
Slå ihop på domännamnsnyckeln
När två drag överlappar, slå ihop dem på domännamnsnyckeln och behåll den nyare tidsstämpeln. Slå ihop beror då aldrig på vilket drag som avslutades först.
Logga vilket drag som vann varje rad. Om ett nummer flyttar sig mellan två rapporter berättar den loggen dig om indexet eller slås ihop förändrades.
Behåll slås ihop i en enda funktion med ett test, eftersom det är den enda platsen där två drag möts.
Att återuppta efter ett krasch
Krascher händer. En deploy startar om workern, en laptop sover, ett VPN bryts, och loopen dör på sida 31 av 50.
Spara sidan efter varje anrop
Persistera det sista färdiga sidnumret efter varje anrop. Starta från det numret plus ett, inte från toppen, och du betalar för 2 krediter per sida istället för att köpa hela draget igen.
Spara det efter att svaret har parserats, inte innan. En sida du misslyckades att läsa bör hämtas igen, och ett sparad sidnummer måste alltid betyda rader du har.
En sparande som sker en gång per minut istället för en gång per anrop lämnar ett fönster där en krasch kan upprepa kreditutgiften. Håll skrivningen liten, så att den är billig att göra varje gång.
Logga hur lång tid varje sparande tar samt hur lång tid varje anrop tar. Ett sparande som tävlar med anropet pekar mot lagerlagret, och det är värt att fixa innan loopen växer.
Upsert på domännamnsnyckeln
Behåll en idempotent skrivning. Upsert varje rad på dess domännamnsnyckel, så att en sida som hämtas två gånger lämnar en rad, inte två.
Använd samma nyckel på varje skrivväg, inklusive det manuella reparaturskriptet. Två skrivvägar med två nycklar är hur dupliceringar överlever en återupptagning.
Ett reparaturskript skrivs ofta i brådska, efter ett misslyckat drag. Det är då en andra nyckel smyger in, så granska det noggrant som huvudloopen.
Avvisa en återupptagning när sorteringen ändrades
Logga sortering och ordning tillsammans med sidnumret. Om någon ändrar sort mellan en krasch och omstarten betyder inte det sparade sidnumret längre samma skiva. Avvisa återupptagningen och starta rent.
Lagra en liten återupptagningspost med sorteringen, ordningen, sidan och tiden. Loopen läser den posten först, och den vägrar att fortsätta om något fält inte längre matchar begäran den ska göra.
En avvisad återupptagning är inte ett misslyckande. Det är loopen som säger att den sparade sidan inte längre betyder samma rader, och att starta rent är det billigare misstaget.
Att köra loopen i produktion
- 1
Starta med en liten domän
Kör loopen mot en domän med några hundra rader, så att den korta sidstoppen aktiveras och du ser utgångsvägen fungera.
- 2
Kontrollera en sida för ögat
Öppna ett sparad svar och bekräfta att åtkomsten fortfarande läser raderna. Lita inte på en parsing du inte har tittat på.
- 3
Behåll gränserna i konfiguration
Sätt taket och sidstorleken i en konfigurationsfil, så att en förändring visas i granskningen och inte döljs inne i en funktion.
- 4
Logga anledningen till att loopen avslutades
Registrera om körningen avslutades på en kort sida, på taket eller på ett fel. Dessa tre avslutningar betyder olika saker för den som läser loggen.