Skip to content

Build1 publisherNot yet confirmed elsewhere3 min readPublished

Your ETag polling budget assumes a token: unauthenticated 304s still cost GitHub quota

A measured run published on dev.to shows three If-None-Match requests returning 304 with empty bodies each took one off the 60/hour bucket. The discount is documented only for authorized calls.

The Engineer · Build desk

Drafted by a language model from the sources cited here and checked against its claim ledger before publication. How we use AISend a correction

What happened

  • On July 29, 2026, from one IP with no token, a GET to /repos/python/cpython returned 200 with a 5,996-byte body and x-ratelimit-remaining moved from 32 to 31.
  • Three subsequent If-None-Match requests sent without an Authorization header each returned 304 Not Modified with zero bytes of body, and x-ratelimit-remaining fell 31 to 30, 30 to 29, and 29 to 28.
  • Without an Authorization header, an If-None-Match request that returns 304 still decrements x-ratelimit-remaining; the unauthenticated bucket is 60 requests per hour, and the ETag saves bytes but not quota.
  • On GitHub's page "Best practices for using the REST API" the claim that 304 responses do not count against the rate limit appears five times; two of the five carry the authorization condition and three do not.
  • The page states: "Making a conditional request does not count against your primary rate limit if a 304 response is returned and the request was made while correctly authorized with an Authorization header."

Compiled by The EngineerSomething wrong?How this is made

Why it matters

A developer sat down to write a post about polling GitHub for free with ETags and published the opposite result: with no Authorization header, three consecutive If-None-Match requests came back 304 Not Modified with zero bytes of body, and each one still decremented the unauthenticated bucket of 60 requests per hour [3][1]. If you sized a poller on the bare sentence that 304 responses do not count against your rate limit, your budget is wrong by exactly the number of polls you make.

The run, dated July 29, 2026 from a single IP with no token, is small and legible [2]. A plain GET on /repos/python/cpython returned 200 with a 5,996-byte body and moved x-ratelimit-remaining from 32 to 31 [2]. Three conditional requests followed, all 304, all zero bytes: 31 to 30, 30 to 29, 29 to 28 [3]. Four requests, four decrements, no discount for the empty ones [13]. The ETag saved the bytes and none of the quota [1].

The interesting part is not that the documentation is wrong. According to the writeup, it is not [11]. On GitHub's "Best practices for using the REST API" page the claim appears five times, and two of those five attach the condition [6]. The strict one spells the header out: "Making a conditional request does not count against your primary rate limit if a 304 response is returned and the request was made while correctly authorized with an Authorization header" [7]. A bullet in the "Avoid polling" list carries the same condition in one word, "authenticated", and never mentions 304 at all [8]. The other three drop the clause, including "each 304 Not Modified response is fast and does not use your rate limit" and, further down under "Make requests that can be cached", "A conditional request only saves you time and rate limit if the endpoint returns 304 Not Modified" [10]. Three of five mentions omit the condition [15]. All four curl examples on the page send Authorization: Bearer YOUR-TOKEN, so the page is written for a reader who already has one [11]. Read whole, it holds; quoted a sentence at a time, it produces a poller that dies at the top of the hour.

The author is careful about what the measurement proves, and the caveat is worth repeating: a null result on the unauthenticated branch does not verify the authenticated one [4]. The numbers are equally consistent with the clause being load-bearing and the discount working with a token, and with the discount being dead for everybody and the docs being stale, and separating those needs a token he did not have [4]. He says he chose the generous reading and labels that a choice rather than a finding [5].

The operational arithmetic is unforgiving either way. Unauthenticated, a conditional poll once a minute consumes the entire 60-per-hour allowance whether or not anything changed [14]; the three 304s in this run cost 5 percent of an hour's budget for nothing [9].

Two things to watch. First, whether the authenticated branch still discounts at all: that is one token, one 304, and a logged delta on x-ratelimit-remaining, and nobody should assume it until someone posts the header trace. Second, the docs source file in github/docs, which the author read on July 29 and again on August 19, 2026 without change [12]. Until then, instrument the counter rather than the sentence.

Loading claim ledger
Loading source directory links
Loading share composer
Loading topic controls
Loading related stories