GunSpec
Praxis

Im Tarif bleiben

Das meiste Kontingent geht für Daten drauf, die sich nicht geändert haben. Hier steht, was jeder Tarif erlaubt, welche Aufrufe frei sind, welche Form fünf Anfragen ersetzt und wie ein Skript anhält, bevor die Grenze es für Sie tut.

Zwei Limits zählen Anfragen, aus der Konfiguration, die die API durchsetzt: eine Tagesgrenze je Schlüssel und ein Monatskontingent für das ganze Konto. Rate Limits behandelt die Minutenseite.

TarifAnfragen / Monat (Konto)Anfragen / Tag (je Schlüssel)MCP-Aufrufe / TagSeitentiefe
Explorer20050205
Builder25,0005,00050025
Studio150,00010,0002,500Keine Grenze
Enterprise1,000,00050,00010,000Keine Grenze

MCP-Aufrufe sind eine Untergrenze innerhalb des Anfragelimits, nie ein Zusatzkontingent: die zuerst erreichte Grenze lehnt ab, wie die MCP-Limitseite erklärt. Mehr als 10 Seiten in 90 Sekunden werden ebenfalls abgelehnt. Durchlaufen Sie den Katalog also stetig statt parallel.

Das Monatskontingent des Tarifs wird durchgesetzt, nicht nur angezeigt. Das sind die Regeln, damit ein Skript darum herum planen kann.

  • Der Monat gehört dem Konto. Jeder Schlüssel darin zieht aus einem gemeinsamen Topf: ein zweiter Schlüssel verdoppelt den Tarif nicht, während die Tagesgrenze je Schlüssel bleibt.
  • Nur ausgelieferte Aufrufe verbrauchen ihn. Ein Aufruf, den eine Grenze mit einem 429 abgelehnt hat, nicht, und ein 304 ebenso wenig. Eine Wiederholungsschleife gegen eine Grenze kann also keinen Monat aufbrauchen, für den sie abgelehnt wurde. Eine bedingte Anfrage bleibt der kostenlose Weg, Ihre Kontingente zu lesen, wie Caching erklärt.
  • Es ist ein Kalendermonat in UTC. Er geht um Mitternacht UTC am 1. auf null zurück, und X-Monthly-Reset nennt den Zeitpunkt. Das Abrechnungsdatum eines bezahlten Tarifs verschiebt ihn nicht.
  • Die zuerst erreichte Grenze lehnt den Aufruf ab. Sind beide verbraucht, nennt das 429 MONTHLY_CAP_EXCEEDED, weil diese Grenze länger dauert.
  • Die 200 Anfragen eines Explorer-Schlüssels reichen bei seiner Tagesgrenze von 50 etwa 4 Tage, danach wird jeder Aufruf bis zum 1. abgelehnt. Das ist die Größe des Tarifs, kein Fehler. Die bezahlten Tarife sind für ein Skript, das jeden Tag läuft, und ein Wechsel nach oben hebt das Kontingent sofort an, mit demselben Schlüssel.

Es sind zwei, und zusammen entscheiden sie meist darüber, ob ein Tarif passt.

  • Ein 304 Not Modified zählt weder gegen Ihr Tageslimit noch gegen Ihr Monatskontingent. Vorhandenes zu revalidieren ist frei, es erneut zu laden nicht. Caching erklärt die Mechanik.
  • Eine Antwort aus Ihrem eigenen Cache erreicht uns gar nicht. Beachten Sie das gesendete Cache-Control, und eine stark besuchte Seite schrumpft auf einen Aufruf pro Cache-Lebensdauer.
  • Auch eine Webhook-Zustellung ist keine Anfrage von Ihnen, denn wir rufen Sie an. Änderungen zu empfangen statt zu pollen kostet deshalb nichts pro Datensatz, und Den Katalog spiegeln baut darauf auf.

Dieselbe Seite, dieselben Daten, ein Viertel der Anfragen. Diese Endpunkte gibt es, weil die Schleife, die sie ersetzen, die ist, die jeder zuerst schreibt.

