Terrior API reference

Rewrite commercial copy toward how a specific region writes it, and get told what could not safely be changed. Base URL: https://terrior-api-1069459166596.europe-west1.run.app. Requests and responses are JSON (UTF-8). Version: v1.

Getting started

Subscribe at https://terrior-api-1069459166596.europe-west1.run.app/signup. Checkout returns you to a page that shows your API key once; we store only a hash of it. Then:

curl https://terrior-api-1069459166596.europe-west1.run.app/v1/convert \
  -H 'Authorization: Bearer tk_live_...' \
  -H 'Content-Type: application/json' \
  -d '{"region": "cat-es-56-barcelona", "text": "हमारे इस उत्पाद की क्वालिटी पूरी तरह जांची गई है"}'

Regions available now: cat-es-56-barcelona, cat-es-56-manresa, cat-es-56-sabadell, deu-ch-bs-basel, deu-de-04-hamburg, deu-de-05-bebra, deu-de-07-bergisch-gladbach, deu-de-07-leverkusen, deu-de-13-chemnitz, deu-de-13-dresden, eng-gb-sct, eng-in-05-chandigarh, eng-in-07-delhi, eng-in-09-ahmedabad, eng-in-13, eng-in-16-mumbai, eng-in-16-pune, eng-in-19, eng-in-19-bengaluru, eng-in-21, eng-in-25, eng-in-25-chennai, eng-in-28-kolkata, eng-in-40, eng-ke-05-nairobi, eng-ng-05-lagos, eng-us-ga-atlanta, eng-us-ks-smith-center, eng-us-mo-saint-joseph, eng-us-nc-salisbury, eng-us-pa-bloomsburg, hin-in-05-chandigarh, hin-in-07-delhi, hin-in-10, hin-in-16-mumbai, hin-in-23, hin-in-24, hin-in-24-jaipur, hin-in-34, hin-in-34-patna, hin-in-35, hin-in-35-bhopal, hin-in-36, hin-in-36-lucknow, hin-in-37-raipur, hin-in-38, hin-in-38-ranchi, hin-in-39, hin-in-39-dehradun, hun-hu-01-kiskunfelegyhaza, hun-hu-05-budapest, hun-ro-09-viscri, mar-in-16, mar-in-16-mumbai, mar-in-16-nagpur, mar-in-16-pune, pol-pl-72-wrocaw, pol-pl-77-krakow, pol-pl-78-warsaw, pol-pl-86-poznan, por-br-04, por-br-05, por-br-05-salvador, por-br-15-belo-horizonte, por-br-17, por-br-18, por-br-18-apucarana, por-br-18-arapongas, por-br-18-curitiba, por-br-21-rio-de-janeiro, por-br-22, por-br-23, por-br-26, por-br-26-lages, por-br-27-sao-paulo, por-br-29, ron-ro-03-pitesti, ron-ro-07-botosani, ron-ro-09-brasov, ron-ro-11-buzau, ron-ro-13-cluj-napoca, ron-ro-14-constanta, ron-ro-16-targoviste, ron-ro-18-galati, ron-ro-21-hunedoara, ron-ro-23-iasi, ron-ro-28-piatra-neamt, ron-ro-29-slatina, ron-ro-30-ploiesti, ron-ro-36-timisoara, ron-ro-39-ramnicu-valcea, rus-by-05-stowbtsy, rus-ru-48-moscow, rus-ru-67-saratov, spa-ar-07-buenos-aires, spa-co-34-bogota, spa-es-29-madrid, spa-es-34, spa-es-34-aviles, spa-es-34-gijon, spa-es-34-oviedo, spa-es-51-sevilla, spa-es-57, spa-es-58, spa-es-58-pontevedra, spa-es-58-vigo, spa-es-60-valencia, spa-mx-07-saltillo, spa-mx-15, spa-mx-24-los-zacatones, tam-in-25-chennai, tam-in-25-coimbatore, tam-in-25-cuddalore, tam-in-25-dharmapuri, tam-in-25-erode, tam-in-25-madurai, tam-in-25-nagapattinam, tam-in-25-namakkal, tam-in-25-thiruvarur, tam-in-25-vellore, zho-cn-04-suzhou, zho-cn-10, zho-cn-11, zho-cn-12, zho-cn-23-shanghai, zho-cn-25-weihai. Every plan includes every region, and every region added later.

Authentication

Send your key as a bearer token on every /v1/ request: Authorization: Bearer tk_live_<id>_<secret>. Keys beginning tk_test_ belong to the staging service and are refused by the live one, and the reverse. Any problem with the key itself (missing, malformed, unknown, revoked) is the same 401, deliberately. A key that is valid on an account suspended for non-payment gets 402; a closed account gets 403. Lost a key? Contact support to have it revoked and a new one issued.

