Log in Sign up
Docs/AI/AI Text Summarization & Q&A

AI Text Summarization & Q&A v1.0 25 credits / call

GET|POST /api/v1/text-ai

Summarise a document, or answer questions grounded strictly in its text — structured JSON, priced per token.

At a glance
Base path
/api/v1/text-ai
Methods
GET or POST — parameters are read identically from the query string or a JSON body
Auth
X-Api-Key header (never Authorization: Bearer)
Price
25 credits per call · about $0.0250 at 1,000 credits/USD
Free tier
No per-API allowance; calls draw on your account balance
Test mode
Not supported; this API requires a live key
Rate limit
60 calls/minute per key by default, raisable per key
Pricing mode
Dynamic — the price depends on the request, so it is estimated up front and the difference refunded once the work is done

AI Text Summarization & Q&A

Two calls over a block of text: summarise it, or ask it a question. Both answer with structured JSON, and both are constrained to the document you send — the model is instructed to work from that text alone and to say so when it cannot.

POST /api/v1/text-ai
X-Api-Key: ksty_live_...
Content-Type: application/json

{"text": "<the document>", "length": "short"}
{
  "title": "Q3 revenue review",
  "summary": "Revenue fell 12% to $4.1M, driven by churn in the SMB tier...",
  "key_points": [
    "Revenue $4.1M, down 12% quarter on quarter",
    "SMB churn rose to 4.8% monthly",
    "Board approved a hiring freeze through Q1"
  ],
  "usage": { "input_tokens": 1840, "output_tokens": 310 }
}

Grounding — what "from the document alone" buys you

Both endpoints run under instructions to use only the text you send: no background knowledge, no inference, no filling of gaps. That constraint is the point of the API. A general-purpose model asked "what is this company's revenue?" will often answer something — plausibly, fluently, and from the wrong source. Here, text-ai.ask returns "answered": false and tells you what is missing instead, and every answer it does give arrives with the passages it rests on in quotes, so you can verify it without re-reading the document.

This narrows the failure mode; it does not eliminate it. Treat quotes as the check: an answer whose quotes do not appear in your source is one to reject, and that comparison is cheap to automate.

Pricing — why this API is metered per token

Every other API in this catalog charges a flat price because its work is bounded. This one calls a paid language model, where cost scales with how much text goes in and how much comes out, so it is priced the same way:

Component Credits
Base fee, per call 1
Input, per 1,000 tokens 10
Output, per 1,000 tokens 50

A token is roughly four characters of English prose. The gateway pre-charges an estimate — your text sized generously, plus the full output ceiling for the length you asked for — and the handler reports what the call actually used, which refunds the difference before the response returns. X-Credits-Charged is the final figure, and usage in the body shows the token counts behind it.

Because the estimate assumes the maximum, a long summary places a larger hold than it usually costs. Expect the refund; do not budget against the hold.

Live keys only, and no per-API free allowance

text-ai has no test sandbox and no monthly free grant. Both follow from the same fact: every call spends real money on a metered model upstream, so a free path to it — whether a sandbox key or a refilling allowance — is not something this platform can leave open. A ksty_test_ key gets 403 sandbox_disabled.

Calls draw on your normal account balance, which includes the free credits granted at signup, so there is nothing to buy before your first call.

Endpoints

Summarise — POST /api/v1/text-ai

Returns a title, a prose summary, and the specific load-bearing facts as key_points. length controls roughly how long the prose runs (short ≈ 60–100 words, medium ≈ 150–250, long ≈ 400–600); it does not change how much of the document is read. Set language to translate the output — the document stays in its own language, the summary comes back in yours.

Ask — POST /api/v1/text-ai.ask

Answers question from the document. Read answered before answer: when it is false, answer explains what the document does not cover and quotes is empty.

Repeated questions against the same document are the pattern this endpoint is built for, and documents over about 4,000 characters are cached upstream for a few minutes so the follow-ups cost noticeably less than the first. Send the same text byte-for-byte to benefit — any edit starts a new document as far as the cache is concerned.

