Drive your translations from your own code: a REST API over projects and phrases with scoped keys and per-key rate limits, a dictionary fetch your app can make with no credentials at all, and signed webhooks when a translation changes.
API keys
The MultiLocale REST API has two resources: projects and phrases. A phrase is one key, value and language triple belonging to one or more projects, so translating an app means writing one phrase per language for every source string. Everything you can do to them in the dashboard, your own backend can do over HTTP.
Open a project in the MultiLocale dashboard and create an API key under API keys. The secret is shown once, when the key is created, and never again — store it somewhere safe. A key belongs to one project of one organization, and both are read from the key itself, never from the request.
Authenticate every request with HTTP Basic auth carrying only the key secret, base64-encoded, in the Authorization header.
# The Authorization header is HTTP Basic auth carrying only the key secret,
# with no username and no colon.
Authorization: Basic $(printf %s YOUR_API_KEY_SECRET | base64)Every endpoint lives under https://api.multilocale.com. Requests made with a key are rate limited to 300 per minute per key; going over the limit returns 429. The dashboard authenticates differently, with an operator session token sent as Authorization: Token <access token>.
Quick start
Read the phrases of a project, then upsert some. POST is an upsert: rows that did not exist are created, rows that did are replaced, and each one emits its own webhook event — one per row, not one per request.
# Read the French translations of one project
curl "https://api.multilocale.com/api/phrases?project=YOUR_PROJECT&language=fr" \
-H "Authorization: Basic $(printf %s YOUR_API_KEY_SECRET | base64)"
# Upsert a phrase. Send one object or an array of them; each row is one
# {key, value, language} triple, and `projects` lists the projects it belongs to.
curl -X POST https://api.multilocale.com/api/phrases \
-H "Authorization: Basic $(printf %s YOUR_API_KEY_SECRET | base64)" \
-H "Content-Type: application/json" \
-d '[
{ "key": "Save", "value": "Enregistrer", "language": "fr", "projects": ["YOUR_PROJECT"] },
{ "key": "Save", "value": "Guardar", "language": "es", "projects": ["YOUR_PROJECT"] }
]'Reading phrases is also possible with no credentials at all, scoped by an organization id in the query string. That is how a shipped app fetches its own translations at runtime — the request comes from a browser, where no secret can be hidden — so this one read is anonymous on purpose.
# No Authorization header — the organization id is the only scope
curl "https://api.multilocale.com/api/phrases?organizationId=YOUR_ORGANIZATION_ID&project=YOUR_PROJECT&language=fr"Everything else — writing phrases, and anything to do with projects — needs a key or an operator session.
Browse the full API reference — every endpoint with its parameters, responses and required scope, generated from the API itself. The guides, the CLI and the React and Next.js integration are documented here.
CLI
The same projects and phrases are available from your terminal through the multilocale CLI, plus the file workflows the API deliberately leaves to your machine: downloading translation dictionaries into a codebase and importing existing ones. Install it globally with npm, or run it ad hoc with npx.
Authentication is one command: multilocale login opens your browser to sign in to your MultiLocale account and stores a session for later commands — no API key to paste.
You do not even need an account first: multilocale signup --email you@example.com --json creates one from the terminal and writes the session to the same place login does, so a CI job or a coding agent can go from nothing to translated files without a browser and without a person.
# Install once, globally
npm install -g multilocale
# or run it ad hoc without installing
npx multilocale --help
# Create the account from the terminal — no browser, nobody to click anything
multilocale signup --email you@example.com --json
# Already have an account? Log in instead — opens your browser, stores a session
multilocale login
# Create a project with the locales it ships
multilocale projects create "my-app" --locales en,es,fr --default-locale en
# List your projects
multilocale projects list
# Add a phrase and machine-translate it into every project locale
multilocale add "Save changes"
# Fix one translation by hand
multilocale update "Save changes" "Enregistrer les modifications" -l fr
# Write the project's translation files into your codebase
multilocale downloadThe CLI is open source at github.com/multilocale/cli and published as multilocale on npm. Run any command with --help to see its options.
Agent Plugins
The open Agent Plugins standard packages the Multilocale MCP server and its localization skills together. Install one repository and your agent learns the workflow, connects to the remote tools, and sends you through the same Multilocale OAuth approval flow — no API key or client secret is bundled in the plugin.
# Portable Agent Plugins package (Kiro, Cursor, Copilot-compatible hosts)
https://github.com/multilocale/claude-plugin
# Gemini CLI
gemini extensions install https://github.com/multilocale/claude-plugin
# Google Antigravity uses its native adapter from the same repository
git clone https://github.com/multilocale/claude-plugin.git
agy plugin install ./claude-plugin/com.google.antigravityThe repository includes the portable plugin.json and mcp.json manifests, plus native adapters for Claude, Gemini CLI, and Google Antigravity. The portable package is also the submission artifact for Kiro Powers, the Cursor Marketplace, and Awesome Copilot.
Agents and registries can discover the same package from the well-known Agent Plugin manifest or from Multilocale’s AI Catalog.
Agent Skills
MultiLocale ships Agent Skills — guides following the agentskills.io standard that teach coding agents how to manage translations with the multilocale CLI and the MCP connector, instead of guessing at commands and tools.
# Install the Multilocale skills into your coding agent
npx skills add multilocale/skillsOne command installs the skills into Claude Code, Cursor, Codex, Gemini CLI and any other agent that follows the Skills standard. The CLI also bundles the same guides, version-matched to the commands it ships: multilocale skills get <name> prints one on demand.
The skills are open source at github.com/multilocale/skills. Claude users can also install the MultiLocale Claude plugin, which bundles the connector together with the skills: github.com/multilocale/claude-plugin.
Scopes
Each key carries a list of scopes, so an integration that only needs to read your translations never gets the ability to overwrite them. New keys start read-only; widen them explicitly in the dashboard. A request whose key is missing the scope an endpoint requires is refused with 403.
Scopes say what a key may do; they say nothing about where. Because a key names one project, a request touching any other project is refused even when the scope allows the operation. Users, roles, invites and preferences are dashboard-only and have no scope at all — an API key never reaches them.
Webhooks
Add a webhook subscription in the dashboard and MultiLocale POSTs the events you picked to your server as they happen. A subscription can watch one project or the whole organization, and picking no event at all means every event.
POST https://your-server.com/multilocale-webhook
X-Multilocale-Event: phrase.updated
X-Multilocale-Signature: t=1719000000,v1=<hmac-sha256 hex>
Content-Type: application/json
{
"event": "phrase.updated",
"timestamp": 1719000000,
"data": { "...": "..." }
}Every delivery carries an X-Multilocale-Event header naming the event, and an X-Multilocale-Signature header of the form t=timestamp,v1=signature. The signature is an HMAC-SHA256 of timestamp.body — the unix timestamp, a dot, then the raw JSON body — keyed by the subscription secret shown to you once when the subscription was created. Recompute it over the raw body and compare before trusting the payload.
import crypto from 'node:crypto'
// body must be the RAW request body, byte for byte
function verify(header, body, secret) {
const [t, v1] = (header || '').split(',').map(part => part.split('=')[1])
if (!t || !v1) return false
const expected = crypto
.createHmac('sha256', secret)
.update(`${t}.${body}`)
.digest('hex')
// timingSafeEqual throws on a length mismatch, so a malformed signature
// has to be rejected before the comparison rather than by it.
if (v1.length !== expected.length) return false
return crypto.timingSafeEqual(Buffer.from(v1), Buffer.from(expected))
}Delivery is one best-effort attempt with a five second timeout and no retries, so respond 2xx quickly and do the work asynchronously. An endpoint that fails twenty times in a row is disabled automatically and has to be re-enabled in the dashboard; a single successful delivery resets the counter. Subscriptions themselves are dashboard-only — no API key can create one, because a key that could would be able to redirect every future write to an endpoint of its choosing.
Start building