Font API for creative automation
Their font in every render.
Renders that look made for each brand.
A typeface of their own for each customer of your image, video, PDF or template product, from one API request. Nothing to host.
Set in Slate
100 fonts free, no card
Their colours,
generic letters.
Your customers' campaigns get their name, colours and photos, then come out in the same few fonts as everyone else's.
- The font menu
- Every account picks from the same list, so the brands on your platform end up looking alike.
- A custom typeface
- A design project with a type foundry, out of reach for almost every business you render for.
- Uploaded fonts
- Missing accents and figures that break templates, and licences that may not cover automated commercial output.
A typeface
that's theirs.
After the name and the colours, type is what a brand shows most, on everything it sends out.
- Recognised
- Every post, banner, video and PDF looks like them, not like the template.
- Theirs to use
- On the terms you give them, up to full commercial rights, with no royalties to us.
- Complete
- 271 characters in one hand: capitals, accents, figures and symbols, so a price or a name sets like the rest.
- No project
- No commission and no type design skill: a few choices on their account, and their font is made.
A premium tier,
built in.
Brand-matched output your customers see on every render, and a competitor can't copy by adding fonts to a list.
- 01
Output that looks made for them
Every story, banner, card and report you render comes out in the customer's own typeface. Competitors can add fonts to a menu; they can't add your customers' own.
- 02
A tier to charge for
Sell brand-matched type as a premium plan or an add-on, at the price you choose. Our plans count fonts made, not renders, so what a customer costs you doesn't grow with what you render for them.
- 03
Licensing, settled
The fonts you generate are yours to license to your customers, on your terms: full commercial rights, or narrower ones such as only in what you render for them. Set nothing and they get the full rights. Either way there are no royalties to us, no fee per render, campaign or client, and it holds after your licence ends. Every file carries the licence text, link, copyright and embedding setting you choose.
- 04
A reason to stay
Their font lives on their account, beside their templates and assets, and it's in everything you make for them, so the work they come back to already looks like them.
- 05
Nothing to build or host
No type designers, no font engineering, no GPU or model to run. Your server makes one HTTPS request, and the rest of your pipeline stays as it is.
One campaign,
twelve fonts.
The same SALE 40 template, rendered for 12 customers: one font each, carried through every story, post and banner.
12 customers, 12 fonts, 36 renders.
One request,
then render.
Your server sends a customer's numbers and gets an ordinary TTF back in the same response.
curl https://engine.typomat.app/api/v1/fonts \
-H "Authorization: Bearer $TYPOMAT_KEY" \
-H "Content-Type: application/json" \
-o brightside.ttf \
-d '{
"name": "Brightside",
"parameters": {
"weight": 185,
"width": 0.66,
"contrast": 0.1,
"xHeight": 590,
"curve": 0.85,
"aperture": 0.05
}
}'import { writeFile } from "node:fs/promises";
const response = await fetch("https://engine.typomat.app/api/v1/fonts", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.TYPOMAT_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
name: "Brightside",
parameters: {
weight: 185,
width: 0.66,
contrast: 0.1,
xHeight: 590,
curve: 0.85,
aperture: 0.05
}
}),
});
if (!response.ok) throw new Error((await response.json()).error.message);
await writeFile("brightside.ttf", Buffer.from(await response.arrayBuffer()));import os
import requests
response = requests.post(
"https://engine.typomat.app/api/v1/fonts",
headers={"Authorization": f"Bearer {os.environ['TYPOMAT_KEY']}"},
json={
"name": "Brightside",
"parameters": {
"weight": 185,
"width": 0.66,
"contrast": 0.1,
"xHeight": 590,
"curve": 0.85,
"aperture": 0.05
}
},
)
if not response.ok:
raise RuntimeError(response.json()["error"]["message"])
with open("brightside.ttf", "wb") as file:
file.write(response.content)<?php
$context = stream_context_create(["http" => [
"method" => "POST",
"header" => "Authorization: Bearer " . getenv("TYPOMAT_KEY") . "\r\nContent-Type: application/json\r\n",
"content" => json_encode(["name" => "Brightside", "parameters" => ["weight" => 185, "width" => 0.66, "contrast" => 0.1, "xHeight" => 590, "curve" => 0.85, "aperture" => 0.05]]),
"ignore_errors" => true,
]]);
$body = file_get_contents("https://engine.typomat.app/api/v1/fonts", false, $context);
if (!str_contains($http_response_header[0], " 200 ")) {
throw new RuntimeException(json_decode($body, true)["error"]["message"]);
}
file_put_contents("brightside.ttf", $body);require "json"
require "net/http"
uri = URI("https://engine.typomat.app/api/v1/fonts")
response = Net::HTTP.post(
uri,
{ name: "Brightside", parameters: { weight: 185, width: 0.66, contrast: 0.1, xHeight: 590, curve: 0.85, aperture: 0.05 } }.to_json,
"Authorization" => "Bearer #{ENV.fetch("TYPOMAT_KEY")}",
"Content-Type" => "application/json",
)
raise JSON.parse(response.body)["error"]["message"] unless response.is_a?(Net::HTTPSuccess)
File.binwrite("brightside.ttf", response.body)package main
import (
"io"
"net/http"
"os"
"strings"
)
func main() {
body := `{ "name": "Brightside", "parameters": { "weight": 185, "width": 0.66, "contrast": 0.1, "xHeight": 590, "curve": 0.85, "aperture": 0.05 } }`
request, err := http.NewRequest("POST", "https://engine.typomat.app/api/v1/fonts", strings.NewReader(body))
if err != nil {
panic(err)
}
request.Header.Set("Authorization", "Bearer "+os.Getenv("TYPOMAT_KEY"))
request.Header.Set("Content-Type", "application/json")
response, err := http.DefaultClient.Do(request)
if err != nil {
panic(err)
}
defer response.Body.Close()
data, err := io.ReadAll(response.Body)
if err != nil {
panic(err)
}
if response.StatusCode != http.StatusOK {
panic(string(data))
}
if err := os.WriteFile("brightside.ttf", data, 0o644); err != nil {
panic(err)
}
}import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.nio.file.Files;
import java.nio.file.Path;
public class Main {
public static void main(String[] args) throws Exception {
String body = """
{ "name": "Brightside", "parameters": { "weight": 185, "width": 0.66, "contrast": 0.1, "xHeight": 590, "curve": 0.85, "aperture": 0.05 } }
""";
HttpRequest request = HttpRequest.newBuilder(URI.create("https://engine.typomat.app/api/v1/fonts"))
.header("Authorization", "Bearer " + System.getenv("TYPOMAT_KEY"))
.header("Content-Type", "application/json")
.POST(HttpRequest.BodyPublishers.ofString(body))
.build();
HttpClient client = HttpClient.newBuilder().version(HttpClient.Version.HTTP_1_1).build();
HttpResponse<byte[]> response = client.send(request, HttpResponse.BodyHandlers.ofByteArray());
if (response.statusCode() != 200) throw new RuntimeException(new String(response.body()));
Files.write(Path.of("brightside.ttf"), response.body());
}
}using System.Net.Http.Headers;
using System.Net.Http.Json;
using var client = new HttpClient();
client.DefaultRequestHeaders.Authorization =
new AuthenticationHeaderValue("Bearer", Environment.GetEnvironmentVariable("TYPOMAT_KEY"));
var response = await client.PostAsJsonAsync("https://engine.typomat.app/api/v1/fonts", new { name = "Brightside", parameters = new { weight = 185, width = 0.66, contrast = 0.1, xHeight = 590, curve = 0.85, aperture = 0.05 } });
if (!response.IsSuccessStatusCode) throw new Exception(await response.Content.ReadAsStringAsync());
await File.WriteAllBytesAsync("brightside.ttf", await response.Content.ReadAsByteArrayAsync());import Foundation
var request = URLRequest(url: URL(string: "https://engine.typomat.app/api/v1/fonts")!)
request.httpMethod = "POST"
request.setValue("Bearer \(ProcessInfo.processInfo.environment["TYPOMAT_KEY"]!)", forHTTPHeaderField: "Authorization")
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
request.httpBody = Data(#"{ "name": "Brightside", "parameters": { "weight": 185, "width": 0.66, "contrast": 0.1, "xHeight": 590, "curve": 0.85, "aperture": 0.05 } }"#.utf8)
let (data, response) = try await URLSession.shared.data(for: request)
guard (response as? HTTPURLResponse)?.statusCode == 200 else {
fatalError(String(decoding: data, as: UTF8.self))
}
try data.write(to: URL(fileURLWithPath: "brightside.ttf"))- On their record
- A few numbers per customer, chosen by them or set for them from their brand.
- One request
- The font file is in the response, named for them, with no job queue and no polling.
- Every render
- Uses the file you kept, and costs nothing more.
Priced by fonts,
not renders.
Start free, then choose a plan by how many customers' fonts you make each month.
- Free trial
- Free: 100 fonts over 14 days, with no card.
- API Starter
- $99 a month for 1,000 fonts. For a feature you're testing with real users.
- API Growth
- $249 a month for 10,000 fonts. For a feature your customers rely on.
- API Scale
- $499 a month for 50,000 fonts. For rendering pipelines and batch work.
Good to know
Start with one customer.
Put one customer's font through your own renderer, on a free trial of 100 fonts with no card.