Why Backstage catalog processing hits Git provider rate limits
When a platform team's catalog goes stale, or its GitHub, GitLab or Bitbucket integration starts returning errors, the usual cause is that the catalog is asking the source control platform for more reads than its API quota allows. Tuning the processing interval and moving freshness-critical updates to webhooks keeps catalog freshness inside those quotas, and it does so without giving up the reliability of periodic reconciliation. The problem usually appears gradually: a few hundred repositories become thousands, a freshness target that was once hourly becomes minutes, and a single token that was shared by a few integrations ends up serving the whole portal.
On each processing cycle, the catalog reads the locations it knows about, fetches descriptor files such as catalog-info.yaml from each repository, and compares what it finds with what is already stored. Every one of those reads consumes provider API calls, and discovery providers add listing calls on top of that. The processingInterval setting in the catalog configuration is best understood as a minimum gap between cycles, not a guaranteed cadence. A slow or rate-limited cycle simply runs later, and teams should not read a configured interval as a promise about how fresh any single entity will be. Confirm the exact behavior of your Backstage version in the catalog configuration reference before you set freshness expectations with stakeholders.
Teams need this work once the number of repositories, the freshness target or the number of consumers of a shared token grows past what a default setup was designed for. Large repository counts multiply reads per cycle, short freshness targets push the interval down, and shared tokens mean that one integration's traffic can exhaust the quota that another depends on. Before changing settings, measure the current read volume per cycle, the rate-limit headers your providers return, and how long a full refresh takes. The Backstage integrations documentation and your provider's own rate-limit documentation are the right places to confirm which limits apply to your app identity, GitHub App, or token type.
Processing interval, webhooks and provider schedules compared
This table compares processingInterval, scheduled provider refresh and webhook-driven updates for Git provider catalog ingestion. Read each row as a trade-off: freshness and API load pull in opposite directions, and deletion handling often decides which option fits a large estate.
| Option | Freshness | API call volume and deletions | When it fits |
|---|---|---|---|
| processingInterval as a minimum | Changes appear on the next processing pass after the interval elapses, so latency depends on the interval and queue length | Interval-driven reads repeat even when nothing changed; deletions depend on the provider's refresh behavior, so confirm them in your version | Small repository estates where a few locations are registered and polling cost is low |
| Scheduled provider refresh | Freshness is set by the provider schedule, so it can be uneven across sources and teams | Predictable request bursts at each run; spread schedules across providers to avoid hitting rate limits together | Large estates that need steady, controllable load and can accept delay between scheduled runs |
| Webhook-driven updates | Changes arrive close to when they are pushed, which suits teams that expect quick catalog updates | Requests happen on events rather than on a timer, so idle repositories cost little; the webhook receiver must be secured and kept reliable | Large or active estates where most changes come from pushes and a receiver can be operated safely |
| Deletion handling | Removed files may only disappear after the next full read, so confirm how your provider and version behave | Full reads are the costly part when deletions must be detected; webhooks alone may not report every removal | Plan deletion checks on a schedule even when webhooks are in use |
| Operational effort | Interval changes are a single config edit, which is simple to test | Scheduled refresh needs schedule planning across providers; webhooks need endpoint, secret and retry operations | Assess your team's capacity to run an ingress path before choosing webhooks |
| Signs of rate limiting | Stale entities and slow ingestion usually appear first in logs and catalog timestamps | Repeated throttling responses from the provider and growing processing backlogs indicate too many reads | Watch both signals after each change and adjust the interval or schedule |
| Verification step | Compare a known change in the source with its catalog update time | Check provider usage counters and API responses after each configuration change | Confirm the chosen mix in a staging catalog before applying it to production |
How processingInterval and provider schedules shape API traffic
The processingInterval setting on each catalog provider is a floor, not a schedule. Each provider integration runs on its own timer, and the actual gap between refreshes is the configured interval plus whatever time the backend spends on the previous cycle, waiting for other work to finish, and handling any retries. Under load, a cycle that takes longer than expected pushes the next run later, which is usually what you want for API budgets. The exact key names, their defaults and how the timer behaves for your version should be confirmed in the current Backstage catalog documentation before you set any value, because these details have changed across releases.
The traffic a provider generates depends on what each cycle reads, not on the interval alone. A GitHub, GitLab or Bitbucket discovery provider typically lists organizations or groups, then repositories, then branches or files that match your filters, and every catalog file it finds is fetched and re-checked against the stored entity. A single cycle over 300 repositories with one catalog-info.yaml each, plus a list call per organization and a file fetch per repository, multiplies quickly. Adding branches, multiple entities per file or extra locations multiplies it again. Before you tune the interval, count the repositories, branches and descriptor files each provider will scan, multiply by the cycle frequency, and compare that total with the rate limit your token or app installation actually has. Ask your provider administrator for the current limit, because it depends on the authentication method and plan.
Choosing between polling intervals and webhook-driven updates
Every catalog refresh you schedule spends API quota at your Git provider, and every webhook you subscribe to adds a delivery path that can fail silently. The cards below are the decisions that connect those two costs, and each one names who should own it before the configuration is written.
Freshness target per entity class
Decide how stale each kind of entity may be before someone notices. Services that owners edit daily can tolerate a shorter wait than a stable library or a dormant repository. Set processingInterval as a floor for the whole catalog, then use provider-specific discovery schedules to relax the rest. Ask the platform team and catalog owners to agree on these classes in writing, because every shorter interval multiplies the calls your provider sees.
Whether to subscribe to push events
Webhooks let GitHub, GitLab or Bitbucket tell Backstage that a catalog-info.yaml changed, which removes most polling traffic. The cost is an inbound endpoint, a shared secret, and a delivery pipeline you must monitor. Decide this with the security team, since the endpoint accepts traffic from outside your network. Confirm in the provider's documentation which events each integration can subscribe to before assuming full coverage.
Handling deletions when webhooks miss events
A dropped delivery can leave a deleted repository in the catalog or hide a renamed file until the next scheduled run. Webhooks rarely cover every path, so keep a periodic reconciliation pass that compares what the provider reports with what the catalog holds. Assign one owner for reconciliation failures and agree how long a stale entity may remain before it is marked as an orphan.
Shared versus per-integration tokens
A single token shared across all discovery jobs is simple to operate, but all of them draw from one rate limit pool and one revocation point. Separate tokens or GitHub App installations per integration isolate quota and make audit trails clearer. Ask your identity and security owners to choose the credential type, scope each token to read-only access on the repositories it needs, and set a rotation owner.
Provider-specific rate limit behavior
GitHub, GitLab and Bitbucket each enforce their own quotas, reset windows and responses when a limit is hit, so one tuning profile does not transfer cleanly. Read each provider's current rate limit documentation for the plan and token type you use, and confirm the figures with the provider rather than copying them from older articles. Platform engineers should own these profiles and review them after any change in provider plan.
Separating discovery from entity updates
Discovery finds which repositories or files exist, while entity updates read the contents of each one. Running both on the same schedule multiplies calls for large organizations. Let discovery run less often and let webhooks or targeted refreshes handle content changes. The owner of discovery should also own the list of excluded repositories, so that archived or experimental projects do not keep consuming quota.
Setting up interval tuning and webhook updates
This sequence moves a Backstage catalog from polling every provider on a fixed schedule to a setup where webhooks deliver most changes, a longer reconciliation schedule catches what they miss, and API consumption stays within the limits each provider enforces. Each step closes with a check you can run before moving on.
Inventory repositories and integrations per provider
The platform team lists every GitHub, GitLab and Bitbucket integration the catalog reads, along with the organizations, groups or projects each one covers and the number of repositories that contain a catalog-info.yaml or are discovered by a provider. Record which integrations use the Backstage integration configuration, which use entity providers, and which use static locations. Owners of each source confirm the list. Done when every provider has a row with its repository count, scope and refresh method, and no repository is read by two integrations without a reason.
Measure current API consumption from provider usage data
Using the usage reporting each provider offers for its API, such as rate limit headers in recent responses and the administrative audit or usage views available on your plan, record how many requests the Backstage backend makes per hour and how close those counts come to the limit for each token. Compare the figures with the processingInterval values currently set. Confirm the exact limit values and reporting options with each provider's documentation for your plan, since they differ by product and account type. Done when you have a baseline per provider showing peak consumption and the share of the limit it uses.
Set processing intervals per integration
Set processingInterval for each provider integration in the catalog configuration as a minimum wait between full reads, not a fixed schedule. Start with longer intervals for sources where changes are rare, such as archived or infrequently updated organizations, and shorter ones only where a team needs quick visibility. Keep catalog.processingInterval consistent with the backend's own refresh logic and document the chosen value for each provider in the change record. Done when the configuration reviews cleanly and a dry-run of the backend shows each integration reading on its own interval.
Configure authentication and token scopes
Create one credential per provider integration, using a GitHub App, GitLab token or Bitbucket credential scoped to read the repositories it needs and nothing else. Avoid sharing one personal token across providers, because limits and revocation then apply to every integration at once. Store the credentials in your secrets manager and reference them from the integration configuration instead of committing them. Confirm the rate limit that applies to each credential type with the provider's documentation. Done when each integration authenticates with its own credential and the backend logs show successful reads with no authorization errors.
Add webhooks and their secrets
Register a webhook on each provider that sends push and repository events to the Backstage backend endpoint for that integration, and set a shared secret on both sides so the backend can verify each payload signature. Restrict the endpoint to the networks the provider uses where your network policy allows it, and reject unsigned requests. Only subscribe to the events the catalog reacts to, so the backend does not process unrelated traffic. Done when a test push produces a catalog update in the logs and a deliberately unsigned request is rejected.
Enable a reconciliation schedule
Keep the scheduled full read running at a longer interval than before, so it catches webhooks that were dropped, delivered out of order, or sent while the backend was down. Choose the interval based on the baseline from step two and the number of entities per provider, and note that the reconciliation read still consumes API calls. Check that the schedule does not start all providers at the same moment. Done when a log entry shows the reconciliation run completing and the rate limit headers show remaining capacity after the run.
Verify with a test change
Make a small, reversible change to a catalog-info.yaml in a test repository, such as a description edit, and time how long the catalog takes to show it. Repeat with the webhook disabled to confirm the fallback still picks up the change on the scheduled read. Then review the provider usage data and confirm that consumption stayed below the limits you recorded in step two. Record the observed latency and consumption in the change record. Done when both paths show the change in the catalog and the usage data stays within the planned headroom.
Signs that Git provider limits are being hit
How do I know if my Backstage catalog is hitting GitHub or GitLab rate limits?
The most common sign is entities that stop refreshing while the rest of the catalog keeps working, combined with provider error responses in the backend logs. Throttling usually appears as HTTP 403 or 429 responses from the provider API, sometimes with a retry-after or reset header. Confirm the exact log messages and metric names in your own deployment, since they depend on your Backstage version and the provider integration you use. Check the backend logs for the provider's hostname and the words rate limit, throttle or secondary limit, then compare the timing against your processing schedule.
Why are some catalog entities not updating after I change catalog-info.yaml?
When a provider is throttled, the catalog cannot read the changed file, so the entity keeps its last successful state and its refresh timestamp stops moving forward. The change appears only after a later processing cycle succeeds for that location. Pick one entity you edited, note the time of the commit, and check whether its last-updated value advances in the catalog UI or API. If it does not advance across several cycles while other entities do, a provider limit or an authentication problem is the likely cause, and the logs will show which.
What does a growing catalog processing time look like in Backstage?
Processing time grows when each refresh spends more time waiting on retries, backoff or repeated API calls instead of parsing files. You may see individual processing cycles lasting longer over several days, a backlog of entities waiting to be processed, or the same locations appearing in logs repeatedly. Compare the duration of recent cycles with your baseline from a quiet period, and check whether the growth follows an increase in the number of registered locations or a change in processingInterval. The metric names for catalog processing duration and queue length vary by version, so confirm them in your metrics endpoint before building alerts.
Why do entities show old owners or stale last-updated values?
Ownership and freshness are read from the same processed data, so a throttled provider can leave spec.owner, relations and last-updated values frozen at an earlier state. This matters for audits and for ownership routing, because a team that has changed a component may still appear as the old owner. Sample several entities with recent ownership changes and compare their displayed owner with the file in the repository. A mismatch that persists across processing cycles points to a refresh that is failing, not to a modeling error.
Which provider dashboards show that API quota is exhausted?
Git providers expose request usage in different places, and the Backstage team does not control those dashboards, so confirm the current locations in each provider's documentation or admin console. For GitHub, check the rate limit status for the GitHub App or token that Backstage uses, and for GitLab and Bitbucket, check the admin or API usage views available to your plan and tenant. Look for a sustained plateau at the limit that lines up with your processing cycles. Record which credential the catalog uses, because several integrations sharing one token will exhaust quota faster than expected.
Which log messages should I search for when catalog refreshes fail?
Search the backend logs for provider hostnames together with error codes such as 403, 429 and 5xx, and for words like rate limit, abuse or retry. Also look for catalog processing errors that name a specific location URL, since those identify which repositories are failing. Because message wording changes between releases and between integrations, copy the exact strings from a failure in your own environment and build alerts from those rather than from examples. Include the Backstage version in your notes, because the next release line can change log output.
Can a single failing Git provider slow down the whole catalog?
It can, if the failing provider shares a processing schedule, a worker pool or a token with healthy sources. Retries against a throttled provider consume time that would otherwise process other locations. To test this, group your locations by provider and compare processing times per group during a throttling event. If the other providers stay steady while one group degrades, you have an isolation problem to address in the scheduling design, which is covered in the sections on provider schedules and webhooks.
Securing webhooks and provider tokens
Webhooks and provider tokens let the catalog react to changes without polling, but each one is a path into your source control and a credential that can be misused. The cards below cover the controls that keep that path narrow, from validating incoming events to rotating the tokens behind them.
Validate every webhook signature
Configure a shared secret on each provider webhook and reject any request whose signature does not match before the catalog processes it. Store the secret in your secrets manager, not in the app-config file in Git. Check that a deliberately unsigned test request is refused, because an endpoint that accepts unsigned events lets anyone trigger catalog refreshes and push crafted payloads.
Grant read-only catalog scopes
Create a dedicated token for catalog discovery with read access to the repositories and files it needs, and nothing that writes. Scope it to specific organizations or projects rather than an entire account. Confirm the exact scopes each provider requires for reading catalog-info.yaml files and for receiving events, since these differ by platform and change over time.
Rotate credentials on a schedule
Set an owner and a rotation interval for every provider token and webhook secret, and record the expiry date where the provider supports one. Rotate by issuing the new credential, deploying it, and confirming catalog processing still succeeds before revoking the old one. Plan the rotation window so a failed rollout does not stop ingestion for every provider at once.
Restrict the webhook endpoint network path
Where a provider publishes the source IP ranges it sends webhooks from, allow only those ranges at the load balancer or ingress. Confirm the current ranges in the provider's documentation before writing rules, and review them when the provider changes them. Treat IP filtering as a layer on top of signature checks, since addresses can change and are not a substitute for authentication.
Audit token use and failures
Track which token made each provider call and alert on sudden spikes in failed authentication or unexpected repository access. Review the provider's audit log for the integration account at a set cadence. Repeated signature failures can indicate a misconfigured secret after rotation or an attempt to flood the endpoint, so both should reach the team that owns the catalog.
Frequently asked questions about Backstage catalog rate limits
How much effort does tuning Backstage catalog processing take?
The effort depends mainly on how many repositories and catalog locations you have, how many of them carry catalog-info.yaml files, and whether you already run webhooks. A small catalog with a few providers and a generous processing interval is usually a short configuration exercise. A large catalog across several Git platforms needs provider-by-provider schedules, webhook setup, a check of API quota usage under real load, and a rollout that you can watch over several cycles. Ask any partner to size the work from a written inventory of your sources, not from the number of plugins you want. Exact durations depend on your environment and should be confirmed in the scope.
Can our internal team tune catalog rate limits without outside help?
Yes, a team that already operates Backstage and has administrative access to its Git platforms can usually make the changes itself. The harder part is knowing which signs point to rate limiting, how each provider counts requests, and which schedules overlap. Teams that lack a Backstage operator, or that have never read their backend logs for provider errors, often benefit from a short external review. Whether to hire help depends on who on your staff owns the backend configuration and can test webhook delivery safely.
What do we need to provide before a tuning engagement starts?
A tuning engagement needs a few specific inputs from your side:
- A written inventory of Git sources, with the owner, provider and approximate count of catalog files for each
- The service accounts or GitHub Apps, GitLab tokens and Bitbucket credentials used today, and the named person who owns each one
- Access to the Backstage backend configuration and logs, and to the Git platform audit or rate limit views where your administrators can grant them
- Network access that lets your Git platform deliver webhooks to a Backstage endpoint you control, along with a decision on how that endpoint is protected
Confirm token ownership early, because rotating a shared token can break several plugins at once.
How long does a change window need to be for catalog tuning?
The window depends on what you change. Lowering a processing interval or adjusting provider schedules is a configuration change that can be applied with a backend restart, and its effect is visible over the next few processing cycles. Adding webhook-driven updates needs a window for endpoint exposure, signature verification and a test in which you push changes to a sample repository. Plan for at least one full catalog refresh to observe the effect. Ask the partner to state the window in the written scope and to name which steps can be done without downtime.
What signs show that Git provider rate limits are being hit?
The most common signs are repeated provider errors in the Backstage backend logs for the catalog, entities that update late or not at all, and bursts of API calls that line up with your processing cycles. Check the rate limit headers or status pages your Git platform exposes for the credential Backstage uses. If one token serves several plugins, quota loss in one place can appear as catalog delays in another. Confirm each signal against the platform's own documentation before changing configuration.
What should a quote for Backstage rate limit tuning include?
A quote should match the written scope and state the following:
- The providers, repositories and catalog locations covered, and which are out of scope
- The changes to processing intervals, provider schedules and webhook handling, with the environments they apply to
- Testing steps, including how a webhook and a rate limit test will be run without affecting production
- Who performs each step and what you must provide, including token and endpoint access
- Change window requirements, handover documentation and any follow-up review of logs after go-live
Fixed-scope agreements list deliverables and timelines in writing before work starts, so ask for them explicitly. Ongoing monitoring or tuning after go-live is a separate retainer and should be quoted that way.
Do webhooks remove the need for processing intervals?
No. Webhooks make updates faster and reduce polling, but a processing interval still acts as a minimum refresh cycle and a safety net for missed deliveries. Webhook events can be dropped, delayed or misconfigured, so most teams keep a reasonable interval as a backstop. The goal is to set the interval based on how stale you can tolerate catalog data being, then use webhooks for the entities that change often. Confirm the exact behavior with the Backstage catalog documentation for your version before you rely on it.
How DeployOpen scopes Backstage catalog tuning
DeployOpen plans, implements, tests and documents Backstage catalog processing and Git provider rate limit controls inside your own infrastructure. The work starts from a written scope that sets which providers, repositories and entity sources are in play, and it ends with a handover your team can run without us.
How we scope
- Inventory every Git provider, catalog location and entity provider that feeds the catalog today.
- Record the current API quota, request volume and processing interval for each provider and integration.
- Agree in writing which providers, repositories and refresh targets are covered before work starts.
How we build
- Set processingInterval values per source, then move high-change repositories to webhook-driven updates.
- Test each change in a non-production Backstage instance against a copy of the real provider configuration.
- Confirm rate limit headers, error logs and ingestion timing before and after each change.
How we hand over
- Deliver the configuration, a schedule per provider and a written record of the trade-offs chosen.
- Document the warning signs of rate limiting and the log entries your team should monitor.
- Walk your platform engineers through the runbook for changing intervals and adding new webhooks.
How we support
- Fixed-scope engagements end at a written handover, and changes after that go through a new scope.
- Ongoing support, upgrades and continuous testing are available as a retainer.
- Retainer work can cover provider limit changes, new repositories and Backstage version upgrades affecting ingestion.

