# macOSicons API

Search 25,000+ macOS app icons, generate and convert icons with the Editor API, list your generated assets, and check account usage.

- Base URL: `https://api.macosicons.com`
- Authentication: send your API key in the `x-api-key` header. Get one at https://macosicons.com/developers
- OpenAPI 3.0 spec (JSON): https://macosicons.com/api/openapi.json
- Human-readable docs: https://macosicons.com/developers

## Endpoints

- `POST /api/v1/search` — Search icons
- `GET /api/v1/search/getCategories` — Get all categories
- `POST /api/v1/editor/mask` — Apply icon mask
- `POST /api/v1/editor/generate` — Generate icons with AI
- `POST /api/v1/editor/convert` — Convert an image to a macOS, Windows or Linux icon
- `POST /api/v1/editor/icon` — Convert an image to an Icon Composer .icon bundle
- `POST /api/v1/editor/iconset` — Convert an image to an Xcode .appiconset
- `POST /api/v1/editor/extract` — Extract PNGs from a .icns file
- `POST /api/v1/editor/removebg` — Remove an image's background
- `POST /api/v1/editor/favicon` — Generate a complete web favicon pack
- `POST /api/v1/editor/pack` — Generate a complete icon release kit
- `POST /api/v1/editor/palette` — Extract an icon's colors
- `POST /api/v1/editor/preview` — Render an icon in context
- `GET /api/v1/editor/usage` — Get editor usage & limits
- `GET /api/v1/assets` — List your generated & uploaded assets
- `GET /api/v1/usage` — Get all usage & limits

## POST /api/v1/search

**Search icons**

Search for icons using various filters and search options.

### Parameters

| Name | In | Type | Description |
| --- | --- | --- | --- |
| `x-api-key` | header | string | API key for authentication (not required for macosicons.com domain) |
| `query` | query | string | Search query as URL parameter |

### Request body (application/json)

| Field | Type | Description |
| --- | --- | --- |
| `query` | string | Search query string. Example: `"safari"` |
| `searchOptions` | object |  |
| `searchOptions.hitsPerPage` | number | Number of results per page (default: 20). Example: `20` |
| `searchOptions.page` | number | Page number (default: 1). Example: `1` |
| `searchOptions.offset` | number | Offset for pagination (default: 0). Example: `0` |
| `searchOptions.filters` | string[] | Array of filter strings. Example: `["category = \"browsers\""]` |
| `searchOptions.sort` | string[] | Array of sort criteria. Example: `["downloads:desc"]` |

### Responses

#### 200 — Successful search results

| Field | Type | Description |
| --- | --- | --- |
| `hits` | object[] |  |
| `hits[].appName` | string |  |
| `hits[].lowResPngUrl` | string |  |
| `hits[].icnsUrl` | string |  |
| `hits[].iOSUrl` | string |  |
| `hits[].category` | string |  |
| `hits[].downloads` | number |  |
| `hits[].credit` | string |  |
| `hits[].timeStamp` | string |  |
| `hits[].uploadedBy` | string |  |
| `totalHits` | number |  |
| `totalPages` | number |  |
| `hitsPerPage` | number |  |
| `page` | number |  |

#### 400 — Bad request

| Field | Type | Description |
| --- | --- | --- |
| `statusCode` | number | Example: `400` |
| `message` | string | Example: `"Valid search query is required"` |

#### 401 — Unauthorized

| Field | Type | Description |
| --- | --- | --- |
| `statusCode` | number | Example: `401` |
| `message` | string | Example: `"Invalid API key"` |

#### 429 — Too Many Requests - Rate limit exceeded or API call limit exceeded

| Field | Type | Description |
| --- | --- | --- |
| `statusCode` | number | Example: `429` |
| `message` | string | Example: `"Rate limit exceeded. Maximum 2 requests per second allowed."` |
| `statusMessage` | string | Example: `"API call limit exceeded"` |

#### 500 — Internal server error

| Field | Type | Description |
| --- | --- | --- |
| `statusCode` | number | Example: `500` |
| `message` | string | Example: `"An error occurred while searching."` |

## GET /api/v1/search/getCategories

**Get all categories**

Returns a list of all categories with their objectId and CategoryName.

### Responses

#### 200 — Categories retrieved successfully

Type: `array`

#### 429 — Rate limit exceeded

| Field | Type | Description |
| --- | --- | --- |
| `statusCode` | number | Example: `429` |
| `message` | string | Example: `"Rate limit exceeded. Maximum 10 requests per second allowed."` |

#### 500 — Internal server error

| Field | Type | Description |
| --- | --- | --- |
| `statusCode` | number | Example: `500` |
| `statusMessage` | string | Example: `"Error fetching categories"` |

## POST /api/v1/editor/mask

**Apply icon mask**

Make any image a compliant macOS app or folder icon. By default it trims transparent edges, fits the artwork inside the squircle content box, clips it to the mask, and adds the macOS drop shadow. Set fitToMask=false to preserve the submitted pixel dimensions and simply clip the image; scale adjusts either result. Position from center with offsetX / offsetY and choose offsetUnit=percent or px (legacy fraction is also supported). App icons use a white background by default; pass background=transparent, a hex color, or derive one from the artwork. Accepts raster formats, SVG, and .icns through multipart, raw bytes, or JSON. Max upload: 25 MB.

