MakeFav API-documentatie
Genereer complete faviconpakketten vanuit je eigen code, buildscripts en CI-pipelines. Met één verzoek maak je van elke afbeelding een favicon.ico, Apple touch icons, Android- en PWA-iconen, een web manifest en kant-en-klare HTML.
Op deze pagina
Snelstart
De MakeFav API is een eenvoudige REST API via HTTPS + JSON. Alle endpoints staan onder één basis-URL:
https://makefav.com/api/v1 - Maak een gratis account aan
Meld je aan, bevestig je e-mailadres en houd je abonnement op de MakeFav-nieuwsbrief actief – dat is de enige voorwaarde van het gratis abonnement. - Maak een API-sleutel aan
Open de pagina API-sleutels in je account en maak een persoonlijke sleutel aan. Deze begint met mf_ en wordt maar één keer getoond, dus bewaar hem veilig. API-sleutels beheren - Stuur je eerste verzoek
Upload een afbeelding naar het generate-endpoint en sla het geretourneerde ZIP-bestand op:
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.
Authenticatie
Elk verzoek aan een beveiligd endpoint moet een persoonlijke API-sleutel bevatten. Je kunt sleutels op elk moment aanmaken, een naam geven en intrekken op je accountpagina: API-sleutels
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.
Limieten
Standaard mag elke API-sleutel 100 verzoeken per uur doen. Elk geauthenticeerd antwoord laat zien waar je staat:
| Header | Beschrijving |
|---|---|
X-RateLimit-Limit | Maximaal aantal verzoeken dat in het huidige tijdvenster is toegestaan. |
X-RateLimit-Remaining | Resterende verzoeken in het huidige tijdvenster. |
X-RateLimit-Reset | Unix-timestamp (seconden, UTC) waarop het tijdvenster wordt gereset. |
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.
Endpoints
| Endpoint | Authenticatie | Beschrijving |
|---|---|---|
POST /generate | API-sleutel | Maak een compleet faviconpakket. |
GET /me | API-sleutel | Je account en de huidige status van je limiet. |
GET /sizes | Geen | Lijst van alle iconbestanden en formaten die de generator maakt. |
GET /openapi.json | Geen | Machineleesbare OpenAPI 3.1-specificatie. |
Een faviconpakket genereren
/generateMaakt een compleet faviconpakket van één bronafbeelding. Stuur de afbeelding op een van de drie manieren:
| Content-type | Afbeeldingsveld | Beschrijving |
|---|---|---|
multipart/form-data | image | Bestandsupload. Aanbevolen – de meest efficiënte optie. |
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 | Openbare http- of https-URL van de afbeelding. De server downloadt deze (max. 10 MB). |
Ondersteunde invoerformaten: PNG, JPG, WebP, AVIF, GIF, BMP, SVG en ICO. Maximale bestandsgrootte: 10 MB.
Parameters
Alle parameters zijn optioneel. Stuur ze als formuliervelden (multipart) of als JSON-eigenschappen. Ongeldige waarden vallen terug op de standaardwaarde.
| Naam | Type / bereik | Standaard | Beschrijving |
|---|---|---|---|
app_name | string, max. 60 tekens | My Website | Naam van je website of app, gebruikt in manifest.json en de voorbeeldpagina. |
short_name | string, max. 24 tekens | eerste 12 tekens van app_name | Korte naam die onder het icoon op Android-startschermen wordt getoond. |
theme_color | #rrggbb | #ffffff | Kleur van de browserinterface (theme-color meta tag en manifest). |
tile_color | #rrggbb | hetzelfde als theme_color | Achtergrondkleur van de tegels in het Windows-startmenu. |
background | transparent | #rrggbb | transparent | Achtergrond van de browser-, Android- en Windows-iconen. |
apple_background | #rrggbb | #ffffff | Achtergrond van Apple touch icons en het maskable icon (iOS ondersteunt geen transparantie). Wordt genegeerd als background een kleur is – in dat geval wordt die kleur gebruikt. |
padding | geheel getal, 0–40 (%) | 0 | Lege ruimte rond de afbeelding, als percentage van het iconformaat. |
radius | geheel getal, 0–50 (%) | 0 | Afgeronde hoeken. 50 maakt een cirkel. Wordt niet toegepast op Apple-iconen, die iOS zelf afrondt. |
zoom | geheel getal, 50–400 (%) | 100 | Schaal van de afbeelding binnen het icoon. Waarden boven 100 snijden de afbeelding bij. |
offset_x, offset_y | getal, -1 tot 1 | 0 | Verschuift de afbeelding horizontaal / verticaal, als fractie van het iconformaat. |
path | string | / | Map op je server waar de iconen komen te staan. Wordt gebruikt in het HTML-fragment, het manifest en browserconfig. |
display | standalone | fullscreen | minimal-ui | browser | standalone | PWA-weergavemodus die in het manifest wordt geschreven. |
pixelated | auto | 1 | 0 | auto | Scherpe nearest-neighbour-schaling voor pixelart. Met auto wordt dit ingeschakeld voor bronnen van 64 px of kleiner. |
maskable | 1 | 0 | 1 | Maak ook maskable-icon-512x512.png met een veilige marge voor adaptieve Android-iconen. |
ico_sizes | kommagescheiden lijst van 16, 24, 32, 48, 64, 128, 256 | 16,32,48 | Formaten die in favicon.ico zijn opgenomen. |
groups | kommagescheiden lijst van favicon, apple, android, ms | favicon,apple,android,ms | Te genereren iconensets: browserfavicons, Apple touch-iconen, Android-/PWA-iconen en Windows-tegels. |
format | zip | json | zip | Antwoordformaat – zie hieronder. |
Antwoord
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.
Voorbeeldverzoek – multipart, ZIP-antwoord
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> Voorbeeldverzoek – JSON met afbeeldings-URL, JSON-antwoord
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…"}
]
}
Je account ophalen
/meGeeft de gebruiker terug aan wie de API-sleutel toebehoort, plus de huidige status van de rate limit. Handig om een sleutel te controleren. Vereist authenticatie; geen parameters.
Voorbeeldverzoek
curl -sS https://makefav.com/api/v1/me \
-H "Authorization: Bearer $MAKEFAV_API_KEY" Voorbeeldantwoord
{
"id": 1,
"email": "[email protected]",
"name": "Jane Doe",
"rate_limit": {"limit": 100, "remaining": 97, "reset": 1767225600}
}
Iconformaten weergeven
/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.
Voorbeeldverzoek
curl -sS https://makefav.com/api/v1/sizes Voorbeeldantwoord
{
"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-specificatie
/openapi.jsonGeeft de OpenAPI 3.1-beschrijving van deze API terug. Geen authenticatie nodig. Importeer hem in Postman, Insomnia of Swagger UI, of gebruik hem om een getypeerde client voor jouw taal te genereren.
curl -sS https://makefav.com/api/v1/openapi.json -o makefav-openapi.json Fouten
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-status | Foutcode | Betekenis |
|---|---|---|
| 400 | bad_request | Het verzoek is onjuist opgebouwd, bijvoorbeeld omdat er geen afbeelding is verzonden of de JSON-body ongeldig is. |
| 401 | unauthorized | De API-sleutel ontbreekt of is ongeldig. |
| 403 | forbidden | Het e-mailadres van de sleuteleigenaar is niet bevestigd, het nieuwsbriefabonnement is uitgeschakeld of het account is geblokkeerd. |
| 413 | file_too_large | De afbeelding is groter dan 10 MB. |
| 415 | unsupported_type | Het bestandstype of het content-type van het verzoek wordt niet ondersteund. |
| 422 | invalid_image | Het bestand kon niet als afbeelding worden gedecodeerd, of de afbeeldings-URL kon niet worden gedownload. |
| 429 | rate_limited | Te veel verzoeken. Wacht tot X-RateLimit-Reset. |
| 500 | server_error | Er is iets misgegaan aan onze kant. Probeer het later opnieuw en neem contact op met support als het probleem aanhoudt. |
Uitvoerbestanden
With all groups enabled, a package contains the files below. Files from disabled groups are left out.
| Bestand | Formaat | Groep |
|---|---|---|
favicon.ico | 16, 32, 48 (configureerbaar) | altijd |
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 |
Extra bestanden
| Bestand | Inbegrepen | Beschrijving |
|---|---|---|
favicon.svg | Alleen voor SVG-invoer zonder bijsnijden, zoom, padding, hoeken of achtergrond | Opgeschoonde kopie van je SVG voor browsers die vectorfavicons ondersteunen. |
manifest.json, site.webmanifest | android | Webapp-manifest voor Android en installeerbare PWA's (twee identieke kopieën). |
browserconfig.xml | ms | Windows-tegelconfiguratie met jouw tegelkleur. |
readme-makefav.txt | altijd | Installatie-instructies met de HTML-code voor je <head> aan het einde. |
Codevoorbeelden
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-upload:
# 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-body met een base64-gecodeerde afbeelding (vereist 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-workflow – JSON-formaat, bestanden gedecodeerd met 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 Eenvoudig shellscript voor elke CI – ZIP-formaat:
#!/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-gebruik
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> Wis na het uitrollen je browsercache of open de site in een privévenster – browsers cachen favicons erg hardnekkig.
Changelog
v1
- Eerste openbare release van de MakeFav REST API.
- Endpoints
POST /generate,GET /me,GET /sizesandGET /openapi.json. - ZIP- en JSON-antwoordformaten, multipart-, base64- en URL-afbeeldingsinvoer.
- Persoonlijke API-sleutels met 100 verzoeken per uur.
Versie 1 is stabiel: we voegen alleen optionele parameters en velden toe. Ingrijpende wijzigingen verschijnen als een nieuwe versie met voorafgaande kennisgeving.
Support
Vragen, bugmeldingen of een hogere rate limit nodig? We helpen je graag. Vermeld de tijd van het verzoek en de foutcode in je bericht – nooit je API-sleutel.