Authentication

All requests require an API key passed as a Bearer token in the Authorization header. Keys are issued per organization and come in two variants: hawk_live_ for production and hawk_test_ for development.

# All requests Authorization: Bearer hawk_live_<your-key>
Key format: hawk_live_<32 random chars> or hawk_test_<32 random chars>. Keys are stored as SHA-256 hashes — save your key when issued, it cannot be retrieved later.

Rate limits

Quotas are enforced per organization and reset daily at midnight UTC. Exceeding your quota returns a 429 with a reset_at timestamp in the response body.

# 429 response body { "error": "quota_exceeded", "reset_at": "2026-02-25T00:00:00Z" }

Endpoints

POST /v1/translate Submit a translation job

Creates a translation job and returns immediately with a 202 Accepted and a job_id. The translation runs asynchronously — poll GET /v1/translate/{job_id} or provide a callback_url to receive a webhook when the job completes.

Field Type Description
content string required HTML content to translate. Maximum 50,000 characters. HTML structure is preserved in the output.
target_language string required Target language code (e.g. "es"). See GET /v1/languages for supported codes.
source_language string optional Source language code. Defaults to "en".
tier string optional "instant" (default), "reviewed", or "certified".
callback_url string optional Webhook URL. Called via POST with the completed job payload when status reaches complete.
glossary_id string optional ID of a custom glossary to apply before translation (NJ place names, government titles, etc.).
# Request POST /v1/translate Authorization: Bearer hawk_live_... Content-Type: application/json { "content": "<p>City Council voted Tuesday...</p>", "target_language": "es", "tier": "instant", "callback_url": "https://your-cms.org/hooks/hawk" } # Response — 202 Accepted { "job_id": "3f8a1c2e-...", "status": "queued" }
GET /v1/translate/{job_id} Poll job status

Returns the current state of a translation job. Poll this endpoint until status is complete, reviewed, or failed — or use a callback_url to avoid polling.

# Response — 200 OK (complete job) { "job_id": "3f8a1c2e-...", "status": "complete", "translated_content": "<p>El Concejo Municipal votó...</p>", "source_language": "en", "target_language": "es", "tier": "instant", "word_count": 142, "quality_scores": [ { "index": 0, "overall": 4.5, "fluency": 5, "accuracy": 4, "needs_review": false } ], "created_at": "2026-02-25T14:30:00Z", "completed_at": "2026-02-25T14:30:04Z" }

Job status values:

queued translating machine_translated scoring in_review reviewed complete failed
GET /v1/languages List supported languages

Returns all supported target languages and their availability status.

# Response — 200 OK { "languages": [ { "code": "es", "name": "Spanish", "status": "available" }, { "code": "pt", "name": "Portuguese", "status": "available" }, { "code": "ht", "name": "Haitian Creole", "status": "limited" }, { "code": "hi", "name": "Hindi", "status": "limited" }, { "code": "ur", "name": "Urdu", "status": "limited" } ] }
Languages with "status": "limited" (Haitian Creole, Hindi, Urdu) return unmodified English pending full DeepL support. Do not use them for production content.

Error codes

All errors return a JSON body with an error field.

HTTP Error code Description
401 invalid_api_key Key is missing, malformed, or revoked.
429 quota_exceeded Daily quota exhausted. Response includes reset_at timestamp.
422 unsupported_language Target language not in the supported list. Response includes supported array.
422 content_too_large Content exceeds 50,000 characters. Split into multiple requests.
503 translation_service_unavailable DeepL API is unreachable. The job will be retried automatically (up to 3×). Check job status — it will complete when the service recovers.