Open Source Icon REST API
Search, fetch and render icons over HTTP.
Two endpoints: search 314,000+ open-source icons, then render any of them as SVG. Search takes the free license key from your Settings; the SVG itself needs nothing at all.
Our own VS Code extension, Figma plugin and MCP server search and insert icons through these two endpoints — they bundle no icon data at all — and they are open to you on the same terms. Access-Control-Allow-Origin: * on every response, so calls work from a browser, a null-origin plugin iframe or a server, and preflights allow Authorization.
Fair use. Cache responses on your side; bulk downloads should use the Iconify JSON packages instead. Icons keep their own open-source licenses — see the library pages for each one.
Search icons
GET/api/search?q={query}&lib={prefixes}&col={slugs}&page={1-20}&per_page={1-100}
The same search the website runs: typo-tolerant, ranked by library popularity, filtered by library and collection. Each hit carries palette, which is true for multicolor sets — their icons ignore the color parameter.
| Parameter | Accepts |
|---|---|
| q | The query, 2 to 120 characters — or * to list a whole library or collection, which is only accepted together with lib or col. |
| lib | Comma-separated library prefixes, up to 50: lib=lucide,tabler. Results then come from those libraries only. |
| col | Comma-separated collection slugs, up to 15: col=line-art-stroke. A library's prefix is in its page address (/libraries/lucide) and a collection's slug in its own (/collections/line-art-stroke). |
| page | 1 to 20, default 1. |
| per_page | 1 to 100, default 60. |
curl -H "Authorization: Bearer osi_…" \
"https://opensourceicon.com/api/search?q=arrow&per_page=2"// one library at a time; ?q=*&lib=lucide lists the whole library
const res = await fetch(
"https://opensourceicon.com/api/search?q=arrow&lib=lucide&per_page=20",
{ headers: { Authorization: `Bearer ${licenseKey}` } },
);
const { hits, found } = await res.json();{
"hits": [
{ "prefix": "glyphs", "name": "arrow", "palette": false },
{ "prefix": "uil", "name": "arrow", "palette": false }
],
"found": 9625,
"page": 1,
"per_page": 2
}- Content-Type
- application/json
- Authorization
- Authorization: Bearer osi_…
- Cache-Control
- private, max-age=3600
- Errors
- 401 without a key or with an unknown one. 400 {"error":"too_short"} for a query under two characters, and for a bare * with no lib or col; 400 {"error":"invalid_filters"} for a lib or col we cannot parse. If the search engine is briefly unavailable a name-index fallback answers in the same shape instead of an error, and found is then a floor rather than an exact total.
One icon as SVG
GET/api/icon/{prefix}/{name}?size={px}&color={css color}
The rendered SVG itself, served with a content security policy that forbids scripts and external resources.
| Parameter | Accepts |
|---|---|
| size | Sets width and height in pixels. Capped at 2048. |
| color | Applied to monotone sets only. A hex value, a CSS colour keyword, or rgb(), rgba(), hsl() or hsla() notation. Anything else returns 400. |
curl "https://opensourceicon.com/api/icon/lucide/house?size=48&color=%23ff0000"const res = await fetch("https://opensourceicon.com/api/icon/lucide/house?size=48");
const svg = await res.text();<svg xmlns="http://www.w3.org/2000/svg" width="48" height="48"
viewBox="0 0 24 24"><path fill="none" stroke="#ff0000" .../></svg>- Content-Type
- image/svg+xml
- Authorization
- None — this one stays open.
- Cache-Control
- public, max-age=86400, s-maxage=604800, stale-while-revalidate=604800
- Errors
- 404 for an unknown icon, 400 for a colour outside the allowed forms.