compare.ts
javascript
// Four calls, one page render.const g17 = await gunspec.firearms.get('glock-g17')const p226 = await gunspec.firearms.get('sig-sauer-p226')const m9 = await gunspec.firearms.get('beretta-92fs')const p320 = await gunspec.firearms.get('sig-sauer-p320') // One call, the same page, and the fields already lined up for a// table. Up to five ids, as one comma-separated string.const { data } = await gunspec.firearms.compare({  ids: 'glock-g17,sig-sauer-p226,beretta-92fs,sig-sauer-p320',})
  • Bis zu fünf Datensätze in einem Aufruf vergleichen, statt jeden einzeln zu holen. Die Felder kommen bereits ausgerichtet zurück.
  • Bis zu fünfzig Namen in einem Aufruf auflösen statt einzeln, wie der Resolve-Leitfaden zeigt.
  • Mit fields nur anfordern, was Sie rendern, und lieber per_page erhöhen als mehr Seiten zu holen: eine Seite mit hundert kostet eine Anfrage, fünf Seiten mit zwanzig kosten fünf.
  • Nehmen Sie den Endpunkt, der Ihre Frage bereits beantwortet (Varianten, ähnliche Modelle, Anbauteile), statt sie aus Liste plus Schleife zusammenzusetzen.

Die Tagesgrenze wird je Schlüssel gezählt und das Monatskontingent je Konto, und jede Antwort sagt, wo Sie bei beiden stehen. Ein Skript oder ein CI-Job muss also weder raten noch nachfragen. Lesen Sie es aus der Antwort, die Sie ohnehin haben.

HeaderWas er Ihnen sagt
X-Daily-LimitAnfragen, die dieser Schlüssel in seinem Tarif pro UTC-Tag stellen darf.
X-Daily-RemainingAnfragen, die diesem Schlüssel heute nach dieser noch bleiben: die Antwort auf den letzten Aufruf, der ausgeliefert wird, sagt 0. Richten Sie Ihr Tempo danach.
X-Daily-ResetWann der Tageszähler auf null zurückgeht: die nächste Mitternacht UTC, als ISO-8601-Zeitpunkt.
X-Monthly-LimitAnfragen, die der Tarif pro UTC-Monat erlaubt, für das ganze Konto.
X-Monthly-RemainingAnfragen, die dem Konto diesen Monat nach dieser noch bleiben. Nur ausgelieferte Aufrufe verbrauchen sie.
X-Monthly-ResetWann das Monatskontingent auf null zurückgeht: Mitternacht UTC am 1., als ISO-8601-Zeitpunkt.
Retry-AfterBei einem 429 die Sekunden bis zum Reset der Grenze, die abgelehnt hat: bei der Tagesgrenze höchstens ein Tag, beim Monatskontingent bis zu 31 Tage.
read-allowance
TypeScript
Reads GUNSPEC_API_KEY from the environment.
1/3
import { GunSpec } from '@buun_group/gunspec-sdk' const gunspec = new GunSpec()

Die Tages-Header stehen auf jeder /v1-Antwort eines Tarifs mit Tagesgrenze, auch auf einer abgelehnten Anfrage, die monatlichen auf jeder Antwort auf einen Aufruf mit Schlüssel, ein Aufruf ohne Schlüssel trägt also keinen der drei monatlichen Header. Jeder Wert ist das, was nach dem Aufruf übrig ist, der ihn geliefert hat. Sie kommen aus einem kurzen Cache, solange Sie weit unter einer Grenze liegen, und können früh um etwa eine Minute Verkehr zu hoch stehen; nahe einer Grenze sind sie live. Sie zu lesen kostet nichts, und eine bedingte Anfrage, die mit 304 antwortet, trägt sie ebenfalls und verbraucht nichts. Der Usage-Endpunkt ist selbst eine Anfrage, die zählt: nutzen Sie ihn zum Abgleichen, nicht zum Takten.

