API Reference

REST endpoints under https://api.localize.to/v1. For a gentler introduction, see the Developers page.

Base URL

Authentication — two keys per project

Every project has two independent API keys. They both identify the same project and their privileges are hierarchical:

Both keys are visible in the project's API tab and each can be regenerated independently.

Read endpoints

Authenticate with either key: ?apikey=READ_KEY as a query parameteror x-apikey-write: WRITE_KEY as a header. The write key can replace the read key everywhere — the reverse is not true.

GET /v1/project

Returns project metadata, enabled languages, and basic stats.

curl "https://api.localize.to/v1/project?apikey=READ_KEY"
{
  "id": 1,
  "name": "Demo project",
  "workspace": { "id": 2, "name": "Demo workspace" },
  "account": { "id": 1, "name": "Demo account" },
  "languages": ["en", "sk"],
  "stats": { "keys": 97, "translations": { "en": 97, "sk": 78 } }
}

GET /v1/language/{language}

Returns all translations for a single language.

Query parameters: format — one of json (default, pretty-printed), php, ios, android (same as xml), csv. separator and crlf apply to csv. nested=true splits dotted keys into nested objects/arrays for json and php; ignored by other formats. Leaf wins on collisions.

curl "https://api.localize.to/v1/language/en?apikey=READ_KEY&format=json"
{
  "sign_in": "Sign in",
  "sign_out": "Sign out",
  "projects": "Projects"
}

GET /v1/languages/{lang1,lang2}

Returns translations for the given comma-separated subset of languages. Same format options as above (json, php, csv). nested=true applies per-language to json and php.

curl "https://api.localize.to/v1/languages/en,sk?apikey=READ_KEY"
{
  "en": { "sign_in": "Sign in", "sign_out": "Sign out" },
  "sk": { "sign_in": "Prihlásiť sa", "sign_out": "Odhlásiť sa" }
}

GET /v1/languages

Same as above but returns every enabled language.

Write endpoints — use the write key

All write endpoints (and the single-key inspection endpoint) are authenticated with the x-apikey-write header. Request/response bodies are JSON.

GET /v1/translations/{lkey}

Returns the current state of a single key across all languages, including ownership flags. Use this before a write to verify what's there.

curl "https://api.localize.to/v1/translations/nav.home" \
  -H "x-apikey-write: WRITE_KEY"
{
  "id": 123,
  "lkey": "nav.home",
  "description": "Label for the Home link in the top nav",
  "ai": true,
  "values": {
    "en": { "text": "Home", "ai": true },
    "sk": { "text": "Domov", "ai": false }
  }
}

URL-encode the lkey if it contains anything other than letters, digits, dots, dashes, and underscores.

PUT /v1/translations

Create a key or update an existing one. Idempotent — safe to retry. All fields other than lkey are optional; send only what you want to set.

Query parameter: overwrite=true (default) updates existing AI-owned values and descriptions; overwrite=false only fills in missing ones.

curl -X PUT "https://api.localize.to/v1/translations" \
  -H "x-apikey-write: WRITE_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "lkey": "nav.home",
    "description": "Label for the Home link in the top nav",
    "values": {
      "en": "Home",
      "sk": "Domov"
    }
  }'

Response is the full post-update state, same shape as the GET above.

DELETE /v1/translations

Delete either a single language value or an entire key with all its values.

# delete just one language
curl -X DELETE "https://api.localize.to/v1/translations" \
  -H "x-apikey-write: WRITE_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "lkey": "nav.home", "language": "sk" }'

# delete the whole key
curl -X DELETE "https://api.localize.to/v1/translations" \
  -H "x-apikey-write: WRITE_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "lkey": "nav.home" }'

Responds with 201 and an empty body on success.

POST /v1/translations/rename

Rename a key while preserving all its values.

curl -X POST "https://api.localize.to/v1/translations/rename" \
  -H "x-apikey-write: WRITE_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "lkey": "nav.home", "new_lkey": "nav.homepage" }'

Fails with 400 if new_lkey already exists, 409 if the key is human-owned.

Ownership model (the ai flag)

Every key and every language value carries an ai boolean. Rows written through the write API are stored with ai=true; rows written by a human in the web UI are stored with ai=false. A human edit flips a row to ai=false permanently.

This protects human edits from being silently reverted by an automated integration.

Errors

Standard JSON error shape: { "error": "human-readable message" }.

5xx responses are transient — retry with backoff. Never retry 409 with the same body; it will fail the same way.

End-to-end example

Fetch project metadata, pull the English strings, then add a new key:

# 1. confirm project + languages
curl "https://api.localize.to/v1/project?apikey=READ_KEY"

# 2. pull English strings into a local file
curl "https://api.localize.to/v1/language/en?apikey=READ_KEY" > en.json

# 3. add a new key (uses the write key in a header)
curl -X PUT "https://api.localize.to/v1/translations" \
  -H "x-apikey-write: WRITE_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "lkey": "nav.welcome", "values": { "en": "Welcome" } }'
Localize.to

Translation management for apps and websites — manage keys, languages, users, imports and exports in one place.

support@localize.to