Getting started
Your first translation
Submit a translation job, handle the webhook callback, and poll for results. Works for text, images, or both together.
1. Get an API key
Sign up at dashboard.getfora.ai. Your API key is ready immediately and always accessible from the dashboard. The free tier includes 10M characters and 2,000 images per month.
Alternatively, create an account via API:
curl -X POST https://api.getfora.ai/v1/accounts \
-H "Content-Type: application/json" \
-d '{"email": "you@example.com"}'{
"api_key": "fora_live_abc123...",
"account_id": "b7e2...",
"message": "Your API key has been sent to you@example.com. Store it somewhere safe."
}2. Configure image storage (optional)
Fora writes translated images to your own S3-compatible bucket — it holds no content at rest. Skip this step if you're only translating text.
curl -X POST https://api.getfora.ai/v1/accounts/storage \
-H "Authorization: Bearer $FORA_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"bucket_name": "my-translated-assets",
"region": "us-east-1",
"access_key_id": "AKIA...",
"secret_access_key": "..."
}'For Cloudflare R2, add endpoint and public_url fields to the request body.
3. Submit a translation job
POST /v1/translate accepts source content and returns a job ID immediately. Processing is async. Supported target locales: de, es, fr, it, pt-BR.
Pass protected_terms to preserve brand names, product codes, or technical terms exactly as written. Terms are matched case-insensitively and deduplicated — casing from the first occurrence is used in the prompt. Maximum 50 terms, 100 characters each.
curl -X POST https://api.getfora.ai/v1/translate \
-H "Authorization: Bearer $FORA_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"source_text": "Welcome to our marketplace. Buy and sell with confidence.",
"source_image_urls": [
"https://cdn.example.com/banner.jpg"
],
"target_locales": ["es", "pt-BR"],
"webhook_url": "https://yourapp.com/webhooks/translations",
"protected_terms": ["Nike", "AirForce 1"]
}'{
"job_id": "f3a8b2c1-...",
"status": "pending"
}4. Receive the webhook
When the job completes, Fora POSTs to your webhook_url. Deliveries are retried up to 3 times with exponential backoff on non-2xx responses.
{
"job_id": "f3a8b2c1-...",
"status": "completed",
"results": [
{
"locale": "es",
"translated_text": "Bienvenido a nuestro mercado. Compra y vende con confianza.",
"translated_image_urls": [
"https://my-translated-assets.s3.us-east-1.amazonaws.com/fora/f3a8b2c1/es/banner_es.jpg"
]
},
{
"locale": "pt-BR",
"translated_text": "Bem-vindo ao nosso marketplace.",
"translated_image_urls": [
"https://my-translated-assets.s3.us-east-1.amazonaws.com/fora/f3a8b2c1/pt-BR/banner_pt-BR.jpg"
]
}
]
}5. Poll for results (no webhook)
Poll GET /v1/content/{id}?locale= until status is completed. Returns 202 while processing.
curl "https://api.getfora.ai/v1/content/f3a8b2c1-...?locale=es" \ -H "Authorization: Bearer $FORA_API_KEY"
{
"job_id": "f3a8b2c1-...",
"locale": "es",
"status": "completed",
"translated_text": "Bienvenido a nuestro mercado.",
"translated_image_urls": [
"https://my-translated-assets.s3.amazonaws.com/.../banner_es.jpg"
]
}Node.js example
const API_KEY = process.env.FORA_API_KEY;
const BASE = 'https://api.getfora.ai';
async function translateContent(text, imageUrls, locales, webhookUrl) {
const res = await fetch(`${BASE}/v1/translate`, {
method: 'POST',
headers: {
'Authorization': `Bearer ${API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
source_text: text,
source_image_urls: imageUrls,
target_locales: locales,
webhook_url: webhookUrl,
}),
});
if (!res.ok) throw new Error(`${res.status} ${await res.text()}`);
return res.json(); // { job_id, status }
}
// Express webhook handler
app.post('/webhooks/translations', express.json(), (req, res) => {
const { job_id, status, results } = req.body;
for (const r of results) {
console.log(`[${r.locale}] ${r.translated_text}`);
// persist r.translated_image_urls to your DB
}
res.sendStatus(200);
});Python example
import os, httpx
API_KEY = os.environ["FORA_API_KEY"]
BASE = "https://api.getfora.ai"
def translate_content(text, image_urls, locales, webhook_url=None):
resp = httpx.post(
f"{BASE}/v1/translate",
headers={"Authorization": f"Bearer {API_KEY}"},
json={
"source_text": text,
"source_image_urls": image_urls,
"target_locales": locales,
"webhook_url": webhook_url,
},
)
resp.raise_for_status()
return resp.json() # {"job_id": "...", "status": "pending"}
# Flask webhook receiver
from flask import Flask, request
app = Flask(__name__)
@app.post("/webhooks/translations")
def receive_translation():
data = request.json
for result in data["results"]:
print(f"[{result['locale']}] {result['translated_text']}")
return "", 200Authentication
All endpoints except POST /v1/accounts require an API key:
Authorization: Bearer fora_live_abc123... # or X-API-Key: fora_live_abc123...
Error codes
| Status | Meaning |
|---|---|
| 400 | Invalid request body or unsupported locale |
| 401 | Missing or invalid API key |
| 402 | Monthly free tier quota exceeded |
| 404 | Job or translation not found |
| 500 | Internal error — retry with backoff |