Public API · v1

API Reference

A small, API-key-authenticated surface for pulling and pushing a single project's translations — built for CI pipelines, build-time codegen, and pull/push scripts. It mirrors the Localizely API, so many existing integrations work by only swapping the base URL.

Base URL

https://localizator.carcutter.io

On this page

Authentication

Every request must include a project API key, created from that project's Settings → API keys card in the app. Send it in the X-Api-Token header (the Localizely-compatible form), or as a standard Authorization: Bearer header — either works.

X-Api-Token: lt_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

# or

Authorization: Bearer lt_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

A key is scoped to the single project it was created for. Requests with a missing or invalid token get a 401; a token that belongs to a different project gets the same 404a nonexistent project would, so a leaked token can't be used to probe which project ids exist.

Glossary

Project
A workspace containing one set of translation keys and one or more languages. Every API call is scoped to a single project via its {projectId}.
Locale / language code
An identifier for a target language, e.g. en, de, fr-CA. Wherever the API accepts lang_code / lang_codes, either the locale itself or the underlying language's internal id works.
Translation key
The stable identifier for one translatable string, e.g. greeting or nav.home. Dot-separated segments are treated as nested paths in JSON (see nested vs. flat JSON).
API key
A per-project secret (prefixed lt_) created from Settings → API keys. Sent as X-Api-Token or Authorization: Bearer to authenticate every request.
Overwrite
Import option controlling whether an existing non-empty translation value is replaced (overwrite: true, the default) or left untouched (false — only blanks get filled in).
ARB
Application Resource Bundle — Flutter's JSON-based localization file format (.arb), with @@locale and @key metadata entries alongside the translations.
Nested vs. flat JSON
Nested JSON mirrors dot-path keys as real object nesting ({ nav: { home: ... } }); flat JSON keeps each key as a single top-level dot-path string ({ "nav.home": ... }). Controlled by the nested query param on download.
Import job
An audit record created every time translations are loaded via POST /files or the app's import UI: source file name, format, and how many keys were added/updated. Feeds the project's Activity tab.

Errors

Errors are always a JSON body shaped { "error": string } (validation errors also include an issues array from the request schema).

StatusMeaning
400Malformed request (missing field, unparsable file content).
401Missing, invalid, or revoked API token.
404Project or locale not found (or the token belongs to a different project).
422Request body failed schema validation.
503Database unreachable or not configured.

Project status

GET/api/v1/projects/{projectId}/status

Lists the project's languages, so tooling can discover which locale codes are available before downloading or uploading. The default/source language is listed first.

curl -H "X-Api-Token: $API_TOKEN" \
  "https://localizator.carcutter.io/api/v1/projects/$PROJECT_ID/status"

Response

{
  "id": "cmq6pqqdy04v8tkxfp4vknobz",
  "name": "Demo Project",
  "defaultLocale": "en",
  "languages": [
    { "langCode": "en", "name": "English", "isDefault": true },
    { "langCode": "de", "name": "German", "isDefault": false }
  ]
}

Download a locale

GET/api/v1/projects/{projectId}/files/download

Downloads one locale's translations as a file body (not wrapped in JSON metadata) — a JSON map or a Flutter ARB file.

Query paramRequiredDescription
lang_codesyesA single locale code (e.g. en, fr-CA). Multiple locales per request aren't supported.
typenojson (default) or arb (Flutter).
export_empty_asnomain falls empty values back to the source language; default keeps them blank.
nestednofalse emits a flat dot-path object (JSON only).
# Fetch a locale as nested JSON (the default)
curl -H "X-Api-Token: $API_TOKEN" \
  "https://localizator.carcutter.io/api/v1/projects/$PROJECT_ID/files/download?lang_codes=en"

# Flat, dot-path JSON
curl -H "X-Api-Token: $API_TOKEN" \
  "https://localizator.carcutter.io/api/v1/projects/$PROJECT_ID/files/download?lang_codes=en&nested=false"

# Flutter ARB, filling empty values from the source language
curl -H "X-Api-Token: $API_TOKEN" \
  "https://localizator.carcutter.io/api/v1/projects/$PROJECT_ID/files/download?lang_codes=fr&type=arb&export_empty_as=main" \
  -o app_fr.arb

Load (upload) a locale

POST/api/v1/projects/{projectId}/files

Imports translations for one locale — the push counterpart to download. New keys are created automatically; by default existing non-empty values are overwritten (set overwrite: false to only fill in blanks). Each call archives the submitted content, writes an import joband activity-log entry (visible on the project's Activity tab), and triggers a background Google Drive sync if one is connected.

Send either a file upload or a JSON body — whichever is more convenient for your client.

Option A — multipart/form-data

FieldRequiredDescription
fileyesThe JSON or ARB file to import.
lang_codeyesTarget locale code or language id.
formatnoJSON or ARB; guessed from the file name if omitted.
overwriteno"true" (default) or "false".
curl -X POST "https://localizator.carcutter.io/api/v1/projects/$PROJECT_ID/files" \
  -H "X-Api-Token: $API_TOKEN" \
  -F "file=@de.json" \
  -F "lang_code=de"

Option B — JSON body, inline translations

A flat or nested map of translation keys to values, applied the same way a JSON file import would be (see nested vs. flat JSON).

curl -X POST "https://localizator.carcutter.io/api/v1/projects/$PROJECT_ID/files" \
  -H "X-Api-Token: $API_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "lang_code": "de",
    "translations": { "greeting": "Hallo", "nav": { "home": "Startseite" } }
  }'

Option C — JSON body, raw file content

Send the file's text as a string — useful when you already have a JSON or ARB document in hand and don't want to multipart-encode it.

curl -X POST "https://localizator.carcutter.io/api/v1/projects/$PROJECT_ID/files" \
  -H "X-Api-Token: $API_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "lang_code": "de",
    "format": "JSON",
    "content": "{\"greeting\": \"Hallo\"}"
  }'

Response (all three options)

{
  "added": 12,
  "updated": 3,
  "total": 15,
  "blobUrl": "https://...blob.vercel-storage.com/imports/..."
}

blobUrlis an empty string when the deployment doesn't have Vercel Blob storage configured — the import still succeeds either way.

Need a token?

Open a project's Settings tab in the app and create a key under API keys.

Open the app