Limits

  • 200,000 characters of text per call, about 80 pages of prose. Split longer documents and summarise the summaries; for Q&A, send only the sections that could hold the answer.
  • One language per call. A mixed-language document is summarised in whichever dominates unless you set language.
  • No URL fetching. Pair with html-parser to turn a page into text first.

When to use it

  • Condensing support tickets, transcripts, or long email threads into something scannable.
  • Answering a fixed set of questions across a corpus of contracts or reports, with quotes to audit.
  • Screening inbound documents before a human reads them — and knowing, via answered, which ones do not contain what you need.

Endpoints

POST /api/v1/text-ai 25 credits

Summarise a document into prose plus key points

Reads the whole document regardless of length — the setting controls how long the summary runs, not how much is read, so a short summary of a long document costs almost as much as a long one on the input side and much less on the output side.

Parameters
ParameterTypeDescription
text required string 40–200000 chars The document to summarise. 40 to 200,000 characters.
length optional string Roughly how long the prose summary should run. One of: shortmediumlong Defaults to medium
language optional string Language to write the summary in, e.g. "Spanish". Defaults to the document's own language. Defaults to auto
Request
curl -X POST "https://ksty.ch/api/v1/text-ai" \
  -H "X-Api-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"text":"The board met on 14 March to review third-quarter performance...","length":"medium","language":"auto"}'
