In this article
September 22, 2026
September 22, 2026

On October 19, your unauthenticated GitLab automation gets 60 requests an hour

GitLab.com cuts anonymous callers to 60 requests an hour on October 19. What to fix before the October 7 and 14 brownouts, and why quota is becoming an identity attribute.

Explore with AI
Open in ChatGPT
Open in Claude
Open in Perplexity

On October 19, 2026, GitLab.com starts enforcing rate limits that follow your subscription tier, and any request that arrives without credentials drops to 60 per hour per IP address. Free accounts and anonymous traffic go first; Premium and Ultimate move in January 2027. Before then there are two dress rehearsals, October 7 and October 14, 15:00 to 19:00 UTC, when GitLab switches the new limits on for Free and unauthenticated traffic and then switches them back off.

If you run scripts or agents against GitLab.com, those two four-hour windows are the cheapest production test you will get this year.

A horizontal timeline, not drawn to scale, with five markers: September 17, 2026, change announced; October 7 and October 14, brownout windows from 15:00 to 19:00 UTC; October 19, Free and unauthenticated limits enforced; January 2027, Premium and Ultimate move.

The date is the reason to read this now. The reason it will still matter in a year is underneath it: platforms are starting to meter capacity by who is calling, because agent traffic broke the assumption that requests come from people. GitLab is not the first and will not be the last.

Most teams can stop reading here

GitLab says almost all users are already inside the new limits and won't notice any change. Browsing, pushing, pulling, and ordinary CI carry on. The company names the exception itself: some heavy automation and a small number of Free-tier workloads will reach the new ceilings. This is a GitLab.com change only. Self-Managed and Dedicated limits stay with whoever operates your instance.

The trap is narrower than the headline and worse than it sounds. Unauthenticated requests are capped no matter where they come from, including automation running against a paid account without credentials. Your company can be on Ultimate and your nightly sync script still lands in the 60-per-hour anonymous pool, because nobody ever gave that script a token. The invoice does not travel with the request. The credential does.

The numbers

GitLab's documentation still labels these limits "proposed and not in effect yet," even though the announcement gives firm dates. Sustained limits, measured hourly:

GitLab.com sustained rate limits by plan, effective October 19, 2026 for Free and unauthenticated traffic, January 2027 for Premium and Ultimate.
Rate limit Free Premium Ultimate
Authenticated traffic for a user 5,000 each hour 15,000 each hour 25,000 each hour
Unauthenticated traffic from an IP address 60 each hour 60 each hour 60 each hour

Each plan also carries a per-minute burst limit (100 on Free, 1,250 on Premium, 2,000 on Ultimate), but the hourly number is the one to plan against, because it takes precedence. By GitLab's own arithmetic, running flat out at the burst rate reaches the hourly limit in roughly 50 minutes on Free, 12 minutes on Premium, and 12.5 minutes on Ultimate. The burst limit is a ceiling on spikes, not a throughput budget.

A bar chart of three plans against a 60-minute axis, showing how long each hourly allowance lasts when held at that plan's per-minute burst rate. Free, at 5,000 an hour and 100 a minute, lasts 50 minutes. Premium, at 15,000 an hour and 1,250 a minute, lasts 12 minutes. Ultimate, at 25,000 an hour and 2,000 a minute, lasts 12.5 minutes. The rest of each hour is marked throttled, so the more expensive plans empty far sooner.

The old per-minute table does not go away. GitLab checks your plan's limits first, then the existing limits, and whichever is lower applies, so authenticated API traffic for a user is still capped at 2,000 requests each minute regardless of tier.

Now the number the announcement doesn't put in a table. Unauthenticated traffic from an IP address is limited today at 500 requests each minute. On October 19 the effective anonymous ceiling becomes 60 requests an hour. That is the change, and it is much larger than the tier table makes it look.

The second-largest gap is the one between the two rows: 60 versus 5,000, anonymous versus signed in on the cheapest plan there is. A commenter on the Hacker News thread, which ran to 127 comments the day the post went up, flagged that split as the buried context of the whole announcement. 60 an hour is punishing. 5,000 an hour is a little over one per second, and fine for almost anyone.

How small is 60 an hour in practice? Another commenter counted the requests behind a single gitlab-org/gitlab project page: 12 GraphQL calls plus 2 REST calls. Roughly four page loads an hour for an anonymous client.

