If you wired a Thridy icon into your product straight from our API, there is a good chance the URL you used is now serving a 403 and your users are looking at an alt-text box. We found this the way you would rather we found it, by checking our own data instead of waiting for the support email.
On 27 August 2026 we pulled 200 icons at random from our public API, spread across 30 evenly spaced pages of the 13,332 in the library, and fetched all three image URLs the API returns for each one. Six hundred requests. Here is what came back, and what you should be putting in your markup instead.
Three URLs come back for every icon
Ask our API for an icon and the object you get has three separate links in it. Icon Small Link points at cdn.thridy.com/sicons/, Icon Display Link points at cdn.thridy.com/dicons/, and Icon Download Link points at cdn.thridy.com/icons/. Nothing in the field names tells you the pixel dimensions, the file weight, or which one is meant for an img tag. If you are picking by name alone, "Download" sounds like the real file and the other two sound like previews. That instinct is exactly backwards.
Every download link we tested returned 403
All 200 of the Icon Download Link URLs failed. Not 199. All 200, with an identical 152-byte JSON body: {"error":"moved","message":"Full-resolution downloads are no longer served from this address. Open the icon on thridy.com and use the download button."}, sent with Cache-Control public, max-age=300.
So it is not a wobble and it is not rate limiting. The whole /icons/ prefix has been retired in favour of the download button on the site, and the API is still handing out the old address for every icon in the library. We tested with and without a browser user agent and with a thridy.com referer, and the response is the same each time. Swapping the extension does not help either: /icons/poke-bowl.webp returns the same 403.
The failure mode is quiet, which is the annoying part. An img tag with a dead src does not throw anything you will see in a dashboard. It renders alt text, or a broken-image glyph, or nothing at all if the element is sized by CSS. If you cached the API response at build time months ago, the string in your bundle is still a perfectly well-formed URL. Search your codebase for cdn.thridy.com/icons/ and you will know in a few seconds whether this is your problem.
The other two files, weighed
Both of the remaining links answered 200 on all 200 icons. They are not interchangeable.
The small file is the consistent one. Every one of the 200 was exactly 200 by 200 pixels, WebP, RGBA, with a median of 7,103 bytes. The lightest was 2,788 bytes and the heaviest 15,010, and 90 percent came in under 10,088. If you want one number to plan a page around, the small file is about 7 KB and it does not surprise you.
The display file is not consistent. The median is 32,084 bytes, 4.53 times the small file for the same icon, but the mean is 43,852, and that gap is the story. Of the 200 display files, 176 are 512 by 512 and 24 are 1024 by 1024. The 512s have a median of 30,893 bytes. The 1024s have a median of 147,609. The heaviest single file in the sample was 235,110 bytes, and the worst display-to-small ratio was 21 to 1. Nothing in the URL, the field name or the icon metadata tells you which kind you are about to download. Roughly one icon in eight is five times heavier than you budgeted for.
embedSizes is a list of sizes, not a list of files
Every icon in our sample but one carries the same embedSizes array: 32, 64, 96, 128, 256, 512. One icon, Mutual Fund, has an empty array. Reading that field, it is reasonable to assume there are six rendered files sitting behind six addresses.
There are not. We tried the obvious ways to ask for one. /dicons/poke-bowl.webp?w=64 and ?width=64 both return the full file, byte for byte identical to the plain URL at 45,960 bytes. A path variant like /dicons/64/poke-bowl.webp is a 404. embedSizes describes the sizes our embed snippet will set on the element, not files we host. Set the snippet to 32 and you still download the whole 512 or 1024 pixel image and let the browser throw the rest away.
The CDN will resize on request
There is a resize path that does work, and it is worth knowing about because it beats both of the files above. Our CDN sits behind Cloudflare, and Cloudflare's image transformation URLs take the form https://zone/cdn-cgi/image/options/source-image, per their documentation. On cdn.thridy.com that resolves to something like cdn.thridy.com/cdn-cgi/image/width=64,format=auto/dicons/poke-bowl.webp.
We ran that against 40 icons at four widths, sending an Accept header that allows AVIF. Every request returned 200, at exactly the width we asked for, in AVIF, with the alpha channel intact. Median weights: 1,009 bytes at 32 pixels, 1,618 at 64, 3,056 at 128 and 6,570 at 256.
Read the last two of those against the small file. A 256 pixel AVIF has 64 percent more pixels than the 200 pixel WebP we ship and still lands lighter, 6,570 bytes against 7,103. The single sharpest example in our sample was the Location icon, whose display file is one of the 1024s at 132,664 bytes. At width=64 it comes down as 1,928 bytes, 69 times smaller, into a box where nobody could tell the difference.
Two caveats we measured rather than assumed. format=auto depends on the Accept header the client sends, so a request that does not advertise AVIF gets something else: ours came back as a 64 pixel PNG at 2,579 bytes, still with working transparency, still tiny. And this is Cloudflare's generic transform path, not a Thridy API with our name on it, so treat it as infrastructure that is available today rather than a contract.