> **Behavior change — app icons now default to a white background** — Icons made with /editor/mask now sit on a solid white background (#ffffff) inside the squircle. It used to be left transparent. If you were relying on the previous transparent fill, pass background=transparent to keep it. Nothing else changed: the rounded corners and the margin outside the squircle stay transparent, and shape=folder never used background.

### Parameters

| Name | In | Type | Description |
| --- | --- | --- | --- |
| `x-api-key` | header (required) | string | API key for authentication. Metered against your monthly icon-mask limit. Calls from an authorized macosicons.com origin or a valid search token are unmetered first-party. |

### Request body (application/json)

| Field | Type | Description |
| --- | --- | --- |
| `image` | string (required) | Base64 string or data URL of the image. For multipart/form-data send a file field named "image" instead; for a raw image body send the bytes directly and pass options as query params. Example: `"data:image/png;base64,iVBORw0KGgo…"` |
| `shape` | string | app = squircle app icon; folder = composite the artwork as an emblem on the macOS folder icon. Default: app. Example: `"app"` |
| `shadow` | boolean | Paint the standard macOS drop shadow behind the app icon (shape=app only; the folder graphic has its own shadow). Default: true. Example: `true` |
| `trimTransparent` | boolean | Find the first non-transparent pixels on all edges and refit the content into the standard box. false = apply the mask over the input unchanged. Default: true. Example: `true` |
| `fitToMask` | boolean | Fit the artwork inside the mask target before applying scale. false preserves its submitted pixel dimensions and simply applies the mask over it. Default: true. Example: `true` |
| `scale` | number | Scale multiplier applied after fitting, or to the submitted dimensions when fitToMask=false. Scaling uses the artwork center as its origin, so the image remains centered when offsets are zero. Range: 0.01–10. Default: 1. Example: `1` |
| `offsetX` | number | Horizontal shift from center, interpreted using offsetUnit. Positive moves right. Default: 0. Example: `0` |
| `offsetY` | number | Vertical shift from center, interpreted using offsetUnit. Positive moves down. Default: 0. Example: `0` |
| `offsetUnit` | string | Unit for offsetX/offsetY. percent uses 10 = 10%; px uses output pixels; fraction preserves the legacy 0.1 = 10% contract. Default: fraction. One of: `fraction`, `percent`, `px`. Example: `"percent"` |
| `size` | integer | Output size in px (square), clamped 16–2048. Default: 1024. Example: `1024` |
| `format` | string | Output format: png or webp (PNG preserves alpha). Default: png. Example: `"png"` |
| `background` | string | shape=app only: solid fill behind the artwork, inside the squircle: #rgb, #rrggbb, #rrggbbaa, transparent, or an object like { "r":0,"g":128,"b":255,"alpha":1 }. Pass transparent for no fill. Also the fallback when useAverageColor / useProminentColor cannot resolve a color. Default: #ffffff (opaque white). Example: `"#ffffff"` |
| `useAverageColor` | boolean | shape=app only: fill the background with the artwork's alpha-weighted average color instead of background. Default: false. Example: `false` |
| `useProminentColor` | boolean | shape=app only: fill the background with the artwork's most prominent (dominant) color. Wins over useAverageColor when both are set; falls back to the average color when nothing reaches colorProminenceThreshold. Default: false. Example: `false` |
| `colorAlphaThreshold` | integer | useAverageColor / useProminentColor: a pixel is sampled for color only when its alpha exceeds this (0–255). Raise it (≈64–128) to ignore faint anti-aliased edges and sample only the solid body of the artwork. Default: 16. Example: `16` |
| `colorProminenceThreshold` | number | useProminentColor only: the dominant color must cover at least this share (0–1) of the sampled pixels to be used, otherwise the average color is used instead. Raise it to demand a more dominant color. Default: 0.1. Example: `0.1` |
| `output` | string | binary returns the raw image bytes; json returns a base64 data URL plus metadata. Default: binary. Example: `"binary"` |
| `alphaThreshold` | integer | Alpha (0–254) at/below which a pixel counts as transparent when trimming. Default: 0. Example: `0` |
| `emblemFraction` | number | shape=folder only: emblem size as a fraction of the canvas (0.1–1), before scale. Default: 0.5. Example: `0.5` |
| `emboss` | boolean | shape=folder only: render the emblem as a macOS-style engraving — recolored to the folder blue and shaded as a recess carved into the folder face — instead of drawing its own colors. Default: false. Example: `false` |
| `monocolor` | boolean | shape=folder only: replace the input artwork colors with folderColor. monochrome is accepted as an alias. Default: false. Example: `false` |
| `insideEmboss` | boolean | shape=folder only: engrave the input artwork into the folder face. Alias of emboss and takes precedence over monocolor. Default: false. Example: `false` |
| `folderColor` | string | shape=folder only: tint the shaded folder while preserving highlights and shadows. Also colors monochrome or embossed artwork. Default: #61C5F1. Example: `"#61C5F1"` |

### Responses

#### 200 — Masked image. Binary image/png (or image/webp) by default, with headers X-Icon-Shape: app\|folder and (shape=app) X-Icon-Trimmed: true\|false. When output=json, a JSON object with the base64 data URL and metadata.

| Field | Type | Description |
| --- | --- | --- |
| `ok` | boolean | Example: `true` |
| `shape` | string | Example: `"app"` |
| `width` | number | Example: `1024` |
| `height` | number | Example: `1024` |
| `format` | string | Example: `"png"` |
| `trimmed` | boolean | Example: `true` |
| `shadow` | boolean | Whether the macOS drop shadow was painted (shape=app only). Example: `true` |
| `contentBox` | object | Bounding box of the detected artwork (present when trimmed) |
| `contentBox.left` | number | Example: `90` |
| `contentBox.top` | number | Example: `120` |
| `contentBox.width` | number | Example: `180` |
| `contentBox.height` | number | Example: `180` |
| `backgroundColor` | object | The fill actually painted behind the artwork (shape=app only) |
| `backgroundColor.r` | number | Example: `255` |
| `backgroundColor.g` | number | Example: `255` |
| `backgroundColor.b` | number | Example: `255` |
| `backgroundColor.alpha` | number | Example: `1` |
| `backgroundColorSource` | string | Where backgroundColor came from: "solid" = a fixed color (including the white default), "average" / "prominent" = derived from the artwork. Asking for prominent and getting back "average" tells you nothing in the artwork was dominant enough (shape=app only). Example: `"solid"` |
| `image` | string | Base64 data URL of the masked image (output=json only). Example: `"data:image/png;base64,iVBORw0KGgo…"` |

#### 400 — No image provided or invalid options

| Field | Type | Description |
| --- | --- | --- |
| `statusCode` | number | Example: `400` |
| `message` | string | Example: `"No image provided"` |

#### 401 — Invalid API key

| Field | Type | Description |
| --- | --- | --- |
| `statusCode` | number | Example: `401` |
| `message` | string | Example: `"Invalid API key"` |

#### 403 — Missing API key

| Field | Type | Description |
| --- | --- | --- |
| `statusCode` | number | Example: `403` |
| `message` | string | Example: `"API key is required"` |

#### 413 — Image over 25 MB

| Field | Type | Description |
| --- | --- | --- |
| `statusCode` | number | Example: `413` |
| `message` | string | Example: `"Image exceeds the 25 MB limit"` |

#### 422 — Image could not be decoded

| Field | Type | Description |
| --- | --- | --- |
| `statusCode` | number | Example: `422` |
| `message` | string | Example: `"Image could not be decoded"` |

#### 429 — Rate limit or monthly mask limit exceeded

| Field | Type | Description |
| --- | --- | --- |
| `statusCode` | number | Example: `429` |
| `message` | string | Example: `"Monthly icon-mask limit exceeded"` |

### How the background is chosen

Only one fill is ever painted. The auto modes override background whenever they resolve a color, and fall back to it when they cannot:

- useProminentColor=true → the artwork's dominant color. It wins over useAverageColor when both are set.
- Otherwise useAverageColor=true → the artwork's alpha-weighted average color.
- Otherwise → the solid background color (opaque white unless you pass one).

## POST /api/v1/editor/generate

**Generate icons with AI**

Generate up to 8 icons from a text prompt using an AI image model, then make each one standard-compliant: shape "app" applies the app-icon mask; shape "folder" composites the artwork onto a real macOS folder. masked:false returns the raw generated art. Generated art defaults to a modern macOS app-icon style (soft 3D sculpted forms, tactile materials, smooth gradients, crisp silhouette) unless the prompt asks for a different style. Metered against your monthly AI-generation allowance, counted in images (a count:4 request uses 4, or 8 with matting:true). Every generated image is uploaded and stored as one of your assets — list them any time with GET /api/v1/assets.

> **Now returns hosted URLs by default (with assetId)** — Each generated image is uploaded to storage and returned as a hosted imageUrl plus an assetId (its UserAsset id) — no more inline bytes by default, so responses stay small. Need the raw pixels in the same response? Set includeData:true and each image additionally carries data (base64) and mimeType. The stored asset also records the credits the generation cost and, when you pass documentClientId / projectId, which editor document/project it belongs to — all visible under GET /api/v1/assets.

### Parameters

| Name | In | Type | Description |
| --- | --- | --- | --- |
| `x-api-key` | header (required) | string | API key for authentication. Metered against your monthly AI-generation allowance, counted in images (matted icons count double). |

### Request body (application/json)

| Field | Type | Description |
| --- | --- | --- |
| `prompt` | string (required) | Describe the icon subject/theme. Max 2000 chars. Example: `"a friendly robot mascot in glossy ceramic"` |
| `count` | integer | How many icons to generate (1–8). Default: 1. Example: `4` |
| `shape` | string | app = squircle app icon; folder = macOS folder icon. Default: app. Example: `"app"` |
| `masked` | boolean | Apply the mask / folder composition. false returns the raw generated art. Default: true. Example: `true` |
| `transparent` | boolean | Override the transparent-background hint sent to the model. Default: app→false, folder→true, matting→true. Example: `false` |
| `matting` | boolean | Produce a true transparent-background PNG via two-pass difference matting: the art is generated on a pure white background, the exact render is re-edited onto pure black, and the per-pixel alpha channel is mathematically recovered from the aligned pair. Unlike transparent (a prompt hint the model often fakes), matting produces real, clean alpha and preserves partial transparency — glass, soft shadows, and anti-aliased edges survive. Each matted icon is billed as 2 images against your monthly allowance (a count:4 request with matting:true uses 8). Default: false. Example: `true` |
| `crop` | boolean | Crop each result to the closest non-transparent pixel on all sides. Any pixel that is not 100% transparent counts as content, so partial alpha (soft shadows, anti-aliased edges) survives. Most useful with transparent/matted art; the output dimensions become the content bounding box instead of size×size. Default: false. Example: `false` |
| `size` | integer | Output size in px (16–2048). Default: 1024. Example: `1024` |
| `scale` | number | Scale the art within the mask/folder (center origin). Default: 1. Example: `1` |
| `offsetX` | number | Horizontal shift as a fraction of size (center origin). Default: 0. Example: `0` |
| `offsetY` | number | Vertical shift as a fraction of size (center origin). Default: 0. Example: `0` |
| `format` | string | Output format: png or webp. Default: png. Example: `"png"` |
| `includeData` | boolean | Also return the raw image bytes inline. When true, each image additionally carries data (base64) and mimeType alongside the default hosted imageUrl. Default: false (URL only). Images are stored as assets regardless. Example: `false` |
| `documentClientId` | string | Optional editor document id to link the generated assets to. Surfaces under Generated Assets so you can see which document an image belongs to. Example: `"doc_9f2a…"` |
| `projectId` | string | Optional editor project id to link the generated assets to. Example: `"proj_4b1c…"` |
| `folderId` | string | Optional editor folder id to link the generated assets to. Example: `"fold_7d3e…"` |

### Responses

#### 200 — Generated icons. Each result is a hosted URL plus its assetId (the stored UserAsset). generated may be less than requested on a partial failure (you still get the successful ones).

| Field | Type | Description |
| --- | --- | --- |
| `ok` | boolean | Example: `true` |
| `shape` | string | Example: `"app"` |
| `masked` | boolean | Example: `true` |
| `matting` | boolean | Example: `false` |
| `crop` | boolean | Example: `false` |
| `requested` | number | Example: `4` |
| `generated` | number | Example: `4` |
| `images` | object[] |  |
| `images[].imageUrl` | string | Permanent hosted URL of the stored image (default return). Example: `"https://…/ai-generated/…/uuid.png"` |
| `images[].assetId` | string | UserAsset id — reference or fetch it later via GET /api/v1/assets. Example: `"aB9xY2…"` |
| `images[].width` | number | Example: `1024` |
| `images[].height` | number | Example: `1024` |
| `images[].credits` | number | Flat AI credits this image cost. Example: `25` |
| `images[].data` | string | Base64 image bytes. Present only when includeData:true. Example: `"iVBORw0KGgo…"` |
| `images[].mimeType` | string | MIME type of data. Present only when includeData:true. Example: `"image/png"` |

#### 400 — Invalid prompt or parameters

| Field | Type | Description |
| --- | --- | --- |
| `statusCode` | number | Example: `400` |
| `message` | string | Example: `"A valid prompt is required"` |

#### 401 — Invalid API key

| Field | Type | Description |
| --- | --- | --- |
| `statusCode` | number | Example: `401` |
| `message` | string | Example: `"Invalid API key"` |

#### 429 — Rate limit or monthly generation allowance exceeded

| Field | Type | Description |
| --- | --- | --- |
| `statusCode` | number | Example: `429` |
| `message` | string | Example: `"Monthly generation allowance exceeded: request needs 4, 2 remaining"` |

#### 502 — The image model failed

| Field | Type | Description |
| --- | --- | --- |
| `statusCode` | number | Example: `502` |
| `message` | string | Example: `"The image model failed to generate"` |

#### 503 — Generation not configured

| Field | Type | Description |
| --- | --- | --- |
| `statusCode` | number | Example: `503` |
| `message` | string | Example: `"Icon generation is not configured"` |

### Where your generated images live

Generation and storage are one step — you never have to upload the result yourself:

- Every image in the response is already persisted on your account as a UserAsset (origin "api" for key-authenticated calls).
- imageUrl is the permanent hosted URL; assetId is the stable id you can reference later.
- Pass documentClientId / projectId / folderId to tag the assets with the editor document/project they belong to.
- includeData:true adds the base64 data + mimeType alongside the URL when you need the bytes inline.

## POST /api/v1/editor/convert

**Convert an image to a macOS, Windows or Linux icon**

One endpoint for every OS-native icon format — pick the output with format. "icns" returns a ready-to-use macOS .icns with the full standard size set (16–1024px), no Mac or iconutil needed. "ico" returns a multi-resolution Windows .ico, which also works as a classic web favicon. "windows" returns a zip with that .ico plus the complete MSIX/UWP Images/ asset tree (Square44x44Logo, Square71x71Logo, Square150x150Logo, Square310x310Logo, Wide310x150Logo, StoreLogo and SplashScreen at scale-100/125/150/200/400, plus the taskbar target sizes) and a Package.appxmanifest snippet. "linux" returns a zip with a freedesktop hicolor icon theme (16–512px), a matching .desktop entry and an install.sh that copies both into place and refreshes the icon caches. Set mask=true to first make the artwork app-icon compliant exactly like /editor/mask (trim → squircle refit → mask → drop shadow) — that only applies to format=icns, since Windows and Linux icons follow their own conventions. Non-square input is centered on a square transparent canvas. Accepts PNG, JPEG, WebP and .icns input (largest embedded image is used); 16-bit PNGs keep their bit depth for the .icns. Send the image as multipart/form-data (file field "image"), a raw image body (options as query params), or JSON (base64 / data URL). Returns the binary artifact by default, or JSON (base64 + metadata) when output=json. Counts one call against your monthly icon-conversion budget (shared across /editor/convert, /icon, /iconset and /extract). Max upload: 25 MB.

### Parameters

| Name | In | Type | Description |
| --- | --- | --- | --- |
| `x-api-key` | header (required) | string | API key for authentication. Metered against your monthly icon-conversion limit. Calls from an authorized macosicons.com origin or a valid search token are unmetered first-party. |

### Request body (application/json)

| Field | Type | Description |
| --- | --- | --- |
| `image` | string (required) | Base64 string or data URL of the image. For multipart/form-data send a file field named "image" instead; for a raw image body send the bytes directly and pass options as query params. Example: `"data:image/png;base64,iVBORw0KGgo…"` |
| `format` | string | Output format. icns = macOS icon file; ico = Windows icon file; windows = .ico + MSIX/UWP assets (zip); linux = hicolor theme + .desktop entry (zip). Default: icns. One of: `icns`, `ico`, `windows`, `linux`. Example: `"icns"` |
| `name` | string | Base filename for the download, and the app name written into the Linux .desktop entry. Default: "icon" for icns/ico, "AppIcon" for the zipped sets. Example: `"MyApp"` |
| `mask` | boolean | format=icns only. Make the artwork app-icon compliant first (squircle + drop shadow), like /editor/mask. Accepts the same tuning options (shadow, trimTransparent, scale, offsetX, offsetY, background). Default: false. Example: `true` |
| `exec` | string | format=linux only. The Exec= command in the generated .desktop entry. Default: the slugified name. Example: `"/opt/myapp/bin/myapp %U"` |
| `categories` | string | format=linux only. The Categories= value in the .desktop entry. Default: "Utility;". Example: `"Graphics;Utility;"` |
| `comment` | string | format=linux only. The Comment= line in the .desktop entry. Example: `"Draws widgets"` |
| `output` | string | binary returns the raw artifact bytes; json returns a base64 data URL plus metadata. Default: binary. Example: `"binary"` |

### Responses

#### 200 — The converted icon. Binary by default (image/icns, image/x-icon or application/zip, Content-Disposition: attachment). When output=json, a JSON object with the base64 data URL and metadata.

| Field | Type | Description |
| --- | --- | --- |
| `ok` | boolean | Example: `true` |
| `format` | string | Example: `"icns"` |
| `filename` | string | Example: `"MyApp.icns"` |
| `bytes` | number | Example: `94157` |
| `masked` | boolean | Example: `true` |
| `sizes` | object[] | format=icns only: the image entries embedded in the container |
| `sizes[].type` | string | Example: `"ic10"` |
| `sizes[].width` | number | Example: `1024` |
| `sizes[].height` | number | Example: `1024` |
| `file` | string | Base64 data URL of the converted artifact (output=json only). Example: `"data:image/icns;base64,aWNucw…"` |

#### 400 — No image provided, or an unknown format / invalid options

| Field | Type | Description |
| --- | --- | --- |
| `statusCode` | number | Example: `400` |
| `message` | string | Example: `"Unknown format \"webp\". Use one of: icns, ico, windows, linux."` |

#### 401 — Invalid API key

| Field | Type | Description |
| --- | --- | --- |
| `statusCode` | number | Example: `401` |
| `message` | string | Example: `"Invalid API key"` |

#### 403 — Missing API key

| Field | Type | Description |
| --- | --- | --- |
| `statusCode` | number | Example: `403` |
| `message` | string | Example: `"API key is required"` |

#### 413 — Image over 25 MB

| Field | Type | Description |
| --- | --- | --- |
| `statusCode` | number | Example: `413` |
| `message` | string | Example: `"Image exceeds the 25 MB limit"` |

#### 422 — Image could not be decoded or converted

| Field | Type | Description |
| --- | --- | --- |
| `statusCode` | number | Example: `422` |
| `message` | string | Example: `"The image could not be decoded or processed"` |

#### 429 — Rate limit or monthly icon-conversion limit exceeded

| Field | Type | Description |
| --- | --- | --- |
| `statusCode` | number | Example: `429` |
| `message` | string | Example: `"Monthly icon-conversion limit exceeded"` |

## POST /api/v1/editor/icon

**Convert an image to an Icon Composer .icon bundle**

Turn any image into an Apple Icon Composer .icon bundle, ready for Xcode 26+: drag the unzipped <name>.icon into the project navigator and set the App Icon Set Name build setting to <name> — or open it in Icon Composer to refine layers, Liquid Glass and appearances. Because a .icon "file" is really a directory (icon.json + an Assets folder), the response is the bundle zipped. The artwork becomes a 1024×1024 layer; supply full-bleed square artwork without a pre-applied squircle or shadow (the system applies the icon shape itself). Use fill (hex) to set the background color behind the artwork. Send the image as multipart/form-data (file field "image"), a raw image body, or JSON (base64 / data URL); PNG, JPEG, WebP and .icns are accepted. Returns the zip by default, or JSON (base64) when output=json. Counts one call against your monthly icon-conversion budget. Max upload: 25 MB.

### Parameters

| Name | In | Type | Description |
| --- | --- | --- | --- |
| `x-api-key` | header (required) | string | API key for authentication. Metered against your monthly icon-conversion limit. Calls from an authorized macosicons.com origin or a valid search token are unmetered first-party. |

### Request body (application/json)

| Field | Type | Description |
| --- | --- | --- |
| `image` | string (required) | Base64 string or data URL of the image. For multipart/form-data send a file field named "image" instead; for a raw image body send the bytes directly and pass options as query params. Example: `"data:image/png;base64,iVBORw0KGgo…"` |
| `name` | string | Bundle name — the zip contains <name>.icon/, and the Xcode App Icon Set Name build setting must match it. Default: AppIcon. Example: `"AppIcon"` |
| `fill` | string | Background fill behind the artwork layer: #rgb, #rrggbb or #rrggbbaa. Default: opaque black. Example: `"#1e90ff"` |
| `output` | string | binary returns the zip bytes; json returns a base64 data URL plus metadata. Default: binary. Example: `"binary"` |

### Responses

#### 200 — The zipped .icon bundle (application/zip, Content-Disposition: attachment) by default. When output=json, a JSON object with the base64 data URL and metadata.

| Field | Type | Description |
| --- | --- | --- |
| `ok` | boolean | Example: `true` |
| `name` | string | Example: `"AppIcon"` |
| `filename` | string | Example: `"AppIcon.icon.zip"` |
| `bytes` | number | Example: `401932` |
| `file` | string | Base64 data URL of the zip (output=json only). Example: `"data:application/zip;base64,UEsDBAo…"` |

#### 400 — No image provided or invalid options

| Field | Type | Description |
| --- | --- | --- |
| `statusCode` | number | Example: `400` |
| `message` | string | Example: `"No image provided"` |

#### 401 — Invalid API key

| Field | Type | Description |
| --- | --- | --- |
| `statusCode` | number | Example: `401` |
| `message` | string | Example: `"Invalid API key"` |

#### 403 — Missing API key

| Field | Type | Description |
| --- | --- | --- |
| `statusCode` | number | Example: `403` |
| `message` | string | Example: `"API key is required"` |

#### 413 — Image over 25 MB

| Field | Type | Description |
| --- | --- | --- |
| `statusCode` | number | Example: `413` |
| `message` | string | Example: `"Image exceeds the 25 MB limit"` |

#### 422 — Image could not be decoded or converted

| Field | Type | Description |
| --- | --- | --- |
| `statusCode` | number | Example: `422` |
| `message` | string | Example: `"The image could not be decoded or processed"` |

#### 429 — Rate limit or monthly icon-conversion limit exceeded

| Field | Type | Description |
| --- | --- | --- |
| `statusCode` | number | Example: `429` |
| `message` | string | Example: `"Monthly icon-conversion limit exceeded"` |

## POST /api/v1/editor/iconset

**Convert an image to an Xcode .appiconset**

Turn any image into a ready-to-use Xcode asset catalog icon set: a zipped <name>.appiconset/ with Contents.json and every required PNG — unzip it into your .xcassets catalog and you are done. platform=macos (default) renders the classic 10-image macOS matrix (16/32/128/256/512pt at 1x and 2x, transparency preserved); platform=ios renders the single 1024×1024 universal image modern Xcode uses, flattened onto an opaque background (App Store validation rejects iOS icons with alpha — control the color with iosBackground, default white); platform=universal includes both. Set mask=true to make the artwork app-icon compliant first (same options as /editor/mask) — recommended for macOS artwork that is not already a standard macOS icon; leave it off for iOS, whose icons are full-bleed squares. PNG, JPEG, WebP and .icns inputs are accepted. Returns the zip by default, or JSON (base64) when output=json. Counts one call against your monthly icon-conversion budget. Max upload: 25 MB.

### Parameters

| Name | In | Type | Description |
| --- | --- | --- | --- |
| `x-api-key` | header (required) | string | API key for authentication. Metered against your monthly icon-conversion limit. Calls from an authorized macosicons.com origin or a valid search token are unmetered first-party. |

### Request body (application/json)

| Field | Type | Description |
| --- | --- | --- |
| `image` | string (required) | Base64 string or data URL of the image. For multipart/form-data send a file field named "image" instead; for a raw image body send the bytes directly and pass options as query params. Example: `"data:image/png;base64,iVBORw0KGgo…"` |
| `platform` | string | macos = 10-image macOS matrix; ios = single 1024×1024 universal image; universal = both. Default: macos. Example: `"macos"` |
| `name` | string | Set name — the zip contains <name>.appiconset/. Default: AppIcon. Example: `"AppIcon"` |
| `mask` | boolean | Make the artwork app-icon compliant first (squircle + drop shadow), like /editor/mask. Default: false. Example: `true` |
| `iosBackground` | string | platform=ios/universal: opaque background the 1024 iOS icon is flattened onto (#rgb, #rrggbb). Default: white. Example: `"#ffffff"` |
| `output` | string | binary returns the zip bytes; json returns a base64 data URL plus metadata. Default: binary. Example: `"binary"` |

### Responses

#### 200 — The zipped .appiconset (application/zip, Content-Disposition: attachment) by default. When output=json, a JSON object with the base64 data URL and metadata.

| Field | Type | Description |
| --- | --- | --- |
| `ok` | boolean | Example: `true` |
| `name` | string | Example: `"AppIcon"` |
| `platform` | string | Example: `"macos"` |
| `filename` | string | Example: `"AppIcon.appiconset.zip"` |
| `bytes` | number | Example: `812340` |
| `masked` | boolean | Example: `true` |
| `file` | string | Base64 data URL of the zip (output=json only). Example: `"data:application/zip;base64,UEsDBAo…"` |

#### 400 — No image provided or invalid options

| Field | Type | Description |
| --- | --- | --- |
| `statusCode` | number | Example: `400` |
| `message` | string | Example: `"No image provided"` |

#### 401 — Invalid API key

| Field | Type | Description |
| --- | --- | --- |
| `statusCode` | number | Example: `401` |
| `message` | string | Example: `"Invalid API key"` |

#### 403 — Missing API key

| Field | Type | Description |
| --- | --- | --- |
| `statusCode` | number | Example: `403` |
| `message` | string | Example: `"API key is required"` |

#### 413 — Image over 25 MB

| Field | Type | Description |
| --- | --- | --- |
| `statusCode` | number | Example: `413` |
| `message` | string | Example: `"Image exceeds the 25 MB limit"` |

#### 422 — Image could not be decoded or converted

| Field | Type | Description |
| --- | --- | --- |
| `statusCode` | number | Example: `422` |
| `message` | string | Example: `"The image could not be decoded or processed"` |

#### 429 — Rate limit or monthly icon-conversion limit exceeded

| Field | Type | Description |
| --- | --- | --- |
| `statusCode` | number | Example: `429` |
| `message` | string | Example: `"Monthly icon-conversion limit exceeded"` |

## POST /api/v1/editor/extract

**Extract PNGs from a .icns file**

The inverse of /editor/convert?format=icns: pull the embedded PNG images out of a macOS .icns container — no Mac or iconutil needed. By default the largest embedded PNG is returned. Pass size=N to get the smallest embedded image at least N pixels wide (falls back to the largest available), or all=true to get every embedded PNG in one zip, with files named <type>_<width>x<height>.png. Legacy non-PNG entries (il32, is32, …) are skipped. Send the .icns as multipart/form-data (file field "image"), a raw binary body, or JSON (base64 / data URL). Returns binary by default (image/png, or application/zip when all=true); JSON (base64 + entry metadata) when output=json. Counts one call against your monthly icon-conversion budget. Max upload: 25 MB.

### Parameters

| Name | In | Type | Description |
| --- | --- | --- | --- |
| `x-api-key` | header (required) | string | API key for authentication. Metered against your monthly icon-conversion limit. Calls from an authorized macosicons.com origin or a valid search token are unmetered first-party. |

### Request body (application/json)

| Field | Type | Description |
| --- | --- | --- |
| `image` | string (required) | Base64 string or data URL of the .icns file. For multipart/form-data send a file field named "image" instead; for a raw binary body send the bytes directly and pass options as query params. Example: `"data:application/octet-stream;base64,aWNucw…"` |
| `size` | integer | Return the smallest embedded image at least this many pixels wide (largest available if none is). Ignored when all=true. Example: `512` |
| `all` | boolean | Return every embedded PNG in one zip instead of a single image. Default: false. Example: `false` |
| `name` | string | Base filename for the download. Default: icon. Example: `"icon"` |
| `output` | string | binary returns the raw bytes; json returns base64 data URLs plus per-entry metadata. Default: binary. Example: `"binary"` |

### Responses

#### 200 — The extracted PNG (image/png) or zip of all PNGs (application/zip when all=true), Content-Disposition: attachment. When output=json, a JSON object with entry metadata and base64 data URLs.

| Field | Type | Description |
| --- | --- | --- |
| `ok` | boolean | Example: `true` |
| `filename` | string | Example: `"icon_1024x1024.png"` |
| `entries` | object[] | Every PNG entry found in the container (metadata only) |
| `entries[].type` | string | Example: `"ic10"` |
| `entries[].width` | number | Example: `1024` |
| `entries[].height` | number | Example: `1024` |
| `entries[].bytes` | number | Example: `45427` |
| `images` | object[] | The returned image(s) as data URLs — one for a single-size request, all of them when all=true |
| `images[].type` | string | Example: `"ic10"` |
| `images[].width` | number | Example: `1024` |
| `images[].height` | number | Example: `1024` |
| `images[].image` | string | Example: `"data:image/png;base64,iVBORw0KGgo…"` |

#### 400 — No file provided or invalid options

| Field | Type | Description |
| --- | --- | --- |
| `statusCode` | number | Example: `400` |
| `message` | string | Example: `"No image provided"` |

#### 401 — Invalid API key

| Field | Type | Description |
| --- | --- | --- |
| `statusCode` | number | Example: `401` |
| `message` | string | Example: `"Invalid API key"` |

#### 403 — Missing API key

| Field | Type | Description |
| --- | --- | --- |
| `statusCode` | number | Example: `403` |
| `message` | string | Example: `"API key is required"` |

#### 413 — File over 25 MB

| Field | Type | Description |
| --- | --- | --- |
| `statusCode` | number | Example: `413` |
| `message` | string | Example: `"Image exceeds the 25 MB limit"` |

#### 422 — Not an ICNS container, or no PNG entries inside

| Field | Type | Description |
| --- | --- | --- |
| `statusCode` | number | Example: `422` |
| `message` | string | Example: `"The file is not an ICNS container"` |

#### 429 — Rate limit or monthly icon-conversion limit exceeded

| Field | Type | Description |
| --- | --- | --- |
| `statusCode` | number | Example: `429` |
| `message` | string | Example: `"Monthly icon-conversion limit exceeded"` |

## POST /api/v1/editor/removebg

**Remove an image's background**

Returns the same image with its background made transparent. By default (mode=auto) the background color is detected automatically from the image's border pixels (or set it explicitly with color), then removed with an edge-connected flood fill: only background regions reachable from the image border are cleared, so same-colored areas inside the subject are preserved. Anti-aliased edges are feathered and un-blended from the background color so no halo remains; set feather=false for a hard binary cut (pixel art, flat logos). When no single color describes the backdrop — a photo, a gradient, a screenshot — auto hands the image to an on-device segmentation model instead (force either side with mode=color / mode=ai). For artwork that was flattened onto pure white or pure black, mode=multiply and mode=screen do the compositing move instead: the tone itself becomes the alpha across the whole image at once, with no flood fill, so white enclosed by the subject clears too and soft edges, glows and shadows survive as real gradients; pureTolerance says how close to pure white/black still counts. Accepts PNG, JPEG, WebP, SVG and .icns input (largest embedded image is used); inputs over 4096px on the long edge are downscaled to fit. Send the image as multipart/form-data (file field "image"), a raw image body (options as query params), or JSON (base64 / data URL). Returns a transparent PNG by default (or WebP via format=webp), or JSON (base64 + metadata) when output=json. Counts one call against your monthly background-removal budget. Max upload: 25 MB.

### Parameters

| Name | In | Type | Description |
| --- | --- | --- | --- |
| `x-api-key` | header (required) | string | API key for authentication. Metered against your monthly background-removal limit. Calls from an authorized macosicons.com origin or a valid search token are unmetered first-party. |

### Request body (application/json)

| Field | Type | Description |
| --- | --- | --- |
| `image` | string (required) | Base64 string or data URL of the image. For multipart/form-data send a file field named "image" instead; for a raw binary body send the bytes directly and pass options as query params. Example: `"data:image/png;base64,iVBORw0KGgo…"` |
| `mode` | string | Which strategy to use: auto (default) routes between the color key and the segmentation model from the image itself; color forces the deterministic color key; ai forces the model; multiply keys out pure white and screen keys out pure black across the whole image, with no flood fill (white or black enclosed by the subject clears too). The tone keys are never chosen by auto — pick them when you know the artwork was flattened onto white or black. The response reports which one actually ran. Example: `"auto"` |
| `tolerance` | number | How far (0-100, as % of the max RGB distance) a pixel's color may drift from the background color and still be removed. Raise it for noisy JPEGs or subtle gradients, lower it if the subject is being eaten. Color-key modes only. Default: 12. Example: `12` |
| `pureTolerance` | number | How close to pure white (mode=multiply) or pure black (mode=screen) a tone has to be to count as fully background, as a % of the 0-255 range. 0 keys on literal #ffffff / #000000 only; 4 (default) clears anything from #f5f5f5 up, which is what a scan or a JPEG actually gives you. Tone-key modes only. Example: `4` |
| `color` | string | Background color to key out, as hex (#rgb, #rrggbb). When omitted it is detected from the border pixels. Color-key modes only — the tone keys are white/black by definition. Example: `"#ffffff"` |
| `feather` | boolean | Feather anti-aliased edges with partial alpha and un-blend their color from the background. false = hard binary cut. Under the tone keys this is the alpha ramp itself: false thresholds it at half coverage. Default: true. Example: `true` |
| `trim` | boolean | Crop the result to the remaining non-transparent content. Default: false. Example: `false` |
| `format` | string | Output format: png or webp (both carry transparency). Default: png. Example: `"png"` |
| `output` | string | binary returns the raw image bytes; json returns a base64 data URL plus metadata. Default: binary. Example: `"binary"` |

### Responses

#### 200 — The image with a transparent background (image/png or image/webp), with X-Background-Color / X-Background-Color-Source headers. When output=json, a JSON object with metadata and a base64 data URL.

| Field | Type | Description |
| --- | --- | --- |
| `ok` | boolean | Example: `true` |
| `width` | number | Example: `1024` |
| `height` | number | Example: `1024` |
| `format` | string | Example: `"png"` |
| `mode` | string | Which strategy actually ran: color, ai, multiply or screen. With mode=auto this is how you tell whether the color key or the model produced the result. Example: `"color"` |
| `model` | string | The segmentation model that produced the mask, when mode is "ai". null otherwise. Example: `null` |
| `inferenceMs` | number | Model inference time in ms, when mode is "ai". null otherwise. Example: `null` |
| `backgroundColor` | object | The color that was keyed out — white under multiply, black under screen |
| `backgroundColor.r` | number | Example: `255` |
| `backgroundColor.g` | number | Example: `255` |
| `backgroundColor.b` | number | Example: `255` |
| `backgroundColorSource` | string | "detected" (from the image), "provided" (the color param) or "implied" (fixed by the mode, as white is by multiply and black by screen). Example: `"detected"` |
| `backgroundAgreement` | number | Share (0-1) of the color vote the background color took — how flat the backdrop is. This is the signal mode=auto routes on: below ~0.85 it hands the image to the model. Always 1 under the tone keys, which take no vote. Example: `0.97` |
| `removedFraction` | number | Share (0-1) of pixels that were made transparent. Example: `0.62` |
| `trimmed` | boolean | Example: `false` |
| `image` | string | Example: `"data:image/png;base64,iVBORw0KGgo…"` |

#### 400 — No image provided or invalid options

| Field | Type | Description |
| --- | --- | --- |
| `statusCode` | number | Example: `400` |
| `message` | string | Example: `"No image provided"` |

#### 401 — Invalid API key

| Field | Type | Description |
| --- | --- | --- |
| `statusCode` | number | Example: `401` |
| `message` | string | Example: `"Invalid API key"` |

#### 403 — Missing API key

| Field | Type | Description |
| --- | --- | --- |
| `statusCode` | number | Example: `403` |
| `message` | string | Example: `"API key is required"` |

#### 413 — Image over 25 MB

| Field | Type | Description |
| --- | --- | --- |
| `statusCode` | number | Example: `413` |
| `message` | string | Example: `"Image exceeds the 25 MB limit"` |

#### 422 — The image could not be decoded or processed

| Field | Type | Description |
| --- | --- | --- |
| `statusCode` | number | Example: `422` |
| `message` | string | Example: `"The image could not be decoded or processed"` |

#### 429 — Rate limit or monthly background-removal limit exceeded

| Field | Type | Description |
| --- | --- | --- |
| `statusCode` | number | Example: `429` |
| `message` | string | Example: `"Monthly background-removal limit exceeded"` |

## POST /api/v1/editor/favicon

**Generate a complete web favicon pack**

Turn any image into a ready-to-deploy web favicon pack, zipped: multi-size favicon.ico, favicon-16x16.png, favicon-32x32.png, an opaque 180×180 apple-touch-icon.png (iOS renders touch icons without alpha — control the fill with appleTouchBackground), android-chrome-192x192.png and android-chrome-512x512.png, a standards-compliant site.webmanifest, and favicon-snippet.html with the exact tags for your <head>. Copy the files to your site root and you are done. PNG, JPEG, WebP and .icns inputs are accepted. Returns the zip by default, or JSON (base64 + file list) when output=json. Counts one call against your monthly icon-conversion budget. Max upload: 25 MB.

### Parameters

| Name | In | Type | Description |
| --- | --- | --- | --- |
| `x-api-key` | header (required) | string | API key for authentication. Metered against your monthly icon-conversion limit. Calls from an authorized macosicons.com origin or a valid search token are unmetered first-party. |

### Request body (application/json)

| Field | Type | Description |
| --- | --- | --- |
| `image` | string (required) | Base64 string or data URL of the image. For multipart/form-data send a file field named "image" instead; for a raw image body send the bytes directly and pass options as query params. Example: `"data:image/png;base64,iVBORw0KGgo…"` |
| `name` | string | Site/app name written into site.webmanifest (also names the download). Default: App. Example: `"My Site"` |
| `themeColor` | string | theme_color in the manifest and the snippet meta tag. Default: #ffffff. Example: `"#1e90ff"` |
| `backgroundColor` | string | background_color in the manifest. Default: #ffffff. Example: `"#ffffff"` |
| `appleTouchBackground` | string | Opaque background the apple-touch-icon is flattened onto. Default: white. Example: `"#ffffff"` |
| `pathPrefix` | string | Href/src prefix used in the manifest and HTML snippet (e.g. "/static/"). Default: /. Example: `"/"` |
| `output` | string | binary returns the zip bytes; json returns a base64 data URL plus the file list. Default: binary. Example: `"binary"` |

### Responses

#### 200 — The zipped favicon pack (application/zip, Content-Disposition: attachment) by default. When output=json, a JSON object with the base64 data URL and the file list.

| Field | Type | Description |
| --- | --- | --- |
| `ok` | boolean | Example: `true` |
| `filename` | string | Example: `"My Site_favicons.zip"` |
| `bytes` | number | Example: `421876` |
| `files` | string[] | The paths inside the zip. Example: `["favicon.ico","favicon-16x16.png","site.webmanifest"]` |
| `file` | string | Base64 data URL of the zip (output=json only). Example: `"data:application/zip;base64,UEsDBAo…"` |

#### 400 — No image provided or invalid options

| Field | Type | Description |
| --- | --- | --- |
| `statusCode` | number | Example: `400` |
| `message` | string | Example: `"No image provided"` |

#### 401 — Invalid API key

| Field | Type | Description |
| --- | --- | --- |
| `statusCode` | number | Example: `401` |
| `message` | string | Example: `"Invalid API key"` |

#### 403 — Missing API key

| Field | Type | Description |
| --- | --- | --- |
| `statusCode` | number | Example: `403` |
| `message` | string | Example: `"API key is required"` |

#### 413 — Image over 25 MB

| Field | Type | Description |
| --- | --- | --- |
| `statusCode` | number | Example: `413` |
| `message` | string | Example: `"Image exceeds the 25 MB limit"` |

#### 422 — Image could not be decoded or converted

| Field | Type | Description |
| --- | --- | --- |
| `statusCode` | number | Example: `422` |
| `message` | string | Example: `"The image could not be decoded or processed"` |

#### 429 — Rate limit or monthly icon-conversion limit exceeded

| Field | Type | Description |
| --- | --- | --- |
| `statusCode` | number | Example: `429` |
| `message` | string | Example: `"Monthly icon-conversion limit exceeded"` |

## POST /api/v1/editor/pack

**Generate a complete icon release kit**

One call, one zip with every icon artifact a cross-platform app release needs: <name>.icns (macOS), <name>.ico (Windows), <name>.appiconset/ (Xcode asset catalog — platform selects the macOS matrix, iOS single 1024, or both), <name>.icon/ (Icon Composer bundle for Xcode 26+), a web/ favicon pack (favicon.ico, PNG sizes, apple-touch-icon, site.webmanifest, HTML snippet), and a README explaining each artifact. Set mask=true to conform the artwork to the macOS squircle + drop-shadow standard for the macOS-look artifacts (.icns and the macOS appiconset entries; same tuning options as /editor/mask) — everything else always uses the raw artwork, matching each platform's own convention. This is the endpoint to call from CI on every release. Returns the zip by default, or JSON (base64 + file list) when output=json. Consumes 5 calls of your monthly icon-conversion budget (one per artifact family). Max upload: 25 MB.

### Parameters

| Name | In | Type | Description |
| --- | --- | --- | --- |
| `x-api-key` | header (required) | string | API key for authentication. Metered against your monthly icon-conversion limit. Calls from an authorized macosicons.com origin or a valid search token are unmetered first-party. |

### Request body (application/json)

| Field | Type | Description |
| --- | --- | --- |
| `image` | string (required) | Base64 string or data URL of the image. For multipart/form-data send a file field named "image" instead; for a raw image body send the bytes directly and pass options as query params. Example: `"data:image/png;base64,iVBORw0KGgo…"` |
| `name` | string | Base name for every artifact and the top-level folder. Default: AppIcon. Example: `"AppIcon"` |
| `platform` | string | Which entries the .appiconset includes: macos, ios, or universal. Default: universal. Example: `"universal"` |
| `mask` | boolean | Conform the artwork to the macOS app-icon standard for the macOS-look artifacts. Default: false. Example: `true` |
| `fill` | string | Icon Composer bundle background fill. Default: opaque black. Example: `"#1e90ff"` |
| `themeColor` | string | theme_color for the favicon manifest/snippet. Default: #ffffff. Example: `"#ffffff"` |
| `iosBackground` | string | Opaque background for the iOS 1024 image and apple-touch-icon. Default: white. Example: `"#ffffff"` |
| `output` | string | binary returns the zip bytes; json returns a base64 data URL plus the file list. Default: binary. Example: `"binary"` |

### Responses

#### 200 — The release-kit zip (application/zip, Content-Disposition: attachment) by default. When output=json, a JSON object with the base64 data URL, the file list, and how many conversions were charged.

| Field | Type | Description |
| --- | --- | --- |
| `ok` | boolean | Example: `true` |
| `name` | string | Example: `"AppIcon"` |
| `platform` | string | Example: `"universal"` |
| `filename` | string | Example: `"AppIcon_release_pack.zip"` |
| `bytes` | number | Example: `1873222` |
| `masked` | boolean | Example: `true` |
| `conversionsCharged` | number | Example: `5` |
| `files` | string[] | The paths inside the zip. Example: `["AppIcon/README.md","AppIcon/AppIcon.icns","AppIcon/AppIcon.ico"]` |
| `file` | string | Base64 data URL of the zip (output=json only). Example: `"data:application/zip;base64,UEsDBAo…"` |

#### 400 — No image provided or invalid options

| Field | Type | Description |
| --- | --- | --- |
| `statusCode` | number | Example: `400` |
| `message` | string | Example: `"No image provided"` |

#### 401 — Invalid API key

| Field | Type | Description |
| --- | --- | --- |
| `statusCode` | number | Example: `401` |
| `message` | string | Example: `"Invalid API key"` |

#### 403 — Missing API key

| Field | Type | Description |
| --- | --- | --- |
| `statusCode` | number | Example: `403` |
| `message` | string | Example: `"API key is required"` |

#### 413 — Image over 25 MB

| Field | Type | Description |
| --- | --- | --- |
| `statusCode` | number | Example: `413` |
| `message` | string | Example: `"Image exceeds the 25 MB limit"` |

#### 422 — Image could not be decoded or converted

| Field | Type | Description |
| --- | --- | --- |
| `statusCode` | number | Example: `422` |
| `message` | string | Example: `"The image could not be decoded or processed"` |

#### 429 — Rate limit or monthly icon-conversion limit exceeded

| Field | Type | Description |
| --- | --- | --- |
| `statusCode` | number | Example: `429` |
| `message` | string | Example: `"Monthly icon-conversion limit exceeded"` |

## POST /api/v1/editor/palette

**Extract an icon's colors**

Analyzes an image's non-transparent pixels and returns its colors: the alpha-weighted average, the single most prominent (dominant) color with its coverage share, and the top-N palette. Also returns ready-to-use fill suggestions — a background hex (prominent, falling back to average) and the matching Icon Composer icon.json gradient fill string, so you can theme UI around an icon or pre-fill /editor/icon's fill option. Tune sampling with alphaThreshold (min alpha for a pixel to count, default 16), prominenceThreshold (min share for the dominant color, default 0.1) and count (palette size, 1–16, default 5). PNG, JPEG, WebP and .icns inputs are accepted. Always returns JSON. Counts one call against your monthly icon-conversion budget. Max upload: 25 MB.

### Parameters

| Name | In | Type | Description |
| --- | --- | --- | --- |
| `x-api-key` | header (required) | string | API key for authentication. Metered against your monthly icon-conversion limit. Calls from an authorized macosicons.com origin or a valid search token are unmetered first-party. |

### Request body (application/json)

| Field | Type | Description |
| --- | --- | --- |
| `image` | string (required) | Base64 string or data URL of the image. For multipart/form-data send a file field named "image" instead; for a raw image body send the bytes directly and pass options as query params. Example: `"data:image/png;base64,iVBORw0KGgo…"` |
| `count` | integer | How many palette colors to return (1–16). Default: 5. Example: `5` |
| `alphaThreshold` | integer | A pixel is sampled only when its alpha exceeds this (0–255). Default: 16. Example: `16` |
| `prominenceThreshold` | number | Min share (0–1) the dominant color must cover to count as prominent. Default: 0.1. Example: `0.1` |

### Responses

#### 200 — The extracted colors.

| Field | Type | Description |
| --- | --- | --- |
| `ok` | boolean | Example: `true` |
| `sampled` | number | How many pixels passed the alpha threshold. Example: `40960` |
| `average` | object | Alpha-weighted mean color, or null for fully transparent input |
| `average.hex` | string | Example: `"#4c6085"` |
| `average.r` | number |  |
| `average.g` | number |  |
| `average.b` | number |  |
| `prominent` | object | Dominant color with its coverage share, or null when nothing is prominent enough |
| `prominent.hex` | string | Example: `"#4c6085"` |
| `prominent.r` | number |  |
| `prominent.g` | number |  |
| `prominent.b` | number |  |
| `prominent.share` | number | Example: `0.42` |
| `palette` | object[] | Top colors, most common first |
| `palette[].hex` | string |  |
| `palette[].r` | number |  |
| `palette[].g` | number |  |
| `palette[].b` | number |  |
| `palette[].share` | number |  |
| `fills` | object |  |
| `fills.background` | string | Suggested background hex (prominent → average → white). Example: `"#4c6085"` |
| `fills.iconComposer` | string | The same color encoded as an Icon Composer icon.json gradient fill. Example: `"extended-srgb:0.29804,0.37647,0.52157,1.0"` |

#### 400 — No image provided or invalid options

| Field | Type | Description |
| --- | --- | --- |
| `statusCode` | number | Example: `400` |
| `message` | string | Example: `"No image provided"` |

#### 401 — Invalid API key

| Field | Type | Description |
| --- | --- | --- |
| `statusCode` | number | Example: `401` |
| `message` | string | Example: `"Invalid API key"` |

#### 403 — Missing API key

| Field | Type | Description |
| --- | --- | --- |
| `statusCode` | number | Example: `403` |
| `message` | string | Example: `"API key is required"` |

#### 413 — Image over 25 MB

| Field | Type | Description |
| --- | --- | --- |
| `statusCode` | number | Example: `413` |
| `message` | string | Example: `"Image exceeds the 25 MB limit"` |

#### 422 — Image could not be decoded or converted

| Field | Type | Description |
| --- | --- | --- |
| `statusCode` | number | Example: `422` |
| `message` | string | Example: `"The image could not be decoded or processed"` |

#### 429 — Rate limit or monthly icon-conversion limit exceeded

| Field | Type | Description |
| --- | --- | --- |
| `statusCode` | number | Example: `429` |
| `message` | string | Example: `"Monthly icon-conversion limit exceeded"` |

## POST /api/v1/editor/preview

**Render an icon in context**

Renders an icon into a brand-neutral mockup scene so you can see (and publish) how it actually looks in place: context=dock places it in a macOS Dock strip among ghost placeholder icons; context=homescreen places it on an iPhone-style home screen grid (with an optional label under it); context=desktop renders a large hero shot on a wallpaper with a Finder-style label. context=all renders all three in one call, and theme=both renders each scene in light and dark — a request that produces more than one image always answers with JSON and an `images` array. The backdrop is pluggable: by default a gradient derived from the icon's own dominant color, or pass wallpaper=#hex for a color, wallpaper=blur for a bloom of the icon's own predominant colors, gradient=<preset> for one of twelve built-in gradients, gradient=random for a surprise, or upload your own wallpaper image (multipart field "background", or base64 "backgroundImage" in JSON). By default the artwork is conformed to the platform shape first (macOS squircle + drop shadow for dock/desktop, iOS full-bleed rounded rect for homescreen); mask=false uses it as-is. Scenes contain no licensed OS artwork, so the output is safe for App Store screenshots and launch posts. PNG, JPEG, WebP and .icns inputs are accepted. Returns a PNG by default, JSON (base64) when output=json, or hosted files stored on your account when output=file. Each rendered image counts as one call against your monthly icon-conversion budget. Max upload: 25 MB.

### Parameters

| Name | In | Type | Description |
| --- | --- | --- | --- |
| `x-api-key` | header (required) | string | API key for authentication. Metered against your monthly icon-conversion limit. Calls from an authorized macosicons.com origin or a valid search token are unmetered first-party. |

### Request body (application/json)

| Field | Type | Description |
| --- | --- | --- |
| `image` | string (required) | Base64 string or data URL of the image. For multipart/form-data send a file field named "image" instead; for a raw image body send the bytes directly and pass options as query params. Example: `"data:image/png;base64,iVBORw0KGgo…"` |
| `backgroundImage` | string | Optional wallpaper image to put behind the scene, cropped to fill it. For multipart/form-data send a file field named "background" instead. Wins over wallpaper and gradient. Example: `"data:image/jpeg;base64,/9j/4AAQSkZJRg…"` |
| `context` | string | Which scene to render: dock, homescreen, desktop — or all for every scene in one call (a comma-separated list also works). Default: dock. Example: `"dock"` |
| `theme` | string | Scene theme: dark, light, or both. "both" renders every requested scene twice, once in each theme. Default: dark. Example: `"dark"` |
| `width` | integer | Output width in px, 320–2560 (height follows the scene aspect ratio). Defaults: dock/desktop 1600, homescreen 1170. Example: `1600` |
| `wallpaper` | string | Backdrop: auto (default — a gradient from the icon's dominant color), a #hex color, blur (a bloom of the icon's own predominant colors), random, or a gradient preset name. Example: `"blur"` |
| `gradient` | string | Use a built-in gradient backdrop: twilight (the default), sunset, aurora, ocean, dusk, blossom, ember, mint, lavender, citrus, graphite or slate. "random" picks one at random. Example: `"sunset"` |
| `blur` | number | Blur strength for the blur backdrop and for an uploaded background image. Defaults: 10 for blur, 0 (none) for an image. Example: `10` |
| `label` | string | App name rendered under the icon (homescreen and desktop scenes). Example: `"My App"` |
| `mask` | boolean | Conform the artwork to the platform shape first. Default: true. Example: `true` |
| `output` | string | binary returns the PNG bytes (single image only); json returns base64 data URLs plus metadata; file stores each render on your account and returns hosted URLs + asset ids. Anything that renders more than one image always answers with JSON. Default: binary. Example: `"binary"` |
| `includeData` | boolean | With output=file, also include the base64 image alongside the hosted URL. Default: false. Example: `false` |

### Responses

#### 200 — The rendered scene (image/png, Content-Disposition: attachment) by default. With output=json or output=file — or whenever the request renders more than one image — a JSON object with an `images` array instead.

| Field | Type | Description |
| --- | --- | --- |
| `ok` | boolean | Example: `true` |
| `count` | number | How many images were rendered (scenes × themes). Example: `6` |
| `wallpaperStyle` | string | How the backdrop was produced: auto, color, gradient, blur or image. Example: `"auto"` |
| `gradient` | string | The gradient preset that was used, when wallpaperStyle is "gradient" (null otherwise). Example: `"twilight"` |
| `wallpaper` | string | Representative backdrop color — given, derived from the icon, or sampled from the background image. Example: `"#4c6085"` |
| `images` | object[] | One entry per scene × theme. A single-image response also mirrors the entry's fields at the top level, so existing single-scene callers keep working |
| `images[].context` | string | Example: `"dock"` |
| `images[].theme` | string | Example: `"dark"` |
| `images[].width` | number | Example: `1600` |
| `images[].height` | number | Example: `1000` |
| `images[].wallpaper` | string | Example: `"#4c6085"` |
| `images[].filename` | string | Example: `"preview_dock.png"` |
| `images[].bytes` | number | Example: `148213` |
| `images[].image` | string | Base64 data URL of the rendered PNG (output=json, or output=file with includeData). Example: `"data:image/png;base64,iVBORw0KGgo…"` |
| `images[].imageUrl` | string | Hosted URL of the stored render (output=file only). Example: `"https://files.macosicons.com/preview_dock.png"` |
| `images[].assetId` | string | Id of the stored asset, listable via GET /api/v1/assets (output=file only). Example: `"A1b2C3d4E5"` |

#### 400 — No image provided or invalid options

| Field | Type | Description |
| --- | --- | --- |
| `statusCode` | number | Example: `400` |
| `message` | string | Example: `"No image provided"` |

#### 401 — Invalid API key

| Field | Type | Description |
| --- | --- | --- |
| `statusCode` | number | Example: `401` |
| `message` | string | Example: `"Invalid API key"` |

#### 403 — Missing API key

| Field | Type | Description |
| --- | --- | --- |
| `statusCode` | number | Example: `403` |
| `message` | string | Example: `"API key is required"` |

#### 413 — Image over 25 MB

| Field | Type | Description |
| --- | --- | --- |
| `statusCode` | number | Example: `413` |
| `message` | string | Example: `"Image exceeds the 25 MB limit"` |

#### 422 — Image could not be decoded or converted

| Field | Type | Description |
| --- | --- | --- |
| `statusCode` | number | Example: `422` |
| `message` | string | Example: `"The image could not be decoded or processed"` |

#### 429 — Rate limit or monthly icon-conversion limit exceeded

| Field | Type | Description |
| --- | --- | --- |
| `statusCode` | number | Example: `429` |
| `message` | string | Example: `"Monthly icon-conversion limit exceeded"` |

## GET /api/v1/editor/usage

**Get editor usage & limits**

Returns the calling key's own editor usage and limits for the current calendar month, resetting on the 1st (UTC). Read-only — does not consume budget. Requires a personal x-api-key. Use it to render the usage / limit display in your own tools.

### Parameters

| Name | In | Type | Description |
| --- | --- | --- | --- |
| `x-api-key` | header (required) | string | Personal API key. |

### Responses

#### 200 — Current usage and limits for both editor endpoints.

| Field | Type | Description |
| --- | --- | --- |
| `mask` | object | Usage for /api/v1/editor/mask, counted in requests |
| `mask.endpoint` | string | Example: `"/api/v1/editor/mask"` |
| `mask.used` | number | Example: `12` |
| `mask.limit` | number | Example: `100` |
| `mask.remaining` | number | Example: `88` |
| `mask.plan` | string | Example: `"free"` |
| `mask.month` | string | Example: `"2026-07"` |
| `mask.resetsAt` | string | Example: `"2026-08-01T00:00:00.000Z"` |
| `generate` | object | Usage for /api/v1/editor/generate, counted in images. A matted icon (matting:true) counts as 2 images, since matting runs two model calls per icon |
| `generate.endpoint` | string | Example: `"/api/v1/editor/generate"` |
| `generate.used` | number | Example: `3` |
| `generate.limit` | number | Example: `20` |
| `generate.remaining` | number | Example: `17` |
| `generate.plan` | string | Example: `"free"` |
| `generate.month` | string | Example: `"2026-07"` |
| `generate.resetsAt` | string | Example: `"2026-08-01T00:00:00.000Z"` |
| `convert` | object | Shared icon-conversion budget for the /editor conversion endpoints (icns, icon, iconset, extract, ico, favicon, pack, palette, preview), counted in requests. A release pack consumes 5 |
| `convert.endpoint` | string | Example: `"/api/v1/editor/convert"` |
| `convert.used` | number | Example: `5` |
| `convert.limit` | number | Example: `100` |
| `convert.remaining` | number | Example: `95` |
| `convert.plan` | string | Example: `"free"` |
| `convert.month` | string | Example: `"2026-07"` |
| `convert.resetsAt` | string | Example: `"2026-08-01T00:00:00.000Z"` |
| `removebg` | object | Usage for /api/v1/editor/removebg, counted in requests |
| `removebg.endpoint` | string | Example: `"/api/v1/editor/removebg"` |
| `removebg.used` | number | Example: `4` |
| `removebg.limit` | number | Example: `50` |
| `removebg.remaining` | number | Example: `46` |
| `removebg.plan` | string | Example: `"free"` |
| `removebg.month` | string | Example: `"2026-07"` |
| `removebg.resetsAt` | string | Example: `"2026-08-01T00:00:00.000Z"` |

#### 401 — Invalid API key

| Field | Type | Description |
| --- | --- | --- |
| `statusCode` | number | Example: `401` |
| `message` | string | Example: `"Invalid API key"` |

#### 403 — Missing API key

| Field | Type | Description |
| --- | --- | --- |
| `statusCode` | number | Example: `403` |
| `message` | string | Example: `"API key is required"` |

## GET /api/v1/assets

**List your generated & uploaded assets**

Every image produced by the AI generation endpoints — and every file uploaded through the editor — is stored on your account as an asset. This endpoint returns them newest-first with pagination and filters, so you can see your full generation history: the hosted URL, where each asset came from (origin: api for key-authenticated calls, editor / editor-chat for the editor UI), the credits the generation cost, the prompt and model used, the dimensions, and — for assets created in the editor — the project and document they belong to. Read-only: does not consume any quota. Authenticate with a personal x-api-key to list your API-generated assets.

> **Your generation history in one place** — Images from /api/v1/editor/generate (and the editor's AI tools) are stored automatically — you never upload them yourself. Call this endpoint to page through everything you have generated, filter by origin/kind/project, and read the credits each one cost.

### Parameters

| Name | In | Type | Description |
| --- | --- | --- | --- |
| `x-api-key` | header (required) | string | Personal API key. Lists the assets owned by this key. |
| `kind` | query | string | Filter by asset kind: image, document, archive, data, other. |
| `source` | query | string | Filter by how the asset was created: ai-generated, user-upload, editor-upload, remove-bg, imported, derived. |
| `origin` | query | string | Filter by where the request came from: editor, editor-chat, api, mcp, batch, internal. |
| `projectId` | query | string | Filter to a single editor project (client id). |
| `documentClientId` | query | string | Filter to a single editor document (client id). |
| `skip` | query | integer | Offset for pagination. Default: 0. |
| `limit` | query | integer | Page size (1–200). Default: 60. |

### Responses

#### 200 — Your assets, newest first.

| Field | Type | Description |
| --- | --- | --- |
| `ok` | boolean | Example: `true` |
| `total` | number | Total assets matching the filters. Example: `128` |
| `skip` | number | Example: `0` |
| `limit` | number | Example: `60` |
| `nextSkip` | number | Skip value for the next page, or null on the last page. Example: `60` |
| `assets` | object[] |  |
| `assets[].id` | string | Asset id (assetId returned by the generation endpoints). Example: `"aB9xY2…"` |
| `assets[].fileUrl` | string | Example: `"https://…/ai-generated/…/uuid.png"` |
| `assets[].kind` | string | Example: `"image"` |
| `assets[].source` | string | Example: `"ai-generated"` |
| `assets[].origin` | string | Example: `"api"` |
| `assets[].mimeType` | string | Example: `"image/png"` |
| `assets[].bytes` | number | Example: `184320` |
| `assets[].width` | number | Example: `1024` |
| `assets[].height` | number | Example: `1024` |
| `assets[].prompt` | string | Generation prompt, when the asset was AI-generated. Example: `"a friendly robot mascot in glossy ceramic"` |
| `assets[].modelId` | string | Example: `"gemini/gemini-3.1-flash-image"` |
| `assets[].transparent` | boolean | Example: `false` |
| `assets[].credits` | number | Credits the generation cost. Example: `25` |
| `assets[].projectId` | string | Editor project id, when created in the editor. Example: `"proj_4b1c…"` |
| `assets[].projectName` | string | Resolved project name, when available. Example: `"Menubar set"` |
| `assets[].folderId` | string | Example: `"fold_7d3e…"` |
| `assets[].documentClientId` | string | Editor document id, when created in the editor. Example: `"doc_9f2a…"` |
| `assets[].documentName` | string | Resolved document name, when available. Example: `"Robot.icon"` |
| `assets[].createdAt` | string | Example: `"2026-07-16T12:00:00.000Z"` |

#### 401 — Invalid API key

| Field | Type | Description |
| --- | --- | --- |
| `statusCode` | number | Example: `401` |
| `message` | string | Example: `"A personal API key or editor session token is required to list assets."` |

#### 403 — No credential presented

| Field | Type | Description |
| --- | --- | --- |
| `statusCode` | number | Example: `403` |
| `message` | string | Example: `"API key is required"` |

#### 429 — Rate limit exceeded

| Field | Type | Description |
| --- | --- | --- |
| `statusCode` | number | Example: `429` |
| `message` | string | Example: `"Rate limit exceeded"` |

## GET /api/v1/usage

**Get all usage & limits**

Returns the calling key's usage across every metered endpoint for the current calendar month (resets on the 1st, UTC): the shared search/API quota (search + getCategories) and each editor budget (icon mask, AI generation, the icon-conversion budget shared by /editor/convert, /icon, /iconset and /extract, and background removal). Includes when the usage period started, when it resets, used/limit/remaining per endpoint, and the plan name. Read-only — does not consume any quota. Requires a personal x-api-key.

### Parameters

| Name | In | Type | Description |
| --- | --- | --- | --- |
| `x-api-key` | header (required) | string | Personal API key. |

### Responses

#### 200 — Current usage, limits and plan for the calling user.

| Field | Type | Description |
| --- | --- | --- |
| `plan` | string | The user's plan name. Example: `"free"` |
| `isPaid` | boolean | Example: `false` |
| `period` | object | The current usage period (calendar month, UTC) |
| `period.month` | string | Example: `"2026-07"` |
| `period.start` | string | Example: `"2026-07-01T00:00:00.000Z"` |
| `period.end` | string | Example: `"2026-08-01T00:00:00.000Z"` |
| `period.resetsAt` | string | Example: `"2026-08-01T00:00:00.000Z"` |
| `endpoints` | object | Per-endpoint usage. limit/remaining are null when uncapped |
| `endpoints.search` | object | Shared quota for search + getCategories |
| `endpoints.search.endpoint` | string | Example: `"/api/v1/search"` |
| `endpoints.search.description` | string |  |
| `endpoints.search.used` | number | Example: `42` |
| `endpoints.search.limit` | number | Example: `5000` |
| `endpoints.search.remaining` | number | Example: `4958` |
| `endpoints.mask` | object | Usage for /api/v1/editor/mask, counted in requests |
| `endpoints.mask.endpoint` | string | Example: `"/api/v1/editor/mask"` |
| `endpoints.mask.description` | string |  |
| `endpoints.mask.used` | number | Example: `7` |
| `endpoints.mask.limit` | number | Example: `1000` |
| `endpoints.mask.remaining` | number | Example: `993` |
| `endpoints.generate` | object | Usage for /api/v1/editor/generate, counted in images. A matted icon (matting:true) counts as 2 images, since matting runs two model calls per icon |
| `endpoints.generate.endpoint` | string | Example: `"/api/v1/editor/generate"` |
| `endpoints.generate.description` | string |  |
| `endpoints.generate.used` | number | Example: `3` |
| `endpoints.generate.limit` | number | Example: `200` |
| `endpoints.generate.remaining` | number | Example: `197` |
| `endpoints.convert` | object | Shared icon-conversion budget for the /editor conversion endpoints (icns, icon, iconset, extract, ico, favicon, pack, palette, preview), counted in requests. A release pack consumes 5 |
| `endpoints.convert.endpoint` | string | Example: `"/api/v1/editor/convert"` |
| `endpoints.convert.description` | string |  |
| `endpoints.convert.used` | number | Example: `5` |
| `endpoints.convert.limit` | number | Example: `1000` |
| `endpoints.convert.remaining` | number | Example: `995` |
| `endpoints.removebg` | object | Usage for /api/v1/editor/removebg, counted in requests |
| `endpoints.removebg.endpoint` | string | Example: `"/api/v1/editor/removebg"` |
| `endpoints.removebg.description` | string |  |
| `endpoints.removebg.used` | number | Example: `4` |
| `endpoints.removebg.limit` | number | Example: `200` |
| `endpoints.removebg.remaining` | number | Example: `196` |

#### 400 — Called without a personal API key (no user to report on).

| Field | Type | Description |
| --- | --- | --- |
| `statusCode` | number | Example: `400` |
| `message` | string | Example: `"Provide a personal x-api-key to view your usage."` |

#### 401 — Invalid API key

| Field | Type | Description |
| --- | --- | --- |
| `statusCode` | number | Example: `401` |
| `message` | string | Example: `"Invalid API key"` |

#### 403 — Missing API key

| Field | Type | Description |
| --- | --- | --- |
| `statusCode` | number | Example: `403` |
| `message` | string | Example: `"API key is required"` |
