An icon picker looks like the easiest screen in a product. A search box, a grid of thumbnails, a click. We build one into Thridy and into most of the demos we hand people, and last month we measured what our own first draft actually put on the wire. Twenty-four thumbnails and one page of results came to 1.4 MB.
None of that was necessary. The same 24 icons, from the same API, arrive in 309 KB when the picker asks for the right things, and they arrive no slower. Below are the four places the weight came from, all measured against the public Thridy API in one sitting. Every URL we queried is listed at the end so you can re-run any of it.
Every icon has three image URLs. Only one belongs in a grid.
A result from /api/icons carries three links to the same artwork. Icon Small Link points at a 200 by 200 WebP. Icon Display Link points at a 512 by 512 WebP. Icon Download Link points at a PNG, and that one you should skip: all 28 PNG links we requested returned 403. That is a problem on our side, not yours, but it is not something your picker should sit waiting on.
We measured the two live sizes twice, on two unrelated result sets. Across 20 icons from search=dashboard, the small file averaged 13,182 bytes and the display file 58,495, a median ratio of 4.36 times. Across 24 icons from search=finance, the same two averaged 12,510 and 55,687 bytes, a median ratio of 4.41 times. The samples agree, and the second one is the one that hurts: a 24-icon grid built on Display Link is 1,305 KB of image. Built on Small Link it is 293 KB.
A 200 pixel thumbnail is already generous for a grid cell that renders at 64 or 96 CSS pixels. Reach for Display Link when the user has clicked something and you are showing it large, and not one request before that.

The API sends 1,477 bytes per icon. A grid uses 182.
The JSON is the quieter problem, because a few hundred kilobytes of text never looks alarming in a network tab. We pulled 1,000 icons and measured the encoded size of each record: 1,477 bytes on average. A grid cell needs four fields, slug, Icon Name, Icon Small Link and embedSizes. Those four come to 182 bytes. Eighty-eight percent of what we send you, a picker never renders.
The weight is mostly prose written for search engines rather than for your UI. Per icon, the detail-page description averages 307 bytes, the meta keywords 140, the meta description 134, the keyword list 98, and the alt tag another 98. All of it earns its place on an icon's own page. None of it earns a place behind a thumbnail.
We checked whether you could ask for less. You cannot, yet. We passed fields=slug and the response still came back with all 19 fields on every icon. That is a gap in our API and we would rather name it than let you discover it. Until it is closed, the fix on your side is to keep the page small: limit=12 is a 16,756 byte response, limit=50 is 71,974, and limit=500 is 721,794.
Search corrects one word. It does not correct two.
This is the behaviour most likely to make your picker look broken, and it took us a while to see because the failure comes back as a perfectly valid 200.
Single-word queries get spell correction, and the response tells you so with a corrected flag. search=calendar returns 37 icons with corrected: false. search=calender and search=calandar both return the same 37 with corrected: true. search=shoping returns 101 with corrected: true, led by Shopping Bag, Shopping Bags and Shopping Cart. Correction is doing its job.
Add a second word and it stops. search=shopping cart returns 357 results led by Shopping Cart and Blue Shopping Cart, which is right. search=shoping cart returns the same 357 results led by Adidas Stan Smith, Airport Baggage Cart and Amber Oat Milk, with corrected: false.
The totals match because multi-word queries are treated as an OR. The result set is everything matching any token, and "cart" on its own matches 357 icons. What changes is the ranking. When one term matches nothing, no icon matches the full phrase, and the ordering falls back to the default listing, which is alphabetical. We confirmed that by searching zzz cart: identical first results to shoping cart. The default listing with no search at all does begin 100 Points, 12:30, 18 Plus.
Two things follow for your picker. Read the corrected flag and show it, because someone who typed "calender" deserves to know they are looking at calendars. And treat a top result that shares no word with the query as a miss. If the first hit for a two-word search contains neither word, you are looking at the alphabet, not at relevance.

Pagination never raises its voice
limit is honoured up to 5,000 and silently clamped above it. We asked for 20,000 and got 5,000, in a 7,149,889 byte response. That request finishes in about a third of a second warm, which is the trap: it is fast enough that you might ship it.
Response time is close to flat across page sizes once the connection is warm. Median of five runs was 0.22 seconds at limit=12, 0.22 at limit=50, and 0.30 at limit=500. Our server is not what makes a big page slow. The bytes on the wire, and the work the browser does parsing and decoding them, are.
Past the end, page=99999 returns HTTP 200 with an empty icons array, totalPages: 267, and your own page number echoed straight back. There is no error to catch. An infinite scroll that trusts the status code will keep asking forever, so stop on an empty array or when page reaches totalPages.
Filter on the server
The category parameter works and is worth using. category=Technology returns 689 icons and category=UI & Interface returns 239, out of 13,332 in the library. Fetching everything and filtering in the browser means moving 7 MB to look at 239 icons.
One thing to guard while you are there. Of the 1,000 icons we sampled, 999 carried the same embedSizes array of 32, 64, 96, 128, 256 and 512. One carried an empty array. If you build a size dropdown straight off that field without a fallback, roughly one icon in a thousand will hand your UI nothing to render.
What our picker asks for now
One page of 24 at a time. Category filtered on the server. Icon Small Link in the grid, Icon Display Link held back until something is selected, the PNG link ignored. Pagination that stops on an empty array. The corrected flag surfaced in the UI when it comes back true.
Same screen, same API, same perceived speed, 309 KB instead of 1.4 MB, and a search box that tells the truth about what it found. If you want to check any of this against your own numbers, the endpoints are public and the exact queries we ran are listed below.
Free 3D icons for this
Every icon on Thridy is free to download and use. A few that fit this piece:
- Bug Viewer 3D icon
- Bug Report 3D icon
- Markdown File 3D icon
- Blue Photon Engine 3D icon
- Pineapple Hand Grenade 3D icon
- 35mm Slide 3D icon
Browse all 2,600+ free 3D icons or explore them by category.