Is the 200 pixel file good enough?
Before recommending the small file as a default, we checked whether it is a clean downscale or an over-compressed one. For 40 icons we resampled the display file down to 200 pixels with Lanczos, composited both versions over white and over black, and compared them. The median PSNR was 40.23 dB on white and 40.44 dB on black, with the worst icon at 35.35 dB. That is close, comfortably inside the range where the difference does not survive being drawn at 48 pixels in a sidebar. The small file is honest. It is just aimed at a bigger box than most interfaces use.
What we would put in the markup
For a fixed icon box, one file is enough. A 48 pixel box on a 2x screen needs 96 real pixels, so ask for 96 or 128 and stop there. For a nav icon we would write <img src="https://cdn.thridy.com/cdn-cgi/image/width=128,format=auto/dicons/poke-bowl.webp" width="64" height="64" alt="" loading="lazy" decoding="async"> and set the alt text from what the icon means in your interface, not from ours.
If you want to cover 1x and 2x explicitly, use x descriptors in srcset and let src be the 1x candidate. MDN is precise about the difference: with x descriptors the browser also treats the src URL as a candidate with a default descriptor of 1x, while with w descriptors src is ignored entirely and the sizes attribute takes over. For icons, x descriptors are the simpler tool, because the box is a known number of pixels and nothing about it depends on viewport width.
If you would rather not depend on the transform path, use Icon Small Link. Do not use Icon Display Link for anything under about 128 pixels, and do not use Icon Download Link at all.
What a real page costs
Take a dashboard with 24 icons on it. At our measured medians, the display files come to 752.0 KB, the small files to 166.5 KB, and 64 pixel AVIFs to 37.9 KB. That last pair is a factor of 19.8 on the wire, and it is larger once the browser starts decoding, because decoded bitmaps do not care how well they compressed. A 512 by 512 RGBA bitmap occupies 1.0 MB in memory, so those 24 display files land as 24.0 MB of decoded pixels, against 3.7 MB for the small files and 384 KB at 64 pixels.
Both WebP files come back with Cache-Control max-age=14400, which is four hours, so a returning visitor tomorrow pays the full download again. We wrote about that separately, and it makes the size of the first download matter more than it otherwise would.

What we are fixing
The API should not return a URL that answers 403 for every icon in the library, and it does today. That field needs to either point at something that resolves or stop being sent. Until it changes, the rule is short: sicons and dicons work, icons does not, and the size you actually want is one query string away.
Free 3D icons for this
Every icon on Thridy is free to download and use. A few that fit this piece:
- Blog Post 3D icon
- WebP File 3D icon
- Red Rocket 3D icon
- Percentage 3D icon
- Checkbox 3D icon
- Pineapple Hand Grenade 3D icon
Browse all 2,600+ free 3D icons or explore them by category.
