Tài liệu API của MakeFav
Tạo gói favicon hoàn chỉnh từ mã của bạn, script build và pipeline CI. Chỉ một yêu cầu là biến mọi hình ảnh thành favicon.ico, Apple touch icon, biểu tượng Android và PWA, web manifest cùng mã HTML sẵn sàng để dán.
Trong trang này
Bắt đầu nhanh
MakeFav API là một REST API đơn giản dùng HTTPS + JSON. Tất cả endpoint đều nằm dưới một base URL:
https://makefav.com/api/v1 - Tạo tài khoản miễn phí
Đăng ký, xác nhận địa chỉ email và giữ đăng ký nhận bản tin MakeFav luôn hoạt động – đó là điều kiện duy nhất của gói miễn phí. - Tạo API key
Mở trang API key trong tài khoản của bạn và tạo một key cá nhân. Key bắt đầu bằng mf_ và chỉ hiển thị một lần, vì vậy hãy lưu trữ an toàn. Quản lý API key - Gửi yêu cầu đầu tiên
Tải ảnh lên endpoint generate và lưu tệp ZIP được trả về:
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.
Xác thực
Mọi yêu cầu tới endpoint được bảo vệ đều phải kèm API key cá nhân. Bạn có thể tạo, đặt tên và thu hồi key bất cứ lúc nào trên trang tài khoản: API key
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.
Giới hạn tốc độ
Theo mặc định, mỗi API key được thực hiện 100 yêu cầu mỗi giờ. Mọi phản hồi đã xác thực đều cho bạn biết mức sử dụng hiện tại:
| Header | Mô tả |
|---|---|
X-RateLimit-Limit | Số yêu cầu tối đa được phép trong khoảng thời gian hiện tại. |
X-RateLimit-Remaining | Số yêu cầu còn lại trong khoảng thời gian hiện tại. |
X-RateLimit-Reset | Unix timestamp (giây, UTC) khi khoảng thời gian được đặt lại. |
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.
Endpoint
| Endpoint | Xác thực | Mô tả |
|---|---|---|
POST /generate | API key | Tạo một gói favicon hoàn chỉnh. |
GET /me | API key | Tài khoản của bạn và trạng thái giới hạn tốc độ hiện tại. |
GET /sizes | Không có | Danh sách tất cả các tệp biểu tượng và kích thước mà trình tạo xuất ra. |
GET /openapi.json | Không có | Đặc tả OpenAPI 3.1 có thể đọc bằng máy. |
Tạo gói favicon
/generateTạo gói favicon hoàn chỉnh từ một ảnh nguồn. Gửi ảnh theo một trong ba cách:
| Content type | Trường ảnh | Mô tả |
|---|---|---|
multipart/form-data | image | Tải tệp lên. Khuyến nghị – cách hiệu quả nhất. |
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 | URL http hoặc https công khai của ảnh. Máy chủ sẽ tải ảnh về (tối đa 10 MB). |
Định dạng đầu vào được hỗ trợ: PNG, JPG, WebP, AVIF, GIF, BMP, SVG và ICO. Dung lượng tệp tối đa: 10 MB.
Tham số
Tất cả tham số đều là tùy chọn. Gửi chúng dưới dạng trường biểu mẫu (multipart) hoặc thuộc tính JSON. Giá trị không hợp lệ sẽ dùng giá trị mặc định.
| Tên | Kiểu / phạm vi | Mặc định | Mô tả |
|---|---|---|---|
app_name | chuỗi, tối đa 60 ký tự | My Website | Tên website hoặc ứng dụng của bạn, dùng trong manifest.json và trang ví dụ. |
short_name | chuỗi, tối đa 24 ký tự | 12 ký tự đầu của app_name | Tên ngắn hiển thị bên dưới biểu tượng trên màn hình chính Android. |
theme_color | #rrggbb | #ffffff | Màu giao diện trình duyệt (thẻ meta theme-color và manifest). |
tile_color | #rrggbb | giống theme_color | Màu nền của ô Start trên Windows. |
background | transparent | #rrggbb | transparent | Nền của các biểu tượng trình duyệt, Android và Windows. |
apple_background | #rrggbb | #ffffff | Nền của biểu tượng Apple touch và biểu tượng maskable (iOS không hỗ trợ độ trong suốt). Bị bỏ qua khi background là một màu – khi đó sẽ dùng màu đó. |
padding | số nguyên, 0–40 (%) | 0 | Khoảng trống xung quanh ảnh, tính theo phần trăm kích thước biểu tượng. |
radius | số nguyên, 0–50 (%) | 0 | Bo tròn góc. 50 tạo thành hình tròn. Không áp dụng cho biểu tượng Apple vì iOS tự bo góc. |
zoom | số nguyên, 50–400 (%) | 100 | Tỷ lệ của ảnh bên trong biểu tượng. Giá trị trên 100 sẽ cắt ảnh. |
offset_x, offset_y | số, từ -1 đến 1 | 0 | Dịch chuyển ảnh theo chiều ngang / chiều dọc, tính theo tỷ lệ kích thước biểu tượng. |
path | chuỗi | / | Thư mục trên máy chủ của bạn nơi chứa các biểu tượng. Được dùng trong đoạn mã HTML, manifest và browserconfig. |
display | standalone | fullscreen | minimal-ui | browser | standalone | Chế độ hiển thị PWA được ghi vào manifest. |
pixelated | auto | 1 | 0 | auto | Phóng to theo kiểu nearest-neighbour sắc nét cho pixel art. auto sẽ bật chế độ này với ảnh nguồn từ 64 px trở xuống. |
maskable | 1 | 0 | 1 | Đồng thời tạo maskable-icon-512x512.png với vùng đệm an toàn cho biểu tượng thích ứng của Android. |
ico_sizes | danh sách phân tách bằng dấu phẩy gồm 16, 24, 32, 48, 64, 128, 256 | 16,32,48 | Các kích thước được nhúng trong favicon.ico. |
groups | danh sách phân tách bằng dấu phẩy gồm favicon, apple, android, ms | favicon,apple,android,ms | Các bộ biểu tượng cần tạo: favicon trình duyệt, biểu tượng Apple touch, biểu tượng Android / PWA và ô Windows. |
format | zip | json | zip | Định dạng phản hồi – xem bên dưới. |
Phản hồi
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.
Ví dụ yêu cầu – multipart, phản hồi 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> Ví dụ yêu cầu – JSON với URL ảnh, phản hồi 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…"}
]
}
Lấy thông tin tài khoản
/meTrả về người dùng sở hữu khóa API và trạng thái giới hạn tần suất hiện tại. Hữu ích để kiểm tra khóa. Yêu cầu xác thực; không có tham số.
Ví dụ yêu cầu
curl -sS https://makefav.com/api/v1/me \
-H "Authorization: Bearer $MAKEFAV_API_KEY" Ví dụ phản hồi
{
"id": 1,
"email": "[email protected]",
"name": "Jane Doe",
"rate_limit": {"limit": 100, "remaining": 97, "reset": 1767225600}
}
Liệt kê kích thước biểu tượng
/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.
Ví dụ yêu cầu
curl -sS https://makefav.com/api/v1/sizes Ví dụ phản hồi
{
"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"}
]
}
Đặc tả OpenAPI
/openapi.jsonTrả về mô tả OpenAPI 3.1 của API này. Không cần xác thực. Nhập vào Postman, Insomnia hoặc Swagger UI, hoặc dùng để tạo client có kiểu dữ liệu cho ngôn ngữ của bạn.
curl -sS https://makefav.com/api/v1/openapi.json -o makefav-openapi.json Lỗi
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."
}
} | Trạng thái HTTP | Mã lỗi | Ý nghĩa |
|---|---|---|
| 400 | bad_request | Yêu cầu không đúng định dạng, ví dụ chưa gửi ảnh hoặc nội dung JSON không hợp lệ. |
| 401 | unauthorized | Khóa API bị thiếu hoặc không hợp lệ. |
| 403 | forbidden | Email của chủ sở hữu khóa chưa được xác nhận, đăng ký nhận bản tin bị tắt hoặc tài khoản bị khóa. |
| 413 | file_too_large | Ảnh lớn hơn 10 MB. |
| 415 | unsupported_type | Loại tệp hoặc content type của yêu cầu không được hỗ trợ. |
| 422 | invalid_image | Không thể giải mã tệp thành ảnh hoặc không thể tải ảnh từ URL. |
| 429 | rate_limited | Quá nhiều yêu cầu. Hãy chờ đến X-RateLimit-Reset. |
| 500 | server_error | Đã xảy ra lỗi ở phía chúng tôi. Hãy thử lại sau; liên hệ hỗ trợ nếu lỗi vẫn tiếp diễn. |
Tệp đầu ra
With all groups enabled, a package contains the files below. Files from disabled groups are left out.
| Tệp | Kích thước | Nhóm |
|---|---|---|
favicon.ico | 16, 32, 48 (có thể tùy chỉnh) | luôn có |
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 |
Tệp bổ sung
| Tệp | Bao gồm | Mô tả |
|---|---|---|
favicon.svg | Chỉ với đầu vào SVG không cắt, thu phóng, đệm, bo góc hoặc nền | Bản sao SVG đã được làm sạch cho các trình duyệt hỗ trợ favicon vector. |
manifest.json, site.webmanifest | android | Web app manifest cho Android và PWA có thể cài đặt (hai bản sao giống hệt nhau). |
browserconfig.xml | ms | Cấu hình ô Windows với màu ô của bạn. |
readme-makefav.txt | luôn có | Hướng dẫn cài đặt kèm mã HTML cho <head> của bạn ở cuối. |
Ví dụ mã
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.
Tải lên 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; } Nội dung JSON với ảnh mã hóa base64 (yêu cầu 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)");
}
} Workflow GitHub Actions – định dạng JSON, tệp được giải mã bằng 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 script thuần cho mọi CI – định dạng 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/" Cách dùng trong 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> Sau khi triển khai, hãy xóa bộ nhớ đệm trình duyệt hoặc mở website trong cửa sổ riêng tư – trình duyệt lưu đệm favicon rất kỹ.
Nhật ký thay đổi
v1
- Bản phát hành công khai đầu tiên của MakeFav REST API.
- Endpoints
POST /generate,GET /me,GET /sizesandGET /openapi.json. - Định dạng phản hồi ZIP và JSON, đầu vào ảnh qua multipart, base64 và URL.
- Khóa API cá nhân với 100 yêu cầu mỗi giờ.
Phiên bản 1 ổn định: chúng tôi chỉ thêm các tham số và trường tùy chọn. Các thay đổi không tương thích sẽ được phát hành dưới dạng phiên bản mới và có thông báo trước.
Hỗ trợ
Có câu hỏi, báo lỗi hoặc cần giới hạn tần suất cao hơn? Chúng tôi sẵn sàng hỗ trợ. Vui lòng ghi rõ thời gian yêu cầu và mã lỗi trong tin nhắn – tuyệt đối không gửi khóa API.