For now, I ran a small experiment on my side:
Short version: I do not see evidence that this is the documented Hub resolver rate limit, and I would not conclude that your repository is being intentionally bandwidth-throttled. However, I was able to reproduce a real repo/path-dependent throughput difference, even after controlling for the file contents.
The more interesting clue is lower down in the Xet storage path: for the same Xet file identity, the two repository/ref contexts returned different and very stable reconstruction layouts. The slower Softology path was also more fragmented at several locations, and those were the locations where the comparison tended to favor the other path most strongly.
That is interesting because Hugging Face’s own Xet file-reconstruction documentation explicitly describes fragmentation — many short ranges spread across xorbs — as something that can hurt read performance through additional lookups/range fetches, poorer locality, and extra request overhead. The deduplication specification even describes fragmentation prevention as a deliberate performance tradeoff.
So my current working hypothesis would be something more like:
repo/ref-specific Xet reconstruction/layout state, possibly combined with CDN/Bridge/backend state
rather than simply:
a bandwidth quota applied to this repository.
I would still keep the latter as an open question for HF staff rather than declaring it impossible from the outside.
If the immediate goal is simply to get users downloading at a reasonable speed, using the currently faster mirror/path seems perfectly reasonable. I would keep the original file/path around for now, though, because it is a surprisingly useful reproducer.
What I tested
The first complication was that the two same-named files in the original comparison were not a clean one-variable comparison.
I found another file in mcmonkey/swarm-models, the -v2 variant, that shares the same Xet file hash as the Softology file. That gave a much cleaner A/B control:
- A: Softology repository/path
- B: mcmonkey
-v2 repository/path
- same Xet file identity
- same logical byte ranges
I then sampled multiple ranges rather than downloading the whole ~15 GB file.
In the larger paired run:
| Check |
Result |
| sampled A/B byte SHA-256 matched |
12 / 12 offsets |
| same visible final CDN hostname |
12 / 12 |
| same visible peer IP |
12 / 12 |
| same reported HF CDN POP |
12 / 12 |
| B faster than A |
9 / 12 |
| median B/A wall-throughput ratio |
~1.37Ă— |
This was from one Colab/GCP vantage point, where both paths ended up at the same visible us.gcp.cdn.hf.co endpoint and gcp-us-east1 POP.
That does not reproduce the full ~2 MB/s vs ~30 MB/s magnitude you observed, so I would not claim it reproduces your exact environment. But it does reproduce the important qualitative part: changing the repo/path while keeping the sampled content and visible CDN destination controlled can change the transfer behavior.
Earlier randomized paired measurements were also in the same general direction; in one 10-offset run B was faster in all 10 pairs.
The first-byte timing was not sufficient to explain the difference either. In several comparisons the TTFB was similar, or even slightly worse for B, while the body-transfer rate was much higher. So this does not look like only a slow initial resolver redirect.
The Xet reconstruction difference was the most useful clue
The Xet protocol separates a file’s identity from the particular reconstruction recipe used to read it.
A reconstruction is an ordered list of terms. Each term points to a contiguous chunk range inside a xorb. The current protocol is documented here:
The important part is that a file can require several term ranges and several xorbs to reconstruct a requested logical byte range.
For the same Xet file ID, I requested reconstruction metadata using an A-scoped and B-scoped Xet read token. Xet tokens are explicitly scoped to a repository and ref.
The A/B reconstruction responses were not the same.
Two representative examples were:
| Logical position |
Softology A |
mcmonkey B |
| 768 MiB |
9 terms / 2 xorbs / 2 fetch entries |
1 term / 1 xorb / 1 fetch entry |
| 4096 MiB |
7 terms / 2 xorbs / 2 fetch entries |
1 term / 1 xorb / 1 fetch entry |
I then checked whether this was just random CAS plan selection.
For four logical offsets I made 10 reconstruction requests per repo — 80 requests total — and deliberately refreshed the Xet read tokens halfway through.
The result was unusually clean:
- each A repo/offset returned the same semantic reconstruction 10/10 times
- each B repo/offset returned the same semantic reconstruction 10/10 times
- the layouts remained unchanged after refreshing the tokens
- A and B remained different at 4/4 offsets
So, at least during this test, the reconstruction difference behaved much more like stable repo/ref-associated state than per-request randomness.
This matters because the Xet documentation specifically warns about this shape of fragmentation. The file-reconstruction spec says that many small scattered ranges can mean more lookups, more range fetches and worse locality, while longer contiguous ranges reduce round trips and simplify reconstruction.
The deduplication documentation makes the design tradeoff even more explicit: aggressive deduplication can fragment a file across xorbs, so Xet intentionally tries to preserve longer continuous runs where practical.
In a 12-offset comparison, A had more terms than B at five locations. B was faster at all five of those locations. The term-count difference and the paired speed difference also had a fairly strong positive descriptive association in this small sample (Spearman rho was about 0.70).
I would treat that as support for fragmentation/locality being a contributor, not proof that it is the whole root cause. Twelve offsets from one network location are nowhere near enough to turn that association into a causal claim.
Why I do not think 'fragmentation' is the whole story
There are still other layers involved.
Hugging Face describes two meaningfully different download paths in Migrating the Hub from Git LFS to Xet.
A Xet-aware client obtains reconstruction information and fetches the required xorb ranges. Older/non-Xet-aware /resolve/ traffic takes the compatibility path through the Git LFS Bridge, which reconstructs the Xet-backed file and exposes it as a normal file URL. HF notes that the real Bridge path also involves additional components including a CDN, metadata storage and backing object storage.
I therefore also sampled the raw CAS-provided xorb ranges directly.
The raw-xorb A/B throughput difference was much less consistent than the /resolve/Bridge difference. In one integrated run the selected raw-xorb B/A median was only around 1.26Ă—, and in an earlier boundary test the median was essentially 1.0Ă—.
So I do not think the evidence supports:
“B is simply stored on a network endpoint that is always twice as fast.”
The difference appears to emerge from a more complicated combination of reconstruction/layout and the serving path above it.
Likewise, a simple cold-cache-only explanation became less convincing. Repeating already-read ranges did not consistently erase the A/B gap. CDN/cache state can absolutely still contribute — and HF has discussed cache warming in other Xet contexts — but it does not seem sufficient by itself to explain all of these observations.
So I would currently keep a broader bucket open:
reconstruction fragmentation/locality + Bridge/CDN/backend/path-specific state
with the relative contribution of each part still unknown.
Why this does not look like the documented Hub rate limit
The documented Hub rate limits are request-rate limits.
/resolve/ requests have their own resolver quota, and when that quota is exceeded HF documents a 429 Too Many Requests response plus RateLimit headers.
Across these probes I did not hit a resolver 429, and the resolver headers showed ample request quota remaining.
That makes the documented resolver rate-limit mechanism a poor fit for a symptom like:
one file path continues transferring much more slowly than another after the request has already been accepted.
There may of course be internal resource-management mechanisms that are not publicly documented. I cannot rule those in or out from outside HF. I would just distinguish them from the public resolver-rate-limit mechanism.
A low-cost additional control, if you want one
I would not suggest doing a lot more bandwidth testing unless someone from HF/Xet asks for a specific trace. The current reproducer already separates quite a few variables.
There is one relatively inexpensive experiment that might be informative, though.
Recent huggingface_hub versions support server-side repo-to-repo file copies using copy_files() / hf cp. Xet/LFS files can be copied by hash without downloading and re-uploading the multi-GB payload, provided the source and destination are in the same storage region.
So, as a diagnostic rather than a recommended fix, one possibility would be:
- leave the existing slow file untouched;
- server-side-copy the known-fast same-content file into a new diagnostic path in the Softology repo;
- compare the new path’s reconstruction and download behavior.
That creates an interesting branch:
- if the copied file keeps a B-like reconstruction and stays fast, the stored/reconstruction representation becomes more interesting;
- if it acquires an A-like reconstruction and becomes slow, destination repo/ref/path context becomes more interesting;
- if neither happens, that is useful too.
I would only do this under a new filename/path so the current reproducer is preserved.
I also could not find a documented user-facing command whose stated purpose is “compact/refresh the reconstruction of an existing Xet file”. So I would not recommend deleting and re-uploading the current object merely in the hope of fixing it unless HF/Xet staff specifically suggest that route.
What I would ask HF/Xet maintainers
At this point I think the highest-information question is fairly narrow:
For the same Xet file ID, is it expected that different repo/ref-scoped read tokens can return stable but substantially different term/xorb reconstruction layouts? If so, can that layout difference materially affect the legacy /resolve / Bridge download path, and is there a supported way to refresh or compact an existing reconstruction?
A compact reproducer can be reduced to:
- same Xet file ID;
- same sampled logical bytes;
- same visible CDN host/IP/POP from the test location;
- consistently different
/resolve throughput;
- stable repo/ref-specific reconstruction differences;
- representative examples such as
9 terms / 2 xorbs versus 1 term / 1 xorb.
That seems more actionable than simply reporting “2 MB/s versus 30 MB/s”, because it gives someone who can see the backend a fairly specific boundary to inspect.
So for your original question: yes, there appears to be a real path-dependent performance difference, but I would not currently call it proof that HF has bandwidth-throttled your repository. The most concrete clue I could find is the stable difference in Xet reconstruction layout, with the slower path showing more fragmentation in the ranges where the speed gap was strongest.
For normal users who just need the file, continuing to expose the faster working mirror is probably the least disruptive workaround. For the underlying issue, I think the useful next move is on the HF/Xet side rather than asking you to repeatedly download another 15 GB file.