Skip to content

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/meta
javascript
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 accountPOST /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.

  1. Create a projectPOST /v1/projects. A project is one book. Point it at your uploaded source file and, optionally, the language to translate into:

    bash
    curl -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" }'
    javascript
    const 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();
    python
    import 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.

  2. Start processingPOST /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 a 402 insufficient_credit — top up and it picks right back up).

  3. Track progress — poll GET /v1/projects/{id}/status to see where the book is, or subscribe to lifecycle events / webhooks so you don't have to poll at all.

  4. 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