Suggest a font pairing by style
Choosing typefaces is easier when you separate visual roles. Tell this font pairing suggester whether your heading face is serif, sans-serif, slab, or display, then choose the mood you want the page to convey.
Run — free
It returns a widely available body font, its classification, two alternatives, and a short explanation of why the contrast works. The result is a considered starting point for a real design system, not a claim that any two fonts will work without testing actual weights, sizes, and content.
Start with the heading's visual role
A heading font is selected to establish identity, hierarchy, and pace, while a body font must remain comfortable across sentences, paragraphs, captions, and interface labels. Begin by classifying the heading according to its dominant construction: serif, sans-serif, slab, or display. Serif faces have finishing strokes and often bring literary or historical associations. Sans-serif faces remove those strokes and can feel direct, neutral, or contemporary. Slab faces use substantial, block-like serifs that create weight and confidence. Display faces are the broadest category: decorative, highly contrasted, condensed, unusual, or otherwise intended mainly for large sizes. Classification cannot capture every detail, but it provides a useful first constraint. The suggester then chooses a body face whose texture either contrasts with the heading or creates a controlled family resemblance. If your heading sits between categories, select the one that best describes what viewers notice first, then compare the returned recommendation with the result from the neighboring category.
Use mood to shape the kind of contrast
Contrast is not simply a rule that says serif must always pair with sans-serif. Useful contrast can come from construction, proportions, stroke modulation, width, weight, or personality. Mood determines which of those differences should become prominent. An elegant direction may combine restrained headings with a graceful text serif, while a minimal direction usually gives the body a quiet, neutral texture. A playful result can use rounder shapes or livelier details, and a technical result benefits from clear character distinctions and engineered proportions. The algorithm uses a fixed guidance table, so identical inputs always return identical recommendations. That predictability is helpful when documenting a design decision or generating repeatable suggestions in a workflow. Treat the primary font as the clearest match for the selected category and mood, and treat the alternatives as nearby directions. They are not ranked by popularity, and the tool does not download, inspect, license, or measure any font files.
Test the pairing in the real layout
A recommendation becomes a working pairing only after you test the actual fonts with real content. Set several heading levels, a long paragraph, short labels, numbers, punctuation, links, and emphasized text. Check that the heading remains distinctive without making the body look weak or unrelated. For body copy, start with a regular or medium weight and adjust size, line height, and line length before rejecting the family; spacing often matters as much as the font choice. Confirm that bold and italic styles exist for the languages and scripts you need, and review licensing terms for each project. Also test small screens, low contrast displays, and browser fallback behavior. If both faces demand attention, simplify the body choice. If they look accidentally similar, strengthen hierarchy through scale, weight, or a more deliberate structural contrast. The returned rationale explains the intended relationship, giving you a concrete hypothesis to validate rather than an arbitrary pair of names.
What you can do with it
Create a brand direction
Turn an early mood choice into a concrete heading-and-body typography direction for a mood board.
Unblock a web layout
Get a readable body companion when a distinctive heading face has already been selected.
Document design-system choices
Record a repeatable recommendation and rationale before testing weights, spacing, and responsive sizes.
FAQ
Does the tool inspect my heading font?
No. It uses the classification and mood you provide; it does not receive or inspect a font file.
Are the recommendations deterministic?
Yes. The same normalized classification and mood always produce the same recommendation and alternatives.
Can two fonts from the same classification work together?
Yes. A pairing can create hierarchy through weight, size, width, and texture instead of changing classification.
Does a recommendation guarantee language coverage?
No. Check the actual font files for every language, script, weight, and style required by your project.
What happens with an unknown classification?
The request returns an invalid input error. Supported heading classifications are serif, sans-serif, slab, and display.
What does the API request cost?
Each API request costs $0.002. The browser version can run the same deterministic logic locally.
For developers — API access
Everything on this page is available programmatically. This section is for teams who want to wire it into their own systems; everyone else can just use the tool above.
API endpoint
Prefer to automate it? One authenticated POST creates the task; the result comes back by webhook or a signed link. The same capability also runs here on the web, by email and from Telegram — and soon from our app too.
Call it from your stack
curl -X POST https://api.kit.forhosting.com/doc/font-pairing-suggest \
-H "Authorization: Bearer $KIT_KEY" \
-H "Content-Type: application/json" \
-d '{"classification":"serif","mood":"elegant"}'const res = await fetch("https://api.kit.forhosting.com/doc/font-pairing-suggest", {
method: "POST",
headers: {
"Authorization": `Bearer ${process.env.KIT_KEY}`,
"Content-Type": "application/json"
},
body: JSON.stringify({
"classification": "serif",
"mood": "elegant"
})
});
const { task_id } = await res.json();import os, requests
res = requests.post(
"https://api.kit.forhosting.com/doc/font-pairing-suggest",
headers={"Authorization": f"Bearer {os.environ['KIT_KEY']}"},
json={
"classification": "serif",
"mood": "elegant"
},
)
task_id = res.json()["task_id"]<?php
$res = file_get_contents("https://api.kit.forhosting.com/doc/font-pairing-suggest", false, stream_context_create([
"http" => [
"method" => "POST",
"header" => "Authorization: Bearer " . getenv("KIT_KEY") . "\r\nContent-Type: application/json",
"content" => '{"classification":"serif","mood":"elegant"}',
],
]));
$task = json_decode($res, true);body := bytes.NewBufferString(`{"classification":"serif","mood":"elegant"}`)
req, _ := http.NewRequest("POST", "https://api.kit.forhosting.com/doc/font-pairing-suggest", body)
req.Header.Set("Authorization", "Bearer "+os.Getenv("KIT_KEY"))
req.Header.Set("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)Example request
{
"classification": "serif",
"mood": "elegant"
}Example response
{
"task_id": "tsk_a1b2c3d4e5f6a1b2c3d4e5f6",
"type": "doc.font_pairing_suggest",
"status": "queued",
"_links": {
"result": "/tasks/tsk_…/result"
}
}The API is asynchronous: the call returns a task_id immediately and the result arrives by webhook. Polling is capped at 1 req/s per task.
Pricing
Published price — no tokens, no invented credits. A failed task is never charged.
Limits
max_mb | 25 |
max_pages | 200 |
Errors
| HTTP | Code | Meaning |
|---|---|---|
401 | unauthorized | Missing or invalid API key. |
402 | insufficient_balance | Your balance doesn't cover the task price. |
404 | unknown_type | That task type doesn't exist. |
429 | rate_limited | Too many requests. Use the webhook instead of polling. |