A grid of 60 squares, one per request in an anonymous hourly allowance, grouped into four rows of 14 with only four squares left over in a fifth row. Each row of 14 is one page view, because loading a single gitlab-org/gitlab project page costs 12 GraphQL calls plus 2 REST calls. Sixty requests an hour is four page loads.

Quota lives on the principal that authenticates the request

Here is the sentence to get right, because it is easy to get backwards: GitLab applies the new limits per user.

GitLab counts requests against the user, not against the token that carried them. If you react to this announcement by minting a fresh personal access token for each of your six automations under your own account, you have bought exactly zero additional capacity: six tokens, one user, one hourly bucket.

A separate bucket requires a separate principal. On GitLab.com that means a service account: a user account that represents a non-human entity, authenticates with a personal access token, doesn't consume a seat, isn't a billable user, and can't sign in through the UI at all.

One honest caveat before anyone builds a fan-out strategy on this. The docs publish per-user numbers in the by-plan tables; the announcement describes the limits as applying per user and per top-level group. Those are not the same statement, and GitLab hasn't published the group-level figure. A pile of service accounts under one namespace is not an infinite quota trick, and it would be a bad idea to design as if it were. Treat separate principals as isolation and attribution first, headroom second.

Three callers on the left all point at a single gitlab.com/api/v4 box in the middle, and each connects to its own separate quota box on the right. An anonymous request with no credential gets 60 requests an hour, shared with every other anonymous caller on that IP address. Maria's personal token, one of six she holds, draws on her single hourly budget of 5,000 on Free up to 25,000 on Ultimate. A sync-bot service account draws on the same plan limit, counted separately, because it is its own principal.

What a service account actually buys

Two things, and quota multiplication is not one of them.

Escaping the anonymous floor. That is the real cliff on October 19, and the fix is mechanical: a personal access token, an OAuth token, or the CI/CD job token all move a request off the 60-per-hour anonymous allowance and onto your plan's limits.

Which plan's limits? GitLab's answer is that your user limit is the highest subscription tier available to you, so a principal that belongs to an Ultimate group gets the Ultimate allowance. A service account in your paid namespace inherits your paid ceiling, which is the whole point.

Attribution. When a 429 shows up at 3 a.m., the question is which workload burned the window, and a shared human token cannot answer it. GitLab lists service accounts in group and project membership lists as service accounts rather than regular users, which makes the inventory auditable instead of guesswork. One workload, one principal, one line in the membership list.

Operational details worth knowing before you start:

  • On GitLab.com, only top-level group Owners can create service accounts.
  • Free allows up to 100 service accounts per top-level group on GitLab.com; Premium and Ultimate are unlimited.
  • Service accounts went generally available on Free in GitLab 18.11, along with subgroup and project service accounts.
  • Group service accounts can be invited to their own group and any descendant subgroups or projects; project service accounts are available only to the project that created them.
  • They are always marked as external users.
  • Assigning a service account the Owner role for a group means its personal access tokens also carry Owner on that group. Scope accordingly.
  • GitLab.com's rate limits apply to service accounts exactly as they do to people.

The five-item checklist

  • Authenticate every caller. Inventory anything that talks to GitLab.com (cron jobs, webhooks, dashboards, status badges, agent tool calls, that Python script from 2023) and confirm each one sends a credential.
  • Give each workload its own principal. Not for extra quota. So that the next 429 names a suspect.
  • Read RateLimit-Remaining and slow down before you hit zero. All responses, throttled or not, carry RateLimit-Limit, RateLimit-Remaining, and related headers, and GitLab points at RateLimit-Remaining as the quickest signal of where you stand.
  • Honor Retry-After, then back off exponentially. Throttled responses include Retry-After in seconds and a RateLimit-ResetTime with the same information as a timestamp. GitLab's guidance is to wait the stated interval and increase the delay if throttling continues.
  • Batch, cache, and paginate instead of polling. Tight polling loops are the fastest way to spend an hourly allowance on responses nobody read.

A client that does the last three looks roughly like this:

  
const MAX_ATTEMPTS = 5;
const sleep = (ms: number) => new Promise((r) => setTimeout(r, ms));

