Cover letter
Send a candidate's CV and a specific job posting, and the endpoint drafts a cover letter that actually references both — not a generic template with the company name swapped in. It's built for the moment a recruiter, career platform, or applicant-tracking tool needs a letter that reads like someone read the posting.
Run it online
Run this on our servers with your account. Free tools run in your browser; this one bills your KIT balance per the price above.
The gap it closes
Most cover letter tools produce the same three paragraphs regardless of input: an opener about enthusiasm, a middle about experience, a close asking for an interview. This endpoint instead cross-references the CV's actual history against the posting's stated requirements, so the letter names the specific skills or projects that match what the employer asked for. That matters most for staffing agencies, university career centers, and job-board products generating letters at volume for many different postings at once, where a reviewer can spot a recycled paragraph within the first line and discards it accordingly.
What you send, what comes back
POST /text/cover-letter with the CV text (or a structured summary of it) and the job posting text. The task runs asynchronously — you get a task_id immediately, and the finished letter arrives by signed webhook or a signed link valid for 24 hours. There's no synchronous wait, which keeps the endpoint usable in batch pipelines processing dozens of applications without holding a connection open.
Why cover letters resist templating
The cover letter as a genre has always been an argument, not a summary — its job is to explain why this candidate fits this specific role, which is precisely what a fill-in-the-blank template cannot do. Automating that argument well requires actually reading both documents and finding the overlap, which is the part we handle so your product doesn't have to reimplement resume parsing and requirement matching from scratch.
Where it sits in a pipeline
Typical callers are ATS integrations, resume-builder SaaS, and outplacement tools that already hold CV text and are matching candidates to open roles. Because the call is async with webhook delivery, it drops cleanly into a queue: fire one request per candidate-posting pair, let the webhook write the result back to your database, and move on without holding a thread open while generation runs in the background. Pricing is a flat per-request fee plus a per-letter charge, billed only on a completed letter — a failed generation, automatically retried up to three times, is never charged, so batch jobs don't need their own retry logic layered on top.
What you can do with it
Recruiting agency at volume
A staffing firm feeds fifty candidate CVs against ten open postings overnight and gets fifty tailored letters back by morning, each referencing the actual role.
Resume-builder SaaS
A career platform lets users paste a job link; the product calls the endpoint in the background and surfaces a draft letter the user can edit before applying.
University career center
A career-services tool generates a first-draft letter for each student pairing so advisors spend their time coaching, not drafting from a blank page.
Outplacement support
An HR vendor helping laid-off employees re-enter the market batches letters against the roles each person is actively targeting.
FAQ
Is the cover letter generator API free?
There's no free tier — free tiers get abused and slow everyone down. Access runs on a prepaid ForHosting KIT balance: top up from $10.00 (it never expires) and each request is charged at its published price, so a call with no balance returns HTTP 402. No subscription, no tokens, no invented credits, and a failed task is never charged.
Does it need both the CV and the job posting?
Yes. The letter is built from the overlap between the two, so quality depends on sending real CV content and the actual posting text, not a title alone.
How do I get the result back?
By signed webhook, which we recommend for pipelines, or by a signed link that stays valid for 24 hours if you'd rather poll.
Can I generate letters in bulk?
Yes — call the endpoint once per candidate-posting pair; each call is an independent async task with its own task_id, so you can fire many in parallel.
What language does the letter come out in?
It follows the language of the CV and posting you send in; there's no separate language parameter to set.
Will the letter sound generic?
It's designed not to — it references specific skills and requirements pulled from your inputs rather than filling a fixed template, so quality tracks the detail in what you send.
What happens to the CV data afterward?
It's deleted after the retention window and is never used for training; the endpoint exists to generate the letter, not to retain your candidates' data.
Is this the same as a resume parser?
No, it doesn't extract structured resume fields — it reads the CV as context to write the letter. Pair it with a separate parsing step if you need structured data too.
For developers — API access
Everything on this page is available programmatically. This section is for teams who want to wire it into their own systems; everyone else can just use the tool above.
API endpoint
Prefer to automate it? One authenticated POST creates the task; the result comes back by webhook or a signed link. The same capability also runs here on the web, and soon from our app, email and Telegram.
Call it from your stack
curl -X POST https://api.kit.forhosting.com/text/cover-letter \
-H "Authorization: Bearer $KIT_KEY" \
-H "Content-Type: application/json" \
-d '{"input":"…"}'const res = await fetch("https://api.kit.forhosting.com/text/cover-letter", {
method: "POST",
headers: {
"Authorization": `Bearer ${process.env.KIT_KEY}`,
"Content-Type": "application/json"
},
body: JSON.stringify({
"input": "…"
})
});
const { task_id } = await res.json();import os, requests
res = requests.post(
"https://api.kit.forhosting.com/text/cover-letter",
headers={"Authorization": f"Bearer {os.environ['KIT_KEY']}"},
json={
"input": "…"
},
)
task_id = res.json()["task_id"]<?php
$res = file_get_contents("https://api.kit.forhosting.com/text/cover-letter", false, stream_context_create([
"http" => [
"method" => "POST",
"header" => "Authorization: Bearer " . getenv("KIT_KEY") . "\r\nContent-Type: application/json",
"content" => '{"input":"…"}',
],
]));
$task = json_decode($res, true);body := bytes.NewBufferString(`{"input":"…"}`)
req, _ := http.NewRequest("POST", "https://api.kit.forhosting.com/text/cover-letter", body)
req.Header.Set("Authorization", "Bearer "+os.Getenv("KIT_KEY"))
req.Header.Set("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)Example request
{
"input": "…"
}Example response
{
"task_id": "tsk_a1b2c3d4e5f6a1b2c3d4e5f6",
"type": "text.cover_letter",
"status": "queued",
"_links": {
"result": "/tasks/tsk_…/result"
}
}The API is asynchronous: the call returns a task_id immediately and the result arrives by webhook. Polling is capped at 1 req/s per task.
Pricing
Published price — no tokens, no invented credits. A failed task is never charged.
Limits
max_tokens | 20000 |
Errors
| HTTP | Code | Meaning |
|---|---|---|
401 | unauthorized | Missing or invalid API key. |
402 | insufficient_balance | Your balance doesn't cover the task price. |
404 | unknown_type | That task type doesn't exist. |
429 | rate_limited | Too many requests. Use the webhook instead of polling. |
422 | task_failed | The task failed after 3 retries. You are never charged for it. |