API v1¶
The Ponyglot Cloud API connects a website (through a connector) with Ponyglot. This page lists every endpoint of version 1 with its fields and status codes. The interactive reference shows the same from the machine-readable schema.
Basics¶
- Base URL
https://api.ponyglot.app/v1/. The address is permanent. Changes to v1 stay backwards compatible: new optional fields and endpoints may appear, existing ones keep working.- Authentication
Authorization: Bearer <API key>. Keys start withpg_and are created per site in the dashboard. A key belongs to exactly one site, and every request acts on that site.- Format
JSON in UTF-8, for requests and responses. Send
Content-Type: application/json.- Times
ISO 8601 with time zone, for example
2026-09-24T10:00:00Z.
Status codes¶
Code |
Meaning |
|---|---|
|
OK. |
|
Job accepted ( |
|
Unit deleted ( |
|
Missing, invalid or revoked API key. |
|
No subscription covers the site ( |
|
Unknown unit or job. |
|
The job needs confirmation ( |
|
The request is invalid. |
Limits¶
What |
Limit |
|---|---|
Request body |
10 MB |
Units per batch ( |
1–100 |
Segments per unit |
5,000 |
Characters per segment |
100,000 |
|
|
Segment |
1–500 characters (any characters) |
Language codes |
2–15 characters: letters, digits, |
Units per job ( |
1,000 |
Results per page ( |
1–500, default 100 |
IDs per acknowledgement |
1–1,000 |
Reviews per request |
1–500 |
Site¶
GET /site¶
The site the API key belongs to. Use it to check a key.
Field |
Type |
Description |
|---|---|---|
|
integer |
The site’s ID. |
|
string |
The site’s name. |
|
string |
The organization’s name. |
|
string |
Source language from the site settings. |
|
list |
One entry per target language: |
|
list of strings |
Adapters enabled in the settings: |
|
list of strings |
Engines content may be sent to: |
POST /handshake¶
The connector reports itself. Send it on start-up and when settings change.
Request field |
Type |
Description |
|---|---|---|
|
string, required |
Up to 50 characters, e.g. |
|
list of strings |
The connector’s active adapters, up to 20. |
|
language code, required |
The website’s source language. |
|
list of language codes |
All languages configured on the website (e.g. |
The response is the site, as in GET /site, plus:
Field |
Type |
Description |
|---|---|---|
|
string |
Always |
|
list of strings |
Mismatches, e.g. a different source language, or target languages missing from |
In the response, a target language is writable only if it is writable in the site settings
and listed in languages.
GET /status¶
Counts for the whole site. With prefix, only for the units whose external_key starts with
it, for example parler:blog.post: for all objects of one model.
Field |
Type |
Description |
|---|---|---|
|
integer |
Live units. |
|
integer |
Live segments. |
|
integer |
Terms in the organization’s glossary. |
|
list |
Per target language: |
GET /usage¶
This month’s billable characters for the site’s usage scope, for example for a connector’s toolbar.
Field |
Type |
Description |
|---|---|---|
|
string or null |
Plan code: |
|
boolean |
Whether hosted translation is available for this site. |
|
string |
Why not, if |
|
time |
The usage month: a calendar month in UTC. |
|
integer or null |
Included per month; |
|
integer |
Billable characters used this month in the scope. |
|
boolean |
|
GET /health¶
Returns {"status": "ok", "api_version": "1"}. Needs no API key.
Units¶
PUT /units¶
Push the complete current state of one unit. See Units, segments, formats and kinds for keys, formats and kinds.
Request field |
Type |
Description |
|---|---|---|
|
string, required |
Stable ID of the object, chosen by the connector: |
|
string, required |
|
|
string |
Human-readable title for the dashboard, up to 500 characters. |
|
string |
URL path on the site, e.g. |
|
language code |
The unit’s source language, if it differs from the site’s. |
|
list of language codes or null |
Restrict the target languages of this unit; |
|
object |
Anything useful for the connector, e.g. model or template names. |
|
list, required |
All current segments, up to 5,000. An empty list removes all segments. |
Segment fields:
Field |
Type |
Description |
|---|---|---|
|
string, required |
Stable and unique within the unit, up to 500 characters. |
|
string, required |
Source text, up to 100,000 characters. |
|
string |
|
|
string |
Name of the model field, up to 200 characters. |
|
string |
Hint for QA and prompts, e.g. |
|
integer |
Maximum length of the target field, if it has one. |
|
integer |
Order within the parent; defaults to the order in the list. |
|
string |
Key of the parent element, e.g. the plugin that contains this one. |
|
string |
Optional; your own fingerprint of |
|
object |
Optional existing translations: language code → text (see Import existing translations). |
Response 200:
Field |
Type |
Description |
|---|---|---|
|
string |
The unit. |
|
boolean |
|
|
integer |
New segment keys. |
|
integer |
Segments whose text changed. |
|
integer |
Segment keys that are no longer present. |
|
integer |
Segments with the same text. |
|
integer |
Translations that became stale with this push. |
|
integer |
Translations stored from |
|
list of strings |
Keys whose sent |
A snapshot with duplicate segment keys is rejected with 422. Pushing the same snapshot again
changes nothing, so retries are safe.
PUT /units/batch¶
Push up to 100 units: {"units": [snapshot, snapshot]}, each as in PUT /units. Each unit is
applied on its own; one failure doesn’t block the others.
Response 200: {"results": [item, item]}, in the order of the request. Each item has
external_key, ok (boolean), and either result (as in PUT /units) or error (a
message).
DELETE /units/{external_key}¶
The object was deleted on the site. Always 204, also for unknown or already deleted units.
Pushing the same key later restores the unit.
GET /units/{external_key}/status¶
The segment × language matrix of one unit. 404 for unknown or deleted units.
Field |
Type |
Description |
|---|---|---|
|
string |
The unit. |
|
string |
The unit’s page in the dashboard. |
|
time or null |
The last push. |
|
integer |
Live segments. |
|
integer |
Segments that aren’t |
|
list |
Per target language of the unit: |
|
list |
Per segment: |
Connectors link attention_url where translations are held back: they are the only ones that
need a decision in the dashboard.
GET /units¶
The units with something not up to date, ordered by id, for overviews of what needs attention.
Parameter |
Description |
|---|---|
|
Only units whose |
|
Only this target language. |
|
|
|
1–500, default 100. |
Field |
Type |
Description |
|---|---|---|
|
list |
Per unit: |
|
string or null |
Pass as |
In the QA mode editors fix, translations with QA errors are delivered to editors: they count
as review.
Translation jobs¶
POST /jobs¶
Request translation. Jobs run in the background, one at a time per site, in the order requested.
Request field |
Type |
Description |
|---|---|---|
|
string |
|
|
list of external keys |
Required for |
|
string |
|
|
list of language codes |
Default: all writable target languages. Other languages are left out. |
|
boolean |
Also retranslate segments that are approved, in review or held back by QA. |
|
boolean |
Confirm a job that needs it (see |
|
boolean |
Only return the estimate; create nothing. |
Responses:
202with the job (see below).200with an estimate, ifestimate_onlyis set:Field
Type
Description
languageslist of strings
The job’s languages.
estimated_segmentsinteger
Segments to translate.
estimated_charactersinteger
Billable characters: without translation memory hits and repeats.
estimated_tm_segmentsinteger
Segments the translation memory covers.
costobject
The cost estimate, see below.
An estimate doesn’t check the subscription. Without one, the cost’s volume fields are empty or zero.
402or409, see Billing errors.422iftranslatehas nounits, or if none of thelanguagesis a writable target language.
Cost estimate (cost, and estimate in 409):
Field |
Type |
Description |
|---|---|---|
|
integer |
Billable characters of this job. |
|
integer or null |
Included per month; |
|
integer |
Billable characters used this month so far. |
|
integer |
Still expected by queued and running jobs requested earlier. |
|
integer or null |
Included characters left after used and reserved ones. |
|
integer |
Characters of this job beyond the included volume. |
|
integer |
Estimated charge for them, in cents, billed after the month. |
|
string |
|
|
boolean |
Whether the job must be sent with |
Billing errors¶
402 and 409 have this body:
Field |
Type |
Description |
|---|---|---|
|
string |
A message to show to the user. |
|
string |
|
|
object or null |
For |
A job needs confirmation if it has more than 100,000 billable characters, or if it would use
characters beyond the included volume. Show the estimate, then repeat the request with
"confirm": true. See Request translations.
The job¶
POST /jobs (202), GET /jobs, GET /jobs/{id} and POST /jobs/{id}/cancel return jobs:
Field |
Type |
Description |
|---|---|---|
|
integer |
The job’s ID. |
|
string |
|
|
string |
|
|
list of strings |
Target languages. |
|
list of strings or null |
For |
|
boolean |
As requested. |
|
integer |
Segments to translate, estimated when requested. |
|
integer |
Billable characters, estimated. |
|
integer |
Segments the translation memory covers, estimated. |
|
integer |
Translated so far. |
|
integer |
Not translated, see |
|
integer |
Translated but held back by QA errors. |
|
integer |
Billed: source characters sent to engines. |
|
integer |
Covered by the translation memory; not billed. |
|
integer |
Repeats of a text within this job; not billed. |
|
list |
Per problem: |
|
time or null |
Timestamps. |
A job that translated nothing because every segment failed ends as failed.
GET /jobs¶
The 20 most recent jobs of the site. With ?active=true: only queued and running jobs.
GET /jobs/{id}¶
One job. 404 for jobs of other sites.
POST /jobs/{id}/cancel¶
Cancel a queued or running job. Segments that are already translated are kept. Returns the job.
Results¶
GET /results¶
Machine translations ready to be written as drafts or suggestions. Query parameters:
cursor (from next_cursor), limit (1–500, default 100) and unit (an external_key: only
that unit’s results, e.g. for an editor looking at one page).
Field |
Type |
Description |
|---|---|---|
|
list |
See below. |
|
string or null |
Pass as |
Each result:
Field |
Type |
Description |
|---|---|---|
|
string |
Opaque result ID, e.g. |
|
string |
The unit’s |
|
string |
The segment’s |
|
string |
Target language. |
|
string |
The translation. |
|
string |
Same as the source segment. |
|
string |
Fingerprint of the source text it translates. |
|
string |
|
|
string |
|
|
integer or null |
The job that produced it. |
|
list |
QA warnings: |
Only translations of the current source text are listed. Results stay listed until they
are acknowledged. An invalid cursor gets 422.
POST /results/ack¶
{"ids": ["r_5012", "r_5013"]} after writing the drafts. Returns
{"acknowledged": 2}: the number of results that moved to in review. Unknown, superseded and
already acknowledged IDs are skipped.
POST /reviews¶
Report editors’ decisions.
Field |
Type |
Description |
|---|---|---|
|
list, required |
1–500 reviews. |
|
string, required |
The result ID of the draft. |
|
string, required |
|
|
string |
Final text, if the editor changed it ( |
|
string |
Who decided, up to 200 characters. |
Response: updated (integer) and ignored (the IDs that were unknown, superseded or not
waiting for review).
approved: the translation is approved. A changedtextis stored with originhuman. Approved texts feed the translation memory.rejected: the segment counts as missing again and is translated by the next job.
A translation discarded in the dashboard while it waited in a draft still accepts its review until a newer translation replaces it: publishing the draft first counts as approval.
Result IDs¶
Result IDs are opaque strings with the prefix
r_. Don’t parse them, and don’t substitute other IDs.Every generated version has its own ID. A replacement never reuses an earlier ID, even when the text and the source are the same.
Acknowledging a superseded ID changes nothing; reviewing one returns it in
ignored.Numeric IDs are accepted in acknowledgements and reviews, but always ignored.