async function throttledFetch(url: string, token: string): Promise<Response> {
  for (let attempt = 0; attempt < MAX_ATTEMPTS; attempt++) {
    const res = await fetch(url, { headers: { "PRIVATE-TOKEN": token } });

    if (res.status !== 429) {
      const limit = Number(res.headers.get("RateLimit-Limit"));
      const remaining = Number(res.headers.get("RateLimit-Remaining"));
      // Ease off inside the last tenth of the window, not at zero.
      // Proportional, so it works the same on Free and Ultimate.
      if (Number.isFinite(limit) && remaining < limit * 0.1) {
        await sleep(1_000);
      }
      return res;
    }

    const retryAfterMs = Number(res.headers.get("Retry-After") ?? 0) * 1_000;
    const backoffMs = 2 ** attempt * 1_000 + Math.random() * 250;
    await sleep(Math.max(retryAfterMs, backoffMs));
  }

  throw new Error(`Still throttled after ${MAX_ATTEMPTS} attempts`);
}

// One service account token per workload, not one shared human token.
const res = await throttledFetch(
  "https://gitlab.com/api/v4/projects?per_page=100",
  process.env.REPO_SYNC_TOKEN!,
);
  

The failure mode to design around: rate limiting responses for the Projects, Groups, and Users APIs don't include the informational headers at all, and GitLab warns you can receive a 429 even when the headers on your previous response showed remaining quota. Header-watching is an optimization. The 429 path is the contract, and it needs to work when the headers lie.

One boundary that surprises people: the new plan limits cover API requests, web requests, and authenticated Git over HTTPS. Unauthenticated Git over HTTPS doesn't count against the new anonymous limit and stays subject to the current per-IP limit instead.

Two scope questions the announcement doesn't answer, both raised and left hanging in the Hacker News thread: whether raw-file URLs are covered, and whether registry.gitlab.com image pulls count. If you depend on either, limits@gitlab.com is the address GitLab gave for exactly this.

The strongest objection

The one that got the most sympathy is about open source. GitLab's advice to maintainers of busy public projects is to ask the automation calling them to sign in, make the project private, or upgrade, and commenters pointed out that this reads as guidance that hurts open source rather than helping it. "Make it private" is a strange thing to suggest to someone who published a repository on purpose, and there are integrations that genuinely cannot authenticate. GitLab names public status badges as the obvious case and offers an email address for the rest.

But the sharper objection is technical, and it aims at the rationale rather than the fallout. Anonymous quota is scoped to an IP address. A serious scraper rotates IPs and barely notices a per-IP cap. A university network, an office behind one NAT gateway, a CI fleet sharing an egress address, or anyone behind CGNAT notices immediately. The limit lands hardest on the traffic it wasn't designed to stop.

That objection is correct as far as it goes, and GitLab hasn't answered it. What can be said for the change is that a shared unauthenticated pool is a commons, and what's draining it right now is automated, cheap to run, and multiplying. GitLab expects platform load to grow several times over this year. A commons gets consumed by whoever is most aggressive. Making callers identify themselves at least moves the cost onto the caller, and it's the precondition for any smarter enforcement later. Per-IP anonymous limits are a blunt instrument. They're also the only one available before you know who is calling.

This is not the last platform to price capacity this way

The direction is visible in GitLab's own roadmap. It is building a product view that shows your usage against your plan's limits, due later this year, and designing a way to purchase capacity above the standard plan limits. Once you can buy headroom, it gets hard to keep calling this a fair-use guardrail. It's a line item.

The precedents are already there. Docker Hub runs this model today: 100 pulls per six hours for anonymous callers, 200 for a free authenticated Personal account, no pull limit on paid plans. GitHub's unauthenticated REST limit is 60 requests per hour against 5,000 for an authenticated user. That is the same 60, worth knowing before treating GitLab's number as uniquely harsh. It cuts the other way too, as one commenter noted: GitLab's new paid tiers land in roughly the same territory as GitHub's, so upgrading isn't the escape hatch the pricing page implies.

The best summary of the whole announcement came from the thread, four words into a reply: the post "should've been titled, have your agents sign in."

Before October 7, list every non-human caller that touches GitLab.com, and for each one write down the principal it authenticates as. Any row where that column reads "none" is a workload that will start failing in a four-hour window three weeks from now. Any row where two workloads share a principal is a 429 you won't be able to attribute. Fix the first set this month, and the second set before your platform starts selling you the capacity you could have been budgeting all along.

Sources