Format numbers & currency
1.234,56 and 1,234.56 are the same number wearing two different locale conventions, and shipping the wrong one makes a price look either ten times bigger or fundamentally wrong to the reader. The Number & Currency Formatting API takes a raw numeric value and a target locale and returns it formatted the way that locale actually expects — correct thousands separator, decimal mark, currency symbol placement and rounding. It's a small, unglamorous problem that breaks checkout pages and financial reports constantly.
Run — free
Runs in your browser. Free, unlimited — your data never leaves this page.
The bug that hides in plain sight
Number formatting is one of those details developers get right for their own locale and wrong for everyone else's, because it's rarely tested outside the home market. A price of 1000.5 shown as '1000.5' to a German reader, or as '1.000,5' to an American one, both look like typos rather than translation gaps — which makes the bug harder to catch in QA and more damaging when a customer notices it on a checkout page.
What the endpoint takes and returns
Send a numeric value, an optional currency code, and the target locale (like de-DE, en-US or ja-JP), and get back the correctly formatted string — separators, symbol position, decimal precision and any locale-specific rounding rules applied. You're not asking us to guess a currency or convert an exchange rate; this is formatting, not conversion, and it stays predictable because of it.
Why this isn't as simple as a lookup table
Locale formatting conventions vary in ways that aren't always intuitive: Switzerland uses an apostrophe as a thousands separator in some contexts, Arabic numerals get replaced with Eastern Arabic numerals in certain locales, and currency symbol placement (before or after the number, with or without a space) differs even between closely related locales like en-US and en-GB. Getting all of that right by hand across dozens of markets is exactly the kind of repetitive precision work suited to an API rather than a spreadsheet of rules someone maintains manually.
Where it plugs into automation
Submit the request asynchronously, get a task_id back, and receive the formatted value via signed webhook or a signed link valid for 24 hours. Because pricing is per request rather than per word, this endpoint is built for high-volume, low-latency-tolerant use — batch formatting a product catalog or a financial export rather than a single interactive lookup.
Where it typically gets used
Checkout flows, invoicing systems, financial dashboards and any multi-market product catalog rely on consistent number formatting to look trustworthy — a storefront that displays prices wrong for a given country reads as untrustworthy or foreign well before a customer reads a single word of copy.
What you can do with it
Multi-country checkout pages
An e-commerce platform shows the same price value formatted correctly whether the buyer is in Germany, the US or Japan, without maintaining separate formatting logic per storefront.
Financial reporting exports
A finance team generates the same underlying report for offices in different countries, each with numbers formatted to local convention instead of a single hardcoded format.
Invoice generation at scale
An invoicing system formats totals, taxes and line items per the customer's locale automatically, avoiding manual per-market templates.
Product catalogs with regional pricing
A catalog feed re-formats the same base prices for each storefront locale as part of a nightly batch job.
FAQ
What exactly does this API format?
It takes a raw number, an optional currency code and a target locale, and returns the value formatted with the correct separators, decimal precision, symbol placement and rounding for that locale.
Does it also convert currencies?
No, it formats a given numeric value for a locale; it doesn't perform exchange-rate conversion, so pricing stays predictable and results stay accurate to the number you provided.
Is there a free tier?
The tool above runs free in your browser. The API is paid — each call draws from your prepaid ForHosting KIT balance: top up from $10.00 (it never expires), pay each request's published price, and a call with no balance returns HTTP 402. No subscription, no tokens, and a failed task is never charged.
How much does a request cost?
$0.002 per request, flat, with no per-word component since there's no text to process — and you're only billed for tasks that complete successfully.
Which locales are supported?
Standard locale codes such as en-US, es-MX, de-DE, fr-FR and ja-JP are supported; specify the target locale in the request and the formatting rules for that region are applied.
Can I format large batches at once?
Yes, submit requests asynchronously and collect results via webhook, which suits batch jobs like catalog or invoice runs well.
What happens if a request fails?
It's retried automatically up to three times; a persistent failure returns a clear error and is never charged.
How fast is the response?
Because the task is lightweight, results typically return quickly, and delivery is via signed webhook or a signed link valid for 24 hours.
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, and soon from our app, email and Telegram.
Call it from your stack
curl -X POST https://api.kit.forhosting.com/locale/format-number \
-H "Authorization: Bearer $KIT_KEY" \
-H "Content-Type: application/json" \
-d '{"items":["valor-1","valor-2"]}'const res = await fetch("https://api.kit.forhosting.com/locale/format-number", {
method: "POST",
headers: {
"Authorization": `Bearer ${process.env.KIT_KEY}`,
"Content-Type": "application/json"
},
body: JSON.stringify({
"items": [
"valor-1",
"valor-2"
]
})
});
const { task_id } = await res.json();import os, requests
res = requests.post(
"https://api.kit.forhosting.com/locale/format-number",
headers={"Authorization": f"Bearer {os.environ['KIT_KEY']}"},
json={
"items": [
"valor-1",
"valor-2"
]
},
)
task_id = res.json()["task_id"]<?php
$res = file_get_contents("https://api.kit.forhosting.com/locale/format-number", false, stream_context_create([
"http" => [
"method" => "POST",
"header" => "Authorization: Bearer " . getenv("KIT_KEY") . "\r\nContent-Type: application/json",
"content" => '{"items":["valor-1","valor-2"]}',
],
]));
$task = json_decode($res, true);body := bytes.NewBufferString(`{"items":["valor-1","valor-2"]}`)
req, _ := http.NewRequest("POST", "https://api.kit.forhosting.com/locale/format-number", body)
req.Header.Set("Authorization", "Bearer "+os.Getenv("KIT_KEY"))
req.Header.Set("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)Example request
{
"items": [
"valor-1",
"valor-2"
]
}Example response
{
"task_id": "tsk_a1b2c3d4e5f6a1b2c3d4e5f6",
"type": "locale.format_number",
"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_tokens | 20000 |
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. |