Quickstart
This page gets you from nothing to a real, working API call in a few minutes. Every snippet below is copy-paste runnable — pick the tab for curl, JavaScript, or Python.
A few endpoints are open to anyone, and the rest need you to sign in. We call that auth-tiered: a handful of read-only calls (checking the API is reachable, and pricing a file) work anonymously, with no sign-in, while anything that creates or translates a book needs an API key — a secret string that identifies your account. This page walks both tiers in order. First you'll make an anonymous call to confirm you can reach the API, then you'll create a key and make your first call as yourself.
Base URL
The samples use the production base URL https://api.getkenly.com. If you're running the stack on your own machine instead, the base URL is http://localhost:5080 (the Aspire-pinned API port) — swap it in and every sample works unchanged. Both are the API's declared servers, so the interactive Try-it console on every operation page hits the same endpoints.
Tier 1 — Anonymous (no key)
Two things need no credential at all: discovery (asking the API to describe itself) and pricing an upload. Start with discovery — it's the "am I even pointed at the right place?" call.
Confirm connectivity — GET /v1/meta
GET /v1/meta is public — no key required. It hands back a little description of the API: the batch ladder, the export formats it can produce, and the base URL it uses for error types. That's everything a client needs to configure itself before it signs in.
bash
curl https://api.getkenly.com/v1/metajavascript
const res = await fetch("https://api.getkenly.com/v1/meta");
const meta = await res.json();
console.log(meta);python
import requests
meta = requests.get("https://api.getkenly.com/v1/meta").json()
print(meta)json
{
"apiVersion": "1.0",
"batchLadder": [1, 5, 25, 100],
"finalBatch": "rest",
"exportFormats": ["All", "Markdown", "PlainText", "Html", "Epub", "Docx", "Pdf", "Archive"],
"problemTypeBase": "https://kenly/errors/"
}A 200 response here means your base URL and network path are good — you've reached the API. If you're pointing at a backend that just started up, also check GET /v1/model-status: it tells you whether the GPU that does the translating is warmed up (ready) or still starting.
Price a book — POST /v1/anonymous-estimate
Before anyone signs up, they usually want to know what a book will cost. You can get a free page and word estimate for a real file, with no account — POST /v1/anonymous-estimate takes a multipart/form-data file upload and returns a page count and a word estimate. It doesn't run OCR and it doesn't charge anything; it's the same call the marketing site uses to price a file someone drops in before they sign up.
bash
curl -X POST https://api.getkenly.com/v1/anonymous-estimate \
-F "file=@book.pdf"javascript
const form = new FormData();
form.append("file", fileInput.files[0]); // a File from an <input type="file">
const res = await fetch("https://api.getkenly.com/v1/anonymous-estimate", {
method: "POST",
body: form,
});
const estimate = await res.json();
console.log(estimate);python
import requests
with open("book.pdf", "rb") as f:
estimate = requests.post(
"https://api.getkenly.com/v1/anonymous-estimate",
files={"file": f},
).json()
print(estimate)That's the whole anonymous surface — two calls. To create a project and translate it, you need a key. That's Tier 2.
Tier 2 — Authenticated (with an API key)
Get an API key
An API key is a long-lived secret that stands in for signing in. Create one in the web app under Settings → Developer (https://app.getkenly.com/settings/developer): give it a name, choose what it's allowed to do (its scopes), and copy the kenly_sk_live_… secret. Copy it right away — it's shown exactly once and can't be retrieved later. For the full walkthrough, the scope table, and how to mint keys from code, see Authentication & API keys.
Keep the secret out of your source code. The simplest safe habit is to put it in an environment variable and read it from there:
bash
export KENLY_API_KEY="kenly_sk_live_..."Your first authenticated call — GET /v1/wallet
The quickest way to prove your key works is to read your own wallet balance. You can send the key one of two ways — as Authorization: Bearer <key> or as X-Api-Key: <key> — and the API accepts either (that's the dual "smart" scheme; more on it in the auth guide).
bash
curl https://api.getkenly.com/v1/wallet \
-H "Authorization: Bearer $KENLY_API_KEY"javascript
const res = await fetch("https://api.getkenly.com/v1/wallet", {
headers: { Authorization: `Bearer ${process.env.KENLY_API_KEY}` },
});
const wallet = await res.json();
console.log(wallet);python
import os, requests
wallet = requests.get(
"https://api.getkenly.com/v1/wallet",
headers={"Authorization": f"Bearer {os.environ['KENLY_API_KEY']}"},
).json()
print(wallet)json
{ "balanceUsd": 5.00, "currency": "USD", "unlimited": false, "lowBalance": false }A 200 with your balance means you're in — the key works. If instead you get a 401, the key is unknown, revoked, or expired; a 403 means the key is valid but doesn't have a scope the call requires. The Errors page has the full list of codes.
Translate a book, end to end
With a working key, a full run is four steps. Each one links to its operation page, where you can read the exact schema and fire the call from the Try-it console.
Create a project —
POST /v1/projects. A project is one book. Point it at your uploaded source file and, optionally, the language to translate into:bashcurl -X POST https://api.getkenly.com/v1/projects \ -H "Authorization: Bearer $KENLY_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "name": "My Book", "uploadKey": "<your-upload-key>", "targetLanguageCode": "en" }'javascriptconst res = await fetch("https://api.getkenly.com/v1/projects", { method: "POST", headers: { Authorization: `Bearer ${process.env.KENLY_API_KEY}`, "Content-Type": "application/json", }, body: JSON.stringify({ name: "My Book", uploadKey: "<your-upload-key>", targetLanguageCode: "en", }), }); const project = await res.json();pythonimport os, requests project = requests.post( "https://api.getkenly.com/v1/projects", headers={"Authorization": f"Bearer {os.environ['KENLY_API_KEY']}"}, json={"name": "My Book", "uploadKey": "<your-upload-key>", "targetLanguageCode": "en"}, ).json()The response carries the new project's
id— you'll use it in the next steps. Retries are safe with no extra work: the API deduplicates a double-submit of a billable call server-side, so a timed-out request you send again won't double-charge or double-start the book.Start processing —
POST /v1/projects/{id}/process. This is the one call that kicks everything off; from here the book runs itself. Batches auto-advance (1 → 5 → 25 → 100 → rest) with no human approval step, and the only thing that ever pauses a run is your wallet running low (you'll get a402 insufficient_credit— top up and it picks right back up).Track progress — poll
GET /v1/projects/{id}/statusto see where the book is, or subscribe to lifecycle events / webhooks so you don't have to poll at all.Download the export once the book reaches a final state — your finished translation in Markdown (with DOCX, EPUB, and PDF to follow).
If a batch would cost more than your balance, top up the wallet (you can check the balance first with GET /v1/wallet) and the run continues from exactly where it paused.
Next steps
- Authentication & API keys — both credentials, scopes, and managing keys.
- Errors — the stable
codetable you branch on. - Rate limits — the per-principal tiers and the
429back-off headers. - API reference — every
/v1operation, generated from the live OpenAPI document.