<?php
$ch = curl_init('https://ksty.ch/api/v1/text-ai');
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_POST => true,
    CURLOPT_HTTPHEADER => [
        'X-Api-Key: YOUR_API_KEY',
        'Content-Type: application/json',
    ],
    CURLOPT_POSTFIELDS => '{    "text": "The board met on 14 March to review third-quarter performance...",    "length": "medium",    "language": "auto"}',
]);
$data = json_decode(curl_exec($ch), true);
curl_close($ch);
const response = await fetch('https://ksty.ch/api/v1/text-ai', {
  method: 'POST',
  headers: {
    'X-Api-Key': 'YOUR_API_KEY',
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({
    "text": "The board met on 14 March to review third-quarter performance...",
    "length": "medium",
    "language": "auto"
})
});
const data = await response.json();
import requests

response = requests.post(
    'https://ksty.ch/api/v1/text-ai',
    headers={'X-Api-Key': 'YOUR_API_KEY'},
    json={
    "text": "The board met on 14 March to review third-quarter performance...",
    "length": "medium",
    "language": "auto"
},
)
data = response.json()
Response 200 OK
{
    "title": "Q3 revenue review",
    "summary": "Revenue fell 12% to $4.1M...",
    "key_points": [
        "Revenue $4.1M, down 12%",
        "SMB churn rose to 4.8% monthly"
    ],
    "usage": {
        "input_tokens": 1840,
        "output_tokens": 310
    }
}
Response fields
FieldTypeDescription
title string A short descriptive title derived from the document.
summary string The prose summary, at roughly the requested length.
key_points array The specific facts a reader must not miss, most important first. Facts rather than section headings.
usage.input_tokens integer Tokens read, including the instructions. Multiply by the input rate to check the charge.
usage.output_tokens integer Tokens produced. This is what you are billed for on the output side — not the ceiling that was held.
POST /api/v1/text-ai.ask 25 credits

Answer a question strictly from the document

Check answered before reading answer. false means the document does not contain what you asked for, and answer says what is missing — the API is built to tell you that rather than to guess plausibly.

Documents over ~4,000 characters are cached upstream for a few minutes, so a second question against byte-identical text costs noticeably less than the first.

Parameters
ParameterTypeDescription
text required string 40–200000 chars The document to answer from. 40 to 200,000 characters.
question required string ≤ 1000 chars The question to answer.
language optional string Language to answer in. Quotes stay in the document's language. Defaults to auto
Request
curl -X POST "https://ksty.ch/api/v1/text-ai.ask" \
  -H "X-Api-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"text":"Termination. Either party may terminate on 30 days written notice...","question":"What is the notice period for termination?","language":"auto"}'
<?php
$ch = curl_init('https://ksty.ch/api/v1/text-ai.ask');
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_POST => true,
    CURLOPT_HTTPHEADER => [
        'X-Api-Key: YOUR_API_KEY',
        'Content-Type: application/json',
    ],
    CURLOPT_POSTFIELDS => '{    "text": "Termination. Either party may terminate on 30 days written notice...",    "question": "What is the notice period for termination?",    "language": "auto"}',
]);
$data = json_decode(curl_exec($ch), true);
curl_close($ch);
const response = await fetch('https://ksty.ch/api/v1/text-ai.ask', {
  method: 'POST',
  headers: {
    'X-Api-Key': 'YOUR_API_KEY',
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({
    "text": "Termination. Either party may terminate on 30 days written notice...",
    "question": "What is the notice period for termination?",
    "language": "auto"
})
});
const data = await response.json();
import requests

response = requests.post(
    'https://ksty.ch/api/v1/text-ai.ask',
    headers={'X-Api-Key': 'YOUR_API_KEY'},
    json={
    "text": "Termination. Either party may terminate on 30 days written notice...",
    "question": "What is the notice period for termination?",
    "language": "auto"
},
)
data = response.json()
Response 200 OK
{
    "answered": true,
    "answer": "Either party may terminate with 30 days written notice.",
    "quotes": [
        "Either party may terminate on 30 days written notice"
    ],
    "usage": {
        "input_tokens": 620,
        "output_tokens": 88
    }
}
Response fields
FieldTypeDescription
answered boolean false when the document does not contain the answer. Read this first.
answer string The answer, or — when answered is false — what the document does not cover.
quotes array Verbatim passages the answer rests on, for verification. Empty when answered is false.
usage.input_tokens integer Tokens read. Much lower on a cached follow-up question against the same document.
usage.output_tokens integer Tokens produced.

Errors

Errors are JSON with a stable error.code — branch on the code, never on the message. Everything below carries the X-Request-Id of the failed call. Requests rejected by this API are not charged, and a 502 after a charge is refunded automatically.

CodeHTTPCause & what to do
invalid_request this API 400 No "text" was sent, it was shorter than 40 characters, or "question" was missing on the ask endpoint. Send the document as text and, for text-ai.ask, the question as question. Very short text is rejected rather than summarised — a summary of two sentences would be worse than the sentences.
text_too_large this API 413 The text exceeded 200,000 characters. Split the document and summarise the parts, then summarise those summaries. For Q&A, send only the sections that could contain the answer.
content_refused this API 422 The model declined to process the content. Nothing is charged for a refused call. Retrying the identical text will refuse again — this is a judgment about the content, not a transient failure.
provider_busy this API 503 The upstream model was rate-limited, overloaded, or did not respond in time. Retry after a short backoff. Nothing is charged, so a retry costs no credits.
provider_unavailable this API 503 This deployment has no working model credentials configured. An operator problem rather than a request problem — nothing you change in the request will fix it. Nothing is charged; contact support.
provider_error this API 502 The model answered in a form this API could not read. Nothing is charged. This is logged for us to investigate; a retry is worth one attempt.
output_truncated this API 502 The answer hit its output ceiling before it was complete. Ask for a shorter length, or split the document. Nothing is charged for a truncated answer.
invalid_key platform 401 The X-Api-Key header is missing, malformed, revoked or expired. Send an active key. Repeated bad keys from one address are throttled, so fix the header rather than retrying.
forbidden_scope platform 403 The key is valid but not scoped to this API, or it is a test key on an API without sandbox support. Add this API to the key's scopes on the Keys page, or use a key scoped to all APIs.
insufficient_credits platform 402 The balance cannot cover the call. Top up. Nothing is charged and the request never reaches the API, so no work is half-done.
rate_limited platform 429 The key exceeded its per-minute limit. Back off and retry. The limit is per key, so a second key does not share the budget.
upstream_error platform 502 The API failed unexpectedly after being charged. Safe to retry — credits are refunded automatically whenever this is returned.

Response headers

Every successful call reports its own cost, so you never have to guess what a request spent or reconcile it later.

HeaderMeaning
X-Credits-Charged Credits taken for this call. Zero on test-mode calls and on errors raised before the work started.
X-Credits-Remaining Balance left afterwards, across per-API grants, promotional and paid credits.
X-Request-Id Identifier for this call. It appears in your usage log and in every error body — quote it in support requests.