Eine Ablehnung ist kein Schluckauf, den man durchwiederholt. Bis die Grenze zurückgesetzt wird, wird nichts ausgeliefert, ein Aufruf davor wird abgelehnt, und ein kostenloser Schlüssel, der weiter anfragt, wird pausiert. Ein Job, der höflich scheitert, tut fünf Dinge.

  • Lesen Sie X-Daily-Remaining und X-Monthly-Remaining aus jeder Antwort und halten Sie an, sobald eines null erreicht, statt Aufrufe zu verbrauchen, die eine Grenze ablehnen wird.
  • Halten Sie bei einem 429 mit DAILY_CAP_EXCEEDED oder MONTHLY_CAP_EXCEEDED beim ersten an. Wiederholen Sie nicht in einer Schleife: Retry-After nennt die Sekunden bis zum Reset, error.resetsAt den Zeitpunkt, und keine kürzere Wartezeit hilft. Die SDKs warten einen Reset nur ab, wenn er höchstens 30 Sekunden entfernt ist (ihre Option maxRetryAfterMs): ein Aufruf, der eine Sekunde vor Mitternacht abgelehnt wird, wartet und gelingt, eine längere Wartezeit kommt sofort als Fehler bei Ihnen an.
  • Machen Sie nach dem Reset weiter, indem Sie die Arbeit einplanen, nicht indem Sie in einer Anfrage schlafen. Starten Sie den Job nach X-Daily-Reset erneut, oder nach X-Monthly-Reset, wenn der Monat verbraucht ist, wie es ein Cron-Eintrag täte.
  • Geben Sie jeder Last einen eigenen Schlüssel. Die Tagesgrenze gilt je Schlüssel, ein außer Kontrolle geratener Job kann also nicht den Tag eines anderen verbrauchen, und der Nutzungsbericht zeigt, welcher Schlüssel es war. Der Monat gehört dem Konto, die Summe begrenzt also der Tarif, nicht die Schlüssel.
  • Behalten Sie, was Sie schon gelesen haben. Eine Antwort aus Ihrem Cache kostet nichts, und eine 304-Revalidierung zählt weder gegen die Tages- noch gegen die Monatsgrenze, wie Caching erklärt.
stop-politely
TypeScript
Run jobs until an allowance is spent, and stop the moment it is. Returns
1/2
import { GunSpec, RateLimitError } from '@buun_group/gunspec-sdk' const gunspec = new GunSpec() // where it stopped, so a scheduler can run the rest after the reset.async function runWithinLimit(jobs, work) {  for (const [index, job] of jobs.entries()) {    try {      const res = await work(job)      const { dailyRemaining, dailyReset, monthlyRemaining, monthlyReset } = res.rateLimit

Geben Sie einen Aufruf aus, um den Spielraum zu erfahren, und nehmen Sie dann nur, was unter das kleinere der beiden Kontingente passt. Lassen Sie einen Puffer, denn die Remaining-Werte können früh am Tag um etwa eine Minute Verkehr zu hoch stehen, und überspringen Sie sauber, statt den Build scheitern zu lassen, wenn kein Platz ist. Eine bedingte Anfrage mit gespeichertem ETag antwortet mit 304 und denselben Headern und verbraucht nichts, wie Caching erklärt.

size-a-batch
TypeScript
One cheap call at the start tells the job how much room the key has.
1/2
const probe = await gunspec.firearms.list({ per_page: 1 })const { dailyRemaining, dailyReset, monthlyRemaining, monthlyReset } = probe.rateLimit

