Connect a site through the API

Tip

For a Django site, install a connector instead: the dashboard’s Getting started walks you through it (see Connect your Django site).

This guide shows what a connector does to keep Ponyglot in sync with a website: authenticate, introduce itself, push content, and report deletions. Use it if you write your own connector or integrate a system the Connectors (early access) don’t cover. All details are in the API v1.

Get an API key

  1. In the dashboard, open the site and go to API keys. You need the admin or owner role.

  2. Enter a name, for example “Production” or “Staging”, and choose Create API key.

  3. Copy the key (pg_…). It is shown only once.

Use one key per environment, so you can rotate them independently. To rotate a key, create a new one, deploy it, then Revoke the old one. The table of keys shows when each key was last used.

Send the key as a bearer token with every request:

$ curl -H "Authorization: Bearer $PONYGLOT_API_KEY" https://api.ponyglot.app/v1/site

A missing, revoked or mistyped key gets 401. Keep the key out of version control, for example in an environment variable.

Send the handshake

Send POST /v1/handshake when the connector starts and whenever its settings change:

{
  "connector_version": "0.1.0",
  "adapters": ["djangocms", "parler"],
  "source_language": "en",
  "languages": ["en", "de", "fr"]
}

languages lists the languages configured on the website, for example Django’s settings.LANGUAGES. The response contains the site settings. Check two things:

  • target_languages[].writable: a target language is writable only if it is writable in the dashboard and listed in languages. Ponyglot translates only writable languages.

  • warnings: human-readable mismatches, for example a different source language, or target languages that the website doesn’t have. Show them to the developer.

Push a unit

Map each translatable object to a unit with a stable external_key and each translatable field value to a segment with a stable key. Then send a complete snapshot with PUT /v1/units:

{
  "external_key": "djangocms:page:42",
  "adapter": "djangocms",
  "label": "Pricing",
  "path": "/pricing/",
  "segments": [
    {"key": "title", "text": "Pricing", "kind": "title", "field": "title", "max_length": 255},
    {"key": "plugin:103:body", "text": "<p>€79 per site and month</p>", "format": "html",
     "kind": "body", "field": "body", "parent_key": "plugin:102", "position": 4}
  ]
}

Always send all current segments of the unit, in the source language. Ponyglot compares the snapshot with what it has:

  • new keys are added,

  • keys with changed text are changed, and their translations become stale,

  • keys that are missing from the snapshot are removed; they come back if they reappear,

  • changes to structure only (position, parent_key, kind, …) are stored without making anything stale.

Push after every save of a source object, for example from a post_save signal or when a page is published. Pushing an unchanged snapshot changes nothing, so retries are always safe.

Tip

Pick keys that survive edits. For django CMS, use the plugin ID and field name (plugin:103:body). For Wagtail StreamField, use the block UUID path (body.<uuid>.heading). Never use list positions as keys: inserting a paragraph would then change the key of every paragraph after it.

Backfill many units

For the first sync, or after adding a model, send batches with PUT /v1/units/batch:

{
  "units": [
    {"external_key": "parler:blog.post:17", "adapter": "parler", "label": "Delta sync explained",
     "segments": [{"key": "title", "text": "Delta sync explained", "kind": "title"}]},
    {"external_key": "parler:blog.post:18", "adapter": "parler",
     "label": "One glossary for CMS and models",
     "segments": [{"key": "title", "text": "One glossary for CMS and models", "kind": "title"}]}
  ]
}
  • Send at most 100 units per batch and keep the body under 10 MB.

  • Send the batches one after another, not in parallel.

  • Each unit is applied on its own. The response lists {"external_key", "ok", "result"} or {"external_key", "ok": false, "error"} per unit, in the order you sent them. Retry the failed units, for example after fixing duplicate segment keys.

Limits per unit: 5,000 segments, 100,000 characters per segment.

Restrict the languages of a unit

Some objects can’t store every language. With django-modeltranslation, for example, a language needs a database column. Set writable_languages on the unit to the languages it can store; null (the default) means all target languages of the site.

Report deleted objects

When an object is deleted on the website, send DELETE /v1/units/{external_key}. The answer is always 204, also for unknown or already deleted units, so you can repeat it safely. If an object with the same key appears again later, pushing it restores the unit.

Check the result

  • GET /v1/units/{external_key}/status returns the segment × language matrix of one unit, for example for a toolbar in the CMS.

  • GET /v1/status returns counts per language for the whole site and the size of the organization’s glossary.

The dashboard shows the connector version and the time of the last handshake on the site’s page.