Coding agent documentation index Fetch the complete documentation index at: https://docs.worklittle.com/docs-agent-manifest.json Use this file to discover all available pages before exploring further.
SDK
Official TypeScript and Python SDKs for the Worklittle API: install, authenticate with one key, and call every resource with typed methods.
Install
Requires Node 18+.
npm install worklittle
export WORKLITTLE_API_KEY="sk-wl-api01-..."
The same package is also published as @worklittle/sdk.
Authentication
Send your key as a bearer token. Create one under API keys in Worklittle Business. A key can expire after 7, 30, 90 or 180 days, a year, or never. An expired key gets 401 with "This API key has expired".
Authorization: Bearer sk-wl-api01-...
The SDKs and CLI read WORKLITTLE_API_KEY. Prefer the env var over --api-key, which is visible in process lists. On your own computer, worklittle login saves a key for the CLI instead. See CLI.
Quickstart
import { createWorklittle } from "worklittle";
const wl = createWorklittle(); // reads WORKLITTLE_API_KEY
const page = await wl.jobs.search({ q: "software engineer", limit: 3 });
console.log(page.data);
Filters match GET /jobs. Prefix a value with - to exclude it:
await wl.jobs.search({
title: "software engineer, -senior",
company: "-lucid-motors,-tesla",
limit: 10,
});
Client options: apiKey, baseUrl (default https://api.worklittle.com), fetch, maxRetries (default 3). Your key is sent to baseUrl, so only point it at hosts you trust.
Search > detail
const page = await wl.jobs.search({ q: "software engineer", limit: 3 });
const [first] = page.data as Array<{ id: string }>;
const job = await wl.jobs.get(first.id);
GET /jobs returns:
{
"data": [
{
"id": "a3f9c2e1b7d4",
"title": "Software Engineer",
"company": { "name": "Acme Robotics" },
"location": "Remote"
}
],
"meta": { "next_cursor": "eyJvZmZzZXQiOjN9" }
}
List rows are snippets. jobs.get returns the full posting. Pass meta.next_cursor as cursor for the next page. See Search jobs.
Apply
await wl.apply.create({ test: true }); // free practice run, nothing sent
const start = await wl.apply.create({ job_id: first.id });
const done = await wl.apply.waitForApplication(start.session_id!);
// Returns at completed, failed, stopped, awaiting_input or awaiting_submit.
Only completed means submitted. See Apply for jobs.
Resources
| Method | HTTP | Docs |
| --- | --- | --- |
| `wl.jobs.search` / `get` / `stats` / `salaryAverage` / `map` / `suggestions` | `GET /jobs`, `/jobs/:id`, `/jobs/stats`, `/jobs/salary-average`, `/jobs/map`, `/jobs/suggestions` | [Search jobs](/jobs/api/search-jobs) |
| `wl.stats.get` | `GET /jobs/stats` | [Search jobs](/jobs/api/search-jobs) |
| `wl.companies.search` | `GET /companies` | [Search companies](/jobs/api/search-companies) |
| `wl.apply.create` / `get` / `waitForApplication` / `batch` | `POST /v1/apply`, `GET /v1/apply/:id` (`waitForApplication` polls it), `POST /v1/apply` with `job_ids` | [Apply for jobs](/jobs/api/apply-for-jobs) |
| `wl.apply.steer` / `continueSession` / `approveSubmit` / `stop` / `active` / `webhookSecret` | `POST /v1/apply/{id}` with `action` `steer`, `continue`, `approve_submit` or `stop`; `GET /v1/apply/active`, `GET /v1/apply-webhook-secret` | [Apply for jobs](/jobs/api/apply-for-jobs) |
| `wl.applications.list` | `GET /v1/applications` | [Apply for jobs](/jobs/api/apply-for-jobs#applications) |
| `wl.applicants.list` / `get` / `update` / `getSettings` / `updateSettings` / `emails` / `replaceResume` / `delete` | `POST /v1/apply/profile` (`save`, `get`, `delete`; settings and `resume_file` go in `save`), `GET /v1/applicants`, `GET /v1/emails` | [Apply for jobs](/jobs/api/apply-for-jobs#apply-for-someone-else) |
| `wl.applySettings.get` / `update` | `GET` / `PATCH /v1/apply-settings` | [Apply for jobs](/jobs/api/apply-for-jobs#apply-settings-and-auto-apply) |
| `wl.profile.get` / `update` | `GET` / `PATCH /account/profile` | [Apply for jobs](/jobs/api/apply-for-jobs#profile-and-resumes) |
| `wl.resumes.list` / `add` / `addFromPath` / `download` / `delete` | `/account/resumes` (up to 5 per account) | [Apply for jobs](/jobs/api/apply-for-jobs#profile-and-resumes) |
| `wl.appliedJobs.*` | `/jobs/applied`, `/jobs/saved` (`counts`) | [Apply for jobs](/jobs/api/apply-for-jobs#track-applied-jobs) |
| `wl.apply.submit` | `POST /jobs/:id/apply` (jobs posted on Worklittle) | [Apply for jobs](/jobs/api/apply-for-jobs#jobs-posted-on-worklittle) |
| `wl.resumes.create`, `wl.coverLetters.create` (pass `draft: true` for a draft) | `POST /v1/resumes`, `/v1/cover-letters` | [Create resumes](/jobs/api/create-resumes) |
| `wl.agent.listTools` / `runTool` | `/v1/agent/tools`, `/v1/agent/tool` | [MCP](/jobs/mcp) |
| `wl.usage.list` / `wl.billing.*` / `wl.limits.*` | `/usage`, `/billing/*`, `/limits` | [API keys](/jobs/get-started/api-keys) |
Every method returns the parsed JSON body. Query filters match the REST parameters. Posting, ATS and webhook methods are in Business libraries.
Webhooks
import { verifyWebhookSignature } from "worklittle";
const ok = verifyWebhookSignature({
signatureHeader: req.headers["worklittle-webhook-signature"] as string,
timestamp: req.headers["worklittle-webhook-timestamp"] as string,
rawBody, // the unparsed request body string
secret: process.env.WORKLITTLE_WEBHOOK_SECRET!,
});
Rejects timestamps more than 300 seconds old (toleranceSeconds to change). It verifies both apply webhooks (Apply for jobs) and Business webhooks (Webhook integration).
Errors
import { WorklittleError, PaymentRequiredError } from "worklittle";
try {
await wl.jobs.get("JOB_ID");
} catch (err) {
if (err instanceof PaymentRequiredError) {
// 402: fix billing before retrying
} else if (err instanceof WorklittleError) {
console.error(err.status, err.code, err.message, err.requestId);
}
}
| Status | Error class | Behavior | | --- | --- | --- | | 401 / 403 | `WorklittleError` | Invalid key or missing scope | | 402 | `PaymentRequiredError` | Not retried. Do not retry until billing is fixed | | 429 | `WorklittleError` | Retried up to `maxRetries`, waiting `Retry-After` | | 429 + `QUOTA_EXCEEDED` | `QuotaExceededError` | Monthly free quota used. Retried like any 429, then raised |
QuotaExceededError and PaymentRequiredError extend WorklittleError. Errors also carry documentationUrl and body.