Plans and limits

PlanRateRequests included per monthConcurrent requests
Starter60 / minute50,0004
Growth600 / minute500,00016
Scale3,000 / minute2,500,00064

Every plan also has: all regions; at most 5,000 words per request (past that it is a batch job, and the request is refused with 403); a request body of at most 1 MiB (413). Prices are shown at checkout. Requests past the monthly allowance are not refused: they are billed as overage (see What is billed). The per-minute rate is enforced and answers 429 with Retry-After.

Rate-limit headers

Every admitted /v1/convert and /v1/profiles response carries:

HeaderMeaning
X-RateLimit-LimitRequests allowed per minute on your plan.
X-RateLimit-RemainingRequests left in the current sliding minute.
X-RateLimit-ResetUnix time (seconds) at which the window is full again.

A 429 carries Retry-After in seconds. Back off for at least that long; the window slides, so retrying immediately is refused again.

Errors

Every error is {"error": "<a sentence saying what to do>"} with one of these statuses:

StatusWhenRetry?
400The body is not JSON, or text / region is missing or empty.No: fix the request.
401No key, or a key that is malformed, unknown or revoked.No.
402The account is suspended for non-payment. POST /v1/billing/portal still works, so you can pay.After paying.
403No profile for that region yet (the message lists those available); over 5,000 words in one request; or the account is closed.No.
413Request body over 1 MiB.No.
429Over your per-minute rate, or over your plan's concurrent requests.Yes, after Retry-After.
503The service could not reach its own storage or payment provider. The detail is logged on our side, never returned.Yes, with backoff.

What is billed

Your plan's monthly fee covers its included requests. Each successful POST /v1/convert counts as one request. Requests past the allowance are billed per 1,000 (rounded up) on the next invoice. Refused requests (any 4xx or 5xx), GET /v1/profiles, GET /v1/account and GET /health are not counted. Invoices, payment method and cancellation are in the billing portal (POST /v1/billing/portal).

Endpoints

POST /v1/convert billed

Rewrite text toward how region writes it. Only swaps that are provably safe are applied; the rest are returned as flagged for a person to decide. Syntax and structure are never changed.

Request fieldType
regionstringRequired. One of the regions in /v1/profiles.
textstringRequired, non-empty, at most 5,000 words.
{
  "region": "lko",
  "text": "हमारे इस उत्पाद की गुणवत्ता पूरी तरह जांची और परखी गई है, और प्राइस में कोई छिपा हुआ खर्च नहीं है। ...",
  "applied": [
    { "concept": "quality", "offset": 19, "original": "क्वालिटी", "replacement": "गुणवत्ता" }
  ],
  "flagged": [
    { "concept": "price", "offset": 61, "original": "प्राइस", "replacement": "मूल्य",
      "reason": "grammatical gender of the loanword and the native word is unknown or they disagree -- swapping could leave an adjective or verb agreeing with the wrong gender; check before using" }
  ],
  "caveats": [ "Built from newspaper text. Commercial copy is a different register ...", "..." ],
  "convert_ms": 0.0655
}
Response field
textThe input with every applied swap made.
applied[]Swaps made. offset is a Unicode code-point offset into your input, not into the returned text.
flagged[]Swaps the profile suggests but did not make, each with a reason. Review these before publishing.
caveats[]What this region's profile does not yet know. Show them to whoever approves the copy.
convert_msServer-side conversion time in milliseconds, excluding network and queueing.

GET /v1/profiles

The regions your key can use, each with the number of rules, a content digest (changes when the profile is rebuilt), and its caveats.

{ "profiles": [ { "region": "lko", "n_rules": 83, "digest": "c8626095…", "caveats": ["…"] } ] }

GET /v1/account

Your plan's limits, subscription status and this billing period's usage.

{ "tenant": "t_…", "key_id": "…", "tier": "basic",
  "requests_per_minute": 600, "requests_per_month": 500000, "max_concurrency": 16, "regions": "all",
  "subscription": { "status": "active", "period_start": 1791590400, "period_end": 1794268800 },
  "usage_this_period": { "billable_requests": 1234, "requests": 1250, "words": 52310, "overage_units_reported": 0 } }

POST /v1/billing/portal

Returns a one-time link to the billing portal: invoices, payment method, cancellation. Works for an account suspended for non-payment.

{ "url": "https://billing.stripe.com/p/session/…" }

GET /health no key

{ "status": "ok", "regions": ["del", "lko"] }

POST /v1/score not yet available

Scoring (which region a piece of copy reads as, and which words carry that reading) is not served by this API yet and answers 501. It is not billed.