Документація MakeFav API
Генеруйте повні пакети favicon зі свого коду, скриптів збірки та CI-пайплайнів. Один запит перетворює будь-яке зображення на favicon.ico, іконки Apple touch, іконки Android і PWA, веб-маніфест та готовий до вставки HTML.
На цій сторінці
Швидкий старт
MakeFav API – це простий REST API на основі HTTPS і JSON. Усі ендпоінти розташовані за єдиною базовою URL-адресою:
https://makefav.com/api/v1 - Створіть безкоштовний обліковий запис
Зареєструйтеся, підтвердьте адресу електронної пошти та залишайте активною підписку на розсилку MakeFav – це єдина умова безкоштовного тарифу. - Створіть API-ключ
Відкрийте сторінку API-ключів в обліковому записі та створіть особистий ключ. Він починається з mf_ і показується лише один раз, тому зберігайте його надійно. Керування API-ключами - Надішліть перший запит
Завантажте зображення на ендпоінт generate і збережіть отриманий ZIP-файл:
curl --fail-with-body -sS https://makefav.com/api/v1/generate \
-H "Authorization: Bearer $MAKEFAV_API_KEY" \
-F "[email protected]" \
-F "app_name=My App" \
-F "theme_color=#2f9e68" \
-F "radius=20" \
-o favicons.zip Unzip favicons.zip, upload the files to your website and paste the HTML code from the end of readme-makefav.txt into the <head> of every page.
Автентифікація
Кожен запит до захищеного ендпоінта має містити особистий API-ключ. Ви можете створювати, називати й відкликати ключі будь-коли на сторінці свого облікового запису: API-ключі
Send the key as a bearer token in the Authorization header, or – if your HTTP client makes that awkward – in the X-API-Key header. Both are equivalent.
Authorization: Bearer mf_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
# or, alternatively
X-API-Key: mf_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx Missing or invalid keys return 401 Unauthorized. The GET /sizes and GET /openapi.json endpoints do not require a key.
Ліміти запитів
За замовчуванням кожен API-ключ може робити 100 запитів на годину. Кожна автентифікована відповідь показує ваш поточний стан:
| Заголовок | Опис |
|---|---|
X-RateLimit-Limit | Максимальна кількість запитів, дозволена в поточному вікні. |
X-RateLimit-Remaining | Залишок запитів у поточному вікні. |
X-RateLimit-Reset | Unix-мітка часу (секунди, UTC) скидання вікна. |
HTTP/1.1 200 OK
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 97
X-RateLimit-Reset: 1767225600 When the limit is exceeded the API responds with HTTP 429 and the error code rate_limited. Wait until the time in X-RateLimit-Reset before retrying – do not retry in a tight loop.
HTTP/1.1 429 Too Many Requests
Content-Type: application/json
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1767225600
{"error": {"code": "rate_limited", "message": "Rate limit exceeded. Try again after the reset time."}} You can check your current quota at any time with GET /me.
Ендпоінти
| Ендпоінт | Автентифікація | Опис |
|---|---|---|
POST /generate | API-ключ | Створити повний пакет favicon. |
GET /me | API-ключ | Ваш обліковий запис і поточний стан ліміту запитів. |
GET /sizes | Немає | Список усіх файлів іконок і розмірів, які створює генератор. |
GET /openapi.json | Немає | Машинозчитувана специфікація OpenAPI 3.1. |
Згенерувати пакет favicon
/generateСтворює повний пакет favicon з одного вихідного зображення. Надішліть зображення одним із трьох способів:
| Тип вмісту | Поле зображення | Опис |
|---|---|---|
multipart/form-data | image | Завантаження файлу. Рекомендовано – найефективніший варіант. |
application/json | image_base64 | Base64-encoded image, either raw base64 or a data URL such as data:image/png;base64,iVBOR…. |
application/json | image_url | Публічна http- або https-адреса зображення. Сервер завантажує його (макс. 10 МБ). |
Підтримувані вхідні формати: PNG, JPG, WebP, AVIF, GIF, BMP, SVG та ICO. Максимальний розмір файлу: 10 МБ.
Параметри
Усі параметри необов’язкові. Надсилайте їх як поля форми (multipart) або як властивості JSON. Недійсні значення замінюються значенням за замовчуванням.
| Ім’я | Тип / діапазон | За замовчуванням | Опис |
|---|---|---|---|
app_name | рядок, макс. 60 символів | My Website | Назва вашого сайту чи застосунку, використовується в manifest.json і на прикладі сторінки. |
short_name | рядок, макс. 24 символи | перші 12 символів app_name | Коротка назва під іконкою на домашніх екранах Android. |
theme_color | #rrggbb | #ffffff | Колір інтерфейсу браузера (мета-тег theme-color і маніфест). |
tile_color | #rrggbb | такий самий, як theme_color | Колір тла плиток Windows на екрані «Пуск». |
background | transparent | #rrggbb | transparent | Тло іконок для браузера, Android і Windows. |
apple_background | #rrggbb | #ffffff | Тло іконок Apple touch і maskable-іконки (iOS не підтримує прозорість). Ігнорується, якщо background має колір – тоді використовується цей колір. |
padding | ціле число, 0–40 (%) | 0 | Порожній простір навколо зображення у відсотках від розміру іконки. |
radius | ціле число, 0–50 (%) | 0 | Заокруглені кути. Значення 50 дає коло. Не застосовується до іконок Apple, які iOS заокруглює самостійно. |
zoom | ціле число, 50–400 (%) | 100 | Масштаб зображення всередині іконки. Значення понад 100 обрізають зображення. |
offset_x, offset_y | число, від -1 до 1 | 0 | Зміщує зображення по горизонталі / вертикалі як частку розміру іконки. |
path | рядок | / | Папка на вашому сервері, де зберігатимуться іконки. Використовується в HTML-фрагменті, маніфесті та browserconfig. |
display | standalone | fullscreen | minimal-ui | browser | standalone | Режим відображення PWA, записаний у маніфест. |
pixelated | auto | 1 | 0 | auto | Чітке масштабування методом найближчого сусіда для піксель-арту. Значення auto вмикає його для джерел розміром 64 px або менше. |
maskable | 1 | 0 | 1 | Також створити maskable-icon-512x512.png із відступом безпечної зони для адаптивних іконок Android. |
ico_sizes | список через кому: 16, 24, 32, 48, 64, 128, 256 | 16,32,48 | Розміри, вбудовані у favicon.ico. |
groups | список через кому: favicon, apple, android, ms | favicon,apple,android,ms | Набори іконок для генерації: favicon для браузерів, іконки Apple touch, іконки Android / PWA та плитки Windows. |
format | zip | json | zip | Формат відповіді – див. нижче. |
Відповідь
format=zip- Binary ZIP archive with
Content-Type: application/zipandContent-Disposition: attachment. Default. format=json- JSON object with the package
id(32 hex characters), thehtmlsnippet for your <head> and afilesarray; each file hasname,mime,size(bytes) andbase64contents.
Приклад запиту – multipart, відповідь у форматі ZIP
curl --fail-with-body -sS https://makefav.com/api/v1/generate \
-H "Authorization: Bearer $MAKEFAV_API_KEY" \
-F "[email protected]" \
-F "app_name=My App" \
-F "theme_color=#2f9e68" \
-F "radius=20" \
-o favicons.zip HTTP/1.1 200 OK
Content-Type: application/zip
Content-Disposition: attachment
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 99
X-RateLimit-Reset: 1767225600
<binary ZIP data> Приклад запиту – JSON з URL зображення, відповідь у форматі JSON
curl --fail-with-body -sS https://makefav.com/api/v1/generate \
-H "Authorization: Bearer $MAKEFAV_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"image_url": "https://example.com/logo.png",
"app_name": "My App",
"theme_color": "#2f9e68",
"radius": 20,
"groups": "favicon,apple,android",
"format": "json"
}' {
"id": "9f86d081884c7d659a2feaa0c55ad015",
"html": "<link rel=\"apple-touch-icon\" sizes=\"57x57\" href=\"/apple-icon-57x57.png\">\n…\n<meta name=\"theme-color\" content=\"#2f9e68\">",
"files": [
{"name": "favicon.ico", "mime": "image/x-icon", "size": 15086, "base64": "AAABAAMAEBAAAAEAIABoBAAANgAAACAg…"},
{"name": "favicon-16x16.png", "mime": "image/png", "size": 612, "base64": "iVBORw0KGgoAAAANSUhEUgAAABAAAAAQ…"},
{"name": "manifest.json", "mime": "application/json", "size": 1094, "base64": "ewogICAgIm5hbWUiOiAiTXkgQXBwIiwK…"}
]
}
Отримати дані акаунта
/meПовертає користувача, якому належить API-ключ, і поточний стан ліміту запитів. Корисно для перевірки ключа. Потребує автентифікації; без параметрів.
Приклад запиту
curl -sS https://makefav.com/api/v1/me \
-H "Authorization: Bearer $MAKEFAV_API_KEY" Приклад відповіді
{
"id": 1,
"email": "[email protected]",
"name": "Jane Doe",
"rate_limit": {"limit": 100, "remaining": 97, "reset": 1767225600}
}
Список розмірів іконок
/sizesReturns every icon file the generator can produce, with its pixel size and group. No authentication and no parameters. The group value matches the groups parameter of POST /generate.
Приклад запиту
curl -sS https://makefav.com/api/v1/sizes Приклад відповіді
{
"files": [
{"name": "favicon-16x16.png", "size": 16, "group": "favicon"},
{"name": "favicon-32x32.png", "size": 32, "group": "favicon"},
{"name": "apple-touch-icon.png", "size": 180, "group": "apple"},
{"name": "android-icon-192x192.png", "size": 192, "group": "android"},
{"name": "ms-icon-144x144.png", "size": 144, "group": "ms"}
]
}
Специфікація OpenAPI
/openapi.jsonПовертає опис цього API у форматі OpenAPI 3.1. Автентифікація не потрібна. Імпортуйте його в Postman, Insomnia або Swagger UI чи використайте для генерації типізованого клієнта для вашої мови програмування.
curl -sS https://makefav.com/api/v1/openapi.json -o makefav-openapi.json Помилки
Successful requests return HTTP 200. Every error returns a JSON body with a stable, machine-readable code and a human-readable message. Always branch on the code, not on the message text.
HTTP/1.1 422 Unprocessable Content
Content-Type: application/json
{
"error": {
"code": "invalid_image",
"message": "The uploaded file could not be decoded as an image."
}
} | HTTP-статус | Код помилки | Значення |
|---|---|---|
| 400 | bad_request | Запит сформовано некоректно, наприклад, зображення не надіслано або тіло JSON недійсне. |
| 401 | unauthorized | API-ключ відсутній або недійсний. |
| 403 | forbidden | Електронну адресу власника ключа не підтверджено, підписку на розсилку вимкнено або акаунт заблоковано. |
| 413 | file_too_large | Зображення більше за 10 МБ. |
| 415 | unsupported_type | Тип файлу або тип вмісту запиту не підтримується. |
| 422 | invalid_image | Не вдалося декодувати файл як зображення або завантажити зображення за URL. |
| 429 | rate_limited | Забагато запитів. Зачекайте до X-RateLimit-Reset. |
| 500 | server_error | Щось пішло не так на нашому боці. Спробуйте пізніше; якщо проблема не зникне, зверніться до підтримки. |
Вихідні файли
With all groups enabled, a package contains the files below. Files from disabled groups are left out.
| Файл | Розмір | Група |
|---|---|---|
favicon.ico | 16, 32, 48 (налаштовується) | завжди |
favicon-16x16.png | 16 × 16 | favicon |
favicon-32x32.png | 32 × 32 | favicon |
favicon-96x96.png | 96 × 96 | favicon |
apple-icon-57x57.png | 57 × 57 | apple |
apple-icon-60x60.png | 60 × 60 | apple |
apple-icon-72x72.png | 72 × 72 | apple |
apple-icon-76x76.png | 76 × 76 | apple |
apple-icon-114x114.png | 114 × 114 | apple |
apple-icon-120x120.png | 120 × 120 | apple |
apple-icon-144x144.png | 144 × 144 | apple |
apple-icon-152x152.png | 152 × 152 | apple |
apple-icon-180x180.png | 180 × 180 | apple |
apple-icon.png | 192 × 192 | apple |
apple-icon-precomposed.png | 192 × 192 | apple |
apple-touch-icon.png | 180 × 180 | apple |
android-icon-36x36.png | 36 × 36 | android |
android-icon-48x48.png | 48 × 48 | android |
android-icon-72x72.png | 72 × 72 | android |
android-icon-96x96.png | 96 × 96 | android |
android-icon-144x144.png | 144 × 144 | android |
android-icon-192x192.png | 192 × 192 | android |
android-icon-512x512.png | 512 × 512 | android |
web-app-manifest-192x192.png | 192 × 192 | android |
web-app-manifest-512x512.png | 512 × 512 | android |
maskable-icon-512x512.png | 512 × 512 | android |
ms-icon-70x70.png | 70 × 70 | ms |
ms-icon-144x144.png | 144 × 144 | ms |
ms-icon-150x150.png | 150 × 150 | ms |
ms-icon-310x310.png | 310 × 310 | ms |
Додаткові файли
| Файл | Включено | Опис |
|---|---|---|
favicon.svg | Лише для SVG без обрізання, масштабування, відступів, заокруглення кутів або фону | Очищена копія вашого SVG для браузерів, які підтримують векторні favicon. |
manifest.json, site.webmanifest | android | Маніфест веб-застосунку для Android і PWA, що встановлюються (дві однакові копії). |
browserconfig.xml | ms | Конфігурація плитки Windows із вашим кольором плитки. |
readme-makefav.txt | завжди | Інструкції зі встановлення з HTML-кодом для вашого <head> у кінці. |
Приклади коду
Each example uploads logo.png with a few options and saves favicons.zip, handling API errors. Set the environment variable MAKEFAV_API_KEY to your API key first.
Завантаження multipart:
# Multipart upload (recommended). --fail-with-body exits non-zero on HTTP errors
# and still saves the JSON error body, so you can inspect it.
curl --fail-with-body -sS https://makefav.com/api/v1/generate \
-H "Authorization: Bearer $MAKEFAV_API_KEY" \
-F "[email protected]" \
-F "app_name=My App" \
-F "theme_color=#2f9e68" \
-F "radius=20" \
-o favicons.zip \
|| { echo "Request failed:"; cat favicons.zip; rm -f favicons.zip; exit 1; } Тіло JSON із зображенням у base64 (потрібен jq):
# JSON body with a base64-encoded image (built with jq to avoid argument length limits)
base64 < logo.png | tr -d '\n' > logo.b64
jq -n --rawfile img logo.b64 \
'{image_base64: $img, app_name: "My App", theme_color: "#2f9e68", radius: 20}' \
| curl --fail-with-body -sS https://makefav.com/api/v1/generate \
-H "Authorization: Bearer $MAKEFAV_API_KEY" \
-H "Content-Type: application/json" \
--data-binary @- \
-o favicons.zip \
|| { echo "Request failed:"; cat favicons.zip; rm -f favicons.zip; exit 1; } <?php
// Requires the curl extension. Run: MAKEFAV_API_KEY=mf_... php favicons.php
$apiKey = getenv('MAKEFAV_API_KEY') ?: exit("Set MAKEFAV_API_KEY first\n");
$ch = curl_init('https://makefav.com/api/v1/generate');
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_TIMEOUT => 60,
CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . $apiKey],
CURLOPT_POSTFIELDS => [
'image' => new CURLFile('logo.png', 'image/png', 'logo.png'),
'app_name' => 'My App',
'theme_color' => '#2f9e68',
'radius' => '20',
],
]);
$body = curl_exec($ch);
if ($body === false) {
fwrite(STDERR, 'Request failed: ' . curl_error($ch) . PHP_EOL);
exit(1);
}
$status = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);
if ($status !== 200) {
$error = json_decode($body, true)['error'] ?? ['code' => 'http_' . $status, 'message' => substr($body, 0, 200)];
fwrite(STDERR, "Error {$status} {$error['code']}: {$error['message']}" . PHP_EOL);
exit(1);
}
file_put_contents('favicons.zip', $body);
echo 'Saved favicons.zip (' . strlen($body) . " bytes)\n"; # pip install requests · Run: MAKEFAV_API_KEY=mf_... python favicons.py
import os
import sys
import requests
API_KEY = os.environ["MAKEFAV_API_KEY"]
try:
with open("logo.png", "rb") as logo:
resp = requests.post(
"https://makefav.com/api/v1/generate",
headers={"Authorization": f"Bearer {API_KEY}"},
files={"image": ("logo.png", logo, "image/png")},
data={"app_name": "My App", "theme_color": "#2f9e68", "radius": 20},
timeout=60,
)
except requests.RequestException as exc:
sys.exit(f"Request failed: {exc}")
if resp.status_code != 200:
try:
err = resp.json()["error"]
sys.exit(f"Error {resp.status_code} {err['code']}: {err['message']}")
except (ValueError, KeyError, TypeError):
sys.exit(f"Error {resp.status_code}: {resp.text[:200]}")
with open("favicons.zip", "wb") as out:
out.write(resp.content)
remaining = resp.headers.get("X-RateLimit-Remaining")
print(f"Saved favicons.zip ({len(resp.content)} bytes), {remaining} requests left this hour") // favicons.mjs — Node.js 18+, no dependencies. Run: MAKEFAV_API_KEY=mf_... node favicons.mjs
import { readFile, writeFile } from 'node:fs/promises';
const form = new FormData();
form.append('image', new Blob([await readFile('logo.png')], { type: 'image/png' }), 'logo.png');
form.append('app_name', 'My App');
form.append('theme_color', '#2f9e68');
form.append('radius', '20');
const res = await fetch('https://makefav.com/api/v1/generate', {
method: 'POST',
headers: { Authorization: `Bearer ${process.env.MAKEFAV_API_KEY}` },
body: form,
});
if (!res.ok) {
const body = await res.json().catch(() => null);
console.error(`Error ${res.status} ${body?.error?.code ?? ''}: ${body?.error?.message ?? res.statusText}`);
process.exit(1);
}
await writeFile('favicons.zip', Buffer.from(await res.arrayBuffer()));
console.log(`Saved favicons.zip, ${res.headers.get('x-ratelimit-remaining')} requests left this hour`); <input type="file" id="logo" accept="image/*">
<button type="button" id="go">Generate favicons</button>
<script>
// Never put your API key in browser code. Point ENDPOINT at your own backend route,
// which forwards the multipart body to https://makefav.com/api/v1/generate and adds
// the "Authorization: Bearer mf_..." header on the server.
const ENDPOINT = '/my-backend/favicons';
document.getElementById('go').addEventListener('click', async () => {
const file = document.getElementById('logo').files[0];
if (!file) {
alert('Please choose an image first.');
return;
}
const form = new FormData();
form.append('image', file);
form.append('app_name', 'My App');
form.append('theme_color', '#2f9e68');
form.append('radius', '20');
try {
const res = await fetch(ENDPOINT, { method: 'POST', body: form });
if (!res.ok) {
const body = await res.json().catch(() => null);
throw new Error(body?.error?.message || `HTTP ${res.status}`);
}
const url = URL.createObjectURL(await res.blob());
const a = Object.assign(document.createElement('a'), { href: url, download: 'favicons.zip' });
document.body.append(a);
a.click();
a.remove();
setTimeout(() => URL.revokeObjectURL(url), 1000);
} catch (err) {
alert('Favicon generation failed: ' + err.message);
}
});
</script> // go run favicons.go (MAKEFAV_API_KEY must be set)
package main
import (
"bytes"
"encoding/json"
"fmt"
"io"
"mime/multipart"
"net/http"
"net/textproto"
"os"
"time"
)
func main() {
if err := run(); err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
}
func run() error {
var body bytes.Buffer
w := multipart.NewWriter(&body)
logo, err := os.ReadFile("logo.png")
if err != nil {
return err
}
h := make(textproto.MIMEHeader)
h.Set("Content-Disposition", `form-data; name="image"; filename="logo.png"`)
h.Set("Content-Type", "image/png")
part, err := w.CreatePart(h)
if err != nil {
return err
}
if _, err := part.Write(logo); err != nil {
return err
}
for k, v := range map[string]string{"app_name": "My App", "theme_color": "#2f9e68", "radius": "20"} {
if err := w.WriteField(k, v); err != nil {
return err
}
}
if err := w.Close(); err != nil {
return err
}
req, err := http.NewRequest(http.MethodPost, "https://makefav.com/api/v1/generate", &body)
if err != nil {
return err
}
req.Header.Set("Authorization", "Bearer "+os.Getenv("MAKEFAV_API_KEY"))
req.Header.Set("Content-Type", w.FormDataContentType())
client := &http.Client{Timeout: 60 * time.Second}
resp, err := client.Do(req)
if err != nil {
return err
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
var apiErr struct {
Error struct {
Code string `json:"code"`
Message string `json:"message"`
} `json:"error"`
}
_ = json.NewDecoder(resp.Body).Decode(&apiErr)
return fmt.Errorf("error %d %s: %s", resp.StatusCode, apiErr.Error.Code, apiErr.Error.Message)
}
zip, err := io.ReadAll(resp.Body)
if err != nil {
return err
}
if err := os.WriteFile("favicons.zip", zip, 0o644); err != nil {
return err
}
fmt.Printf("Saved favicons.zip (%d bytes)\n", len(zip))
return nil
} # ruby favicons.rb (MAKEFAV_API_KEY must be set) — standard library only
require "net/http"
require "json"
require "uri"
uri = URI("https://makefav.com/api/v1/generate")
req = Net::HTTP::Post.new(uri)
req["Authorization"] = "Bearer #{ENV.fetch("MAKEFAV_API_KEY")}"
res = File.open("logo.png", "rb") do |logo|
req.set_form(
[
["image", logo, { filename: "logo.png", content_type: "image/png" }],
["app_name", "My App"],
["theme_color", "#2f9e68"],
["radius", "20"]
],
"multipart/form-data"
)
Net::HTTP.start(uri.host, uri.port, use_ssl: true, read_timeout: 60) { |http| http.request(req) }
end
unless res.is_a?(Net::HTTPSuccess)
err = (JSON.parse(res.body)["error"] rescue nil) || { "code" => res.code, "message" => res.body.to_s[0, 200] }
abort "Error #{res.code} #{err["code"]}: #{err["message"]}"
end
File.binwrite("favicons.zip", res.body)
puts "Saved favicons.zip (#{res.body.bytesize} bytes)" // .NET 6+ console app (Program.cs, top-level statements, implicit usings enabled)
using System.Net.Http.Headers;
using System.Text.Json;
using var http = new HttpClient { Timeout = TimeSpan.FromSeconds(60) };
http.DefaultRequestHeaders.Authorization =
new AuthenticationHeaderValue("Bearer", Environment.GetEnvironmentVariable("MAKEFAV_API_KEY"));
using var form = new MultipartFormDataContent();
var image = new ByteArrayContent(await File.ReadAllBytesAsync("logo.png"));
image.Headers.ContentType = new MediaTypeHeaderValue("image/png");
form.Add(image, "image", "logo.png");
form.Add(new StringContent("My App"), "app_name");
form.Add(new StringContent("#2f9e68"), "theme_color");
form.Add(new StringContent("20"), "radius");
using var response = await http.PostAsync("https://makefav.com/api/v1/generate", form);
if (!response.IsSuccessStatusCode)
{
var body = await response.Content.ReadAsStringAsync();
string code = "", message = body;
try
{
var error = JsonDocument.Parse(body).RootElement.GetProperty("error");
code = error.GetProperty("code").GetString() ?? "";
message = error.GetProperty("message").GetString() ?? "";
}
catch (Exception e) when (e is JsonException or KeyNotFoundException or InvalidOperationException) { }
Console.Error.WriteLine($"Error {(int)response.StatusCode} {code}: {message}");
return 1;
}
await File.WriteAllBytesAsync("favicons.zip", await response.Content.ReadAsByteArrayAsync());
Console.WriteLine("Saved favicons.zip");
return 0; // Java 11+, no dependencies. Run: MAKEFAV_API_KEY=mf_... java MakeFav.java
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.time.Duration;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.UUID;
public class MakeFav {
public static void main(String[] args) throws IOException, InterruptedException {
String boundary = "----MakeFav" + UUID.randomUUID().toString().replace("-", "");
Map<String, String> fields = new LinkedHashMap<>();
fields.put("app_name", "My App");
fields.put("theme_color", "#2f9e68");
fields.put("radius", "20");
// Build the multipart/form-data body by hand
ByteArrayOutputStream body = new ByteArrayOutputStream();
for (Map.Entry<String, String> field : fields.entrySet()) {
body.write(("--" + boundary + "\r\n"
+ "Content-Disposition: form-data; name=\"" + field.getKey() + "\"\r\n\r\n"
+ field.getValue() + "\r\n").getBytes(StandardCharsets.UTF_8));
}
body.write(("--" + boundary + "\r\n"
+ "Content-Disposition: form-data; name=\"image\"; filename=\"logo.png\"\r\n"
+ "Content-Type: image/png\r\n\r\n").getBytes(StandardCharsets.UTF_8));
body.write(Files.readAllBytes(Path.of("logo.png")));
body.write(("\r\n--" + boundary + "--\r\n").getBytes(StandardCharsets.UTF_8));
HttpRequest request = HttpRequest.newBuilder(URI.create("https://makefav.com/api/v1/generate"))
.timeout(Duration.ofSeconds(60))
.header("Authorization", "Bearer " + System.getenv("MAKEFAV_API_KEY"))
.header("Content-Type", "multipart/form-data; boundary=" + boundary)
.POST(HttpRequest.BodyPublishers.ofByteArray(body.toByteArray()))
.build();
HttpResponse<byte[]> response = HttpClient.newHttpClient()
.send(request, HttpResponse.BodyHandlers.ofByteArray());
if (response.statusCode() != 200) {
// Error body is JSON: {"error":{"code":"...","message":"..."}}
System.err.println("Error " + response.statusCode() + ": "
+ new String(response.body(), StandardCharsets.UTF_8));
System.exit(1);
}
Files.write(Path.of("favicons.zip"), response.body());
System.out.println("Saved favicons.zip (" + response.body().length + " bytes)");
}
} Робочий процес GitHub Actions – формат JSON, файли декодуються через jq:
# .github/workflows/favicons.yml — regenerate favicons whenever the logo changes.
# Add your key as a repository secret named MAKEFAV_API_KEY.
name: Favicons
on:
push:
paths: ['branding/logo.png']
workflow_dispatch:
jobs:
favicons:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Generate favicon package (JSON format)
env:
MAKEFAV_API_KEY: ${{ secrets.MAKEFAV_API_KEY }}
run: |
set -euo pipefail
curl --fail-with-body -sS https://makefav.com/api/v1/generate \
-H "Authorization: Bearer $MAKEFAV_API_KEY" \
-F "image=@branding/logo.png" \
-F "app_name=My App" \
-F "theme_color=#2f9e68" \
-F "radius=20" \
-F "path=/icons/" \
-F "format=json" \
-o favicons.json || { cat favicons.json; exit 1; }
mkdir -p public/icons
# Decode every file from the JSON response (jq is preinstalled on GitHub runners)
jq -r '.files[] | "\(.name) \(.base64)"' favicons.json |
while read -r name data; do
printf '%s' "$data" | base64 -d > "public/icons/$(basename "$name")"
done
jq -r '.html' favicons.json > public/icons/head-snippet.html
ls -l public/icons
- uses: actions/upload-artifact@v4
with:
name: favicons
path: public/icons Звичайний shell-скрипт для будь-якого CI – формат ZIP:
#!/usr/bin/env bash
# scripts/favicons.sh — simpler variant for any CI or build step: fetch the ZIP and unpack it.
set -euo pipefail
: "${MAKEFAV_API_KEY:?Set MAKEFAV_API_KEY}"
curl --fail-with-body -sS https://makefav.com/api/v1/generate \
-H "Authorization: Bearer $MAKEFAV_API_KEY" \
-F "[email protected]" \
-F "app_name=My App" \
-F "theme_color=#2f9e68" \
-F "radius=20" \
-o favicons.zip || { cat favicons.zip; rm -f favicons.zip; exit 1; }
unzip -o -q favicons.zip -d public/
rm favicons.zip
echo "Favicons written to public/" Використання в HTML
Upload all files to the folder you set with path, then paste the snippet into the <head> of every page. It is included at the end of readme-makefav.txt in the package and, with format=json, returned in the html field. Example for the options used in the code examples above:
<head>
<meta charset="utf-8">
<title>My App</title>
<link rel="apple-touch-icon" sizes="57x57" href="/apple-icon-57x57.png">
<link rel="apple-touch-icon" sizes="60x60" href="/apple-icon-60x60.png">
<link rel="apple-touch-icon" sizes="72x72" href="/apple-icon-72x72.png">
<link rel="apple-touch-icon" sizes="76x76" href="/apple-icon-76x76.png">
<link rel="apple-touch-icon" sizes="114x114" href="/apple-icon-114x114.png">
<link rel="apple-touch-icon" sizes="120x120" href="/apple-icon-120x120.png">
<link rel="apple-touch-icon" sizes="144x144" href="/apple-icon-144x144.png">
<link rel="apple-touch-icon" sizes="152x152" href="/apple-icon-152x152.png">
<link rel="apple-touch-icon" sizes="180x180" href="/apple-icon-180x180.png">
<link rel="icon" type="image/png" sizes="192x192" href="/android-icon-192x192.png">
<link rel="icon" type="image/png" sizes="32x32" href="/favicon-32x32.png">
<link rel="icon" type="image/png" sizes="96x96" href="/favicon-96x96.png">
<link rel="icon" type="image/png" sizes="16x16" href="/favicon-16x16.png">
<link rel="icon" href="/favicon.ico" sizes="any">
<link rel="manifest" href="/manifest.json">
<meta name="msapplication-TileColor" content="#2f9e68">
<meta name="msapplication-TileImage" content="/ms-icon-144x144.png">
<meta name="msapplication-config" content="/browserconfig.xml">
<meta name="theme-color" content="#2f9e68">
</head> Після розгортання очистьте кеш браузера або відкрийте сайт у приватному вікні – браузери агресивно кешують favicon.
Журнал змін
v1
- Перший публічний реліз REST API MakeFav.
- Endpoints
POST /generate,GET /me,GET /sizesandGET /openapi.json. - Формати відповіді ZIP і JSON, вхідні зображення через multipart, base64 та URL.
- Особисті API-ключі зі 100 запитами на годину.
Версія 1 стабільна: ми лише додаємо необов’язкові параметри й поля. Зміни, що порушують сумісність, виходитимуть як нова версія із завчасним повідомленням.
Підтримка
Є запитання, повідомлення про помилки або потрібен вищий ліміт запитів? Ми з радістю допоможемо. Вкажіть у повідомленні час запиту та код помилки – але ніколи не API-ключ.