Klar gesagt, damit es nie überrascht.

  • Jeder Aufruf nach der ersten Ablehnung wird ebenfalls abgelehnt. Er zählt in die Tagessumme des Schlüssels, verbraucht aber den Monat nicht.
  • Ein kostenloser Schlüssel, der trotzdem weiter anfragt, wird pausiert. Er antwortet mit 403, KEY_ON_HOLD und einem Retry-After, das nennt, wann die Pause endet, eine Wiederholung bringt eine längere Pause, und ein neuer Schlüssel umgeht das nicht.
  • Pausiert werden nur Schlüssel, nie das Konto. Anmeldung, Portal und Abrechnung funktionieren wie gewohnt, Sie können sich also immer anmelden und den Tarif wechseln.
  • Ein bezahlter Tarif wird nie pausiert. Über seinem Monatskontingent wird er bis zum Reset abgelehnt, oder bis er auf einen größeren Tarif wechselt, was das Kontingent sofort anhebt.
  • Die Pause endet zu dem Zeitpunkt in Retry-After oder sofort, wenn Sie auf einen bezahlten Tarif wechseln.
  • Halten Sie beim ersten 429 an, und nichts davon passiert. Die Fehlerseite listet jeden Grund auf, KEY_ON_HOLD eingeschlossen.

Die Tagesgrenze gilt je Schlüssel, und die API lehnt einen Schlüssel nach dem anderen ab. Ein kontoweiter Durchschnitt verdeckt also genau den, der gleich zu scheitern beginnt. Der Usage-Endpunkt nennt unter daily und perKey die Anfragen jedes Schlüssels heute, was ihm bleibt und wann es zurückgesetzt wird, und unter currentMonth den Monat des Kontos: ausgelieferte Anfragen, was bleibt und wann er auf null zurückgeht. Dieselben Zahlen stehen in Ihrem Profil. Ihn zu lesen ist selbst eine Anfrage, die zählt: takten Sie also über die Antwort-Header und heben Sie ihn dafür auf, alle Schlüssel auf einmal zu sehen.

usage-alert.ts
javascript
// What the account has spent this month and what every key has spent today,// from the same counters the limits are checked against. This call is a// request that counts, so use it to see everything at once and pace from the// response headers, not from here.const { data } = await gunspec.usage.get() // The month is one figure for the whole account, and counts served calls only.const month = data.currentMonthif (month.percentage > 80) {  notify(`${month.remaining} of ${month.limit} requests left this month, back at ${month.resetsAt}`)} // The day is per key. Watch the busiest key, never the account's sum: the API// refuses one key at a time, so an average hides the key about to start failing.const daily = data.dailyconst key = daily?.busiestKeyTodayif (daily && key && key.percentage > 80) {  notify(`${key.keyName} has used ${key.percentage}% of today's requests, back at ${daily.resetsAt}`)} const mcp = data.mcp?.busiestKeyTodayif (mcp && mcp.percentage > 80) {  notify(`${mcp.keyName} is at ${mcp.percentage}% of today's MCP allowance`)}

Beides ist legitim. Das Signal sagt, worum es tatsächlich geht.

Was Sie sehenWas es bedeutet
Viele Anfragen, wenige verschiedene DatensätzeCachen. Sie fragen dieselben Zeilen innerhalb ihrer Lebensdauer erneut ab.
Schübe von 429 zur vollen StundeZeitplan entzerren und Jitter ergänzen. Ihr Cronjob stürmt, nicht Ihr Verkehr.
Stetig mehr verschiedene gelesene DatensätzeUpgraden. Das ist echte Nachfrage, die kein Cache wegnimmt.
PLAN_REQUIRED an einem EndpunktUpgraden oder darauf verzichten. Ein neuer Schlüssel im selben Tarif bekommt dieselbe Antwort.
Tiefenlimit beim Durchlauf erreichtDen Durchlauf in engere Scheiben filtern oder in einen Tarif ohne Seitengrenze wechseln.
MONTHLY_CAP_EXCEEDED vor MonatsendeErst cachen: erneute Abfragen unveränderter Datensätze lassen sich kostenlos revalidieren. Fragt jeder Aufruf etwas Neues, ist das echter Bedarf, und ein größerer Tarif hebt das Kontingent sofort an.
Ein Explorer-Schlüssel ist nach wenigen Tagen ausgeschöpftDas ist der Tarif: sein Monat sind wenige Tage seiner Tagesgrenze. Ein bezahlter Tarif ist für ein Skript, das jeden Tag läuft.