For Developers

Build with the Distill API

Integrate CV anonymisation and ATS-safe formatting directly into your platform. One REST API — clean output, consistent options, predictable pricing.

Three steps to a clean CV

The pipeline is asynchronous. Submit a file, poll for completion, download the result. No webhooks required, no complex setup.

01

Submit

POST a base64-encoded CV file along with the anonymisation options you want applied. The pipeline starts immediately and returns a job ID.

02

Poll

Check the job status every 5 seconds. Extraction and generation typically complete in 15–60 seconds. Status moves through EXTRACTING → DISTILLING → PROCESSED.

03

Download

Once status is PROCESSED, fetch a short-lived signed URL for the anonymised .docx file. The URL is valid for 5 minutes — download and store it on your side.

Complete example

From raw file to anonymised .docx in under a minute.

The example on the right shows the full end-to-end flow in Python — submit, poll, download. The same pattern works in any language that can make HTTP requests.

Options are applied deterministically with no AI cost on re-runs. If you want to change the anonymisation settings after the fact, call POST /distillations/{id}/restart with the new options — the original extraction is reused.

PDF, DOCX, JPG, PNG accepted
8 anonymisation toggles
ATS-safe .docx output
Signed download URL valid for 5 minutes
import base64, time, requests

API_KEY = "your_key_here"
BASE    = "https://api.distill.cv/v1"
HEADERS = {"Authorization": f"Bearer {API_KEY}"}

# 1 — Submit the CV
with open("CV.pdf", "rb") as f:
    content = base64.b64encode(f.read()).decode()

job = requests.post(f"{BASE}/distillations", headers=HEADERS, json={
    "filename": "CV.pdf",
    "content":  content,
    "options": {
        "removeName":        True,
        "removeContact":     True
    }
}).json()

job_id = job["id"]

# 2 — Poll until processed
while True:
    data   = requests.get(f"{BASE}/distillations/{job_id}", headers=HEADERS).json()
    status = data["status"]

    if status == "PROCESSED": break
    if status == "FAILED":    raise RuntimeError("Distillation failed")

    time.sleep(5)

# 3 — Download the result
url = requests.get(
    f"{BASE}/distillations/{job_id}/download", headers=HEADERS
).json()["url"]

# url is a pre-signed S3 link valid for 5 minutes
import urllib.request
urllib.request.urlretrieve(url, "CV_clean.docx")

What you can build

The API is designed to slot into your existing stack, not replace it.

ATS integration

Trigger Distill automatically when a candidate is added to your applicant tracking system. Every submittal lands clean, branded, and anonymised — no manual steps.

Bulk processing

Run your entire candidate pool through the API in parallel. A simple loop over your database is all it takes to produce clean CVs at scale.

Custom workflows

Chain Distill into Zapier, Make, or your own automation. Trigger on email parse, form submit, or document upload — wherever CVs enter your pipeline.

White-label embed

Build Distill's output directly into your own platform. Apply your agency's branding, set your own anonymisation defaults, and surface clean CVs under your name.

Get your API key

API keys are scoped to your organisation and managed from the Distill dashboard. Keys are shown only once at creation — store them in your secrets manager.

Go to Settings → API Keys

Authentication

Authorization: Bearer dk_<keyId>.<secret>

Base URL

https://api.distill.cv/v1

API Reference

Full specification for all available endpoints.

OpenAPI spec
post/distillations

Submits a CV for processing. The file is sent as a base64-encoded string in the JSON body — no second upload call needed.

The pipeline starts immediately after this call returns. Poll GET /distillations/{id} until status is PROCESSED, then call GET /distillations/{id}/download to retrieve the result.

File size: the raw file must be ≤ 7.5 MB (base64 encoding adds ~33%, capping the total JSON body at the 10 MB API Gateway limit). CVs are typically 50 KB–3 MB.

Example (curl)

curl -s -X POST https://api.distill.cv/v1/distillations \
  -H "Authorization: Bearer $DISTILL_API_KEY" \
  -H "Content-Type: application/json" \
  -d "{
    \"filename\": \"CV.pdf\",
    \"content\": \"$(base64 < CV.pdf)\",
    \"options\": { \"removeName\": true }
  }"

Example (Python)

import base64, requests

with open("CV.pdf", "rb") as f:
    content = base64.b64encode(f.read()).decode()

resp = requests.post(
    "https://api.distill.cv/v1/distillations",
    headers={"Authorization": f"Bearer {api_key}"},
    json={"filename": "CV.pdf", "content": content}
)
job_id = resp.json()["id"]

Request Body

contentstringrequired

Base64-encoded file content (standard encoding, no line breaks required).

filenamestring

Original filename including extension (`.pdf`, `.docx`, `.jpg`, `.jpeg`, `.png`). Used to infer `mimeType` when `mimeType` is not supplied.

mimeType"application/pdf" | "application/vnd.openxmlformats-officedocument.wordprocessingml.document" | "image/jpeg" | "image/png"

MIME type of the file. Required when `filename` is absent or has no recognised extension.

optionsobject

Anonymization and formatting options applied during distillation. Options omitted from the object are treated as `false`. The defaults below reflect the Distill dashboard defaults — copy them to replicate the out-of-the-box blind-screening preset: ```json { "removeName": true, "removeContact": true, "removeGender": true, "removeAge": true } ```

Responses

201Job accepted. Pipeline has started; poll status to track progress.
400Invalid request body.
401Missing or invalid API key.
get/distillations/{distillationId}

Returns the current status and metadata of a distillation job.

Poll this endpoint until status is PROCESSED before requesting the download URL. Typical processing time is 15–60 seconds.

Recommended polling interval: 5 seconds.

Path Parameters

distillationIdstringrequired

Example: V1StGXR8_Z5jdHi6B3Ui4

Responses

200Distillation found.
401Missing or invalid API key.
403Distillation belongs to a different organization.
404Distillation not found.
get/distillations/{distillationId}/download

Returns a short-lived (5 minute) signed URL for the processed .docx output.

Only available when status is PROCESSED. Returns 409 for all other statuses.

Path Parameters

distillationIdstringrequired

Example: V1StGXR8_Z5jdHi6B3Ui4

Responses

200Signed download URL.
401Missing or invalid API key.
403Distillation belongs to a different organization.
404Distillation not found.
409Distillation not yet processed.
post/distillations/{distillationId}/restart

Re-runs the distill + generate steps using updated options. The original LLM extraction is reused (no additional AI cost).

Requires the job to have completed its initial extraction (status was at least EXTRACTED at some point). Returns 409 if extraction has not yet finished.

On success the job transitions back to DISTILLING and you should CV polling GET /distillations/{id}.

Path Parameters

distillationIdstringrequired

Example: V1StGXR8_Z5jdHi6B3Ui4

Request Body

optionsobject

Anonymization and formatting options applied during distillation. Options omitted from the object are treated as `false`. The defaults below reflect the Distill dashboard defaults — copy them to replicate the out-of-the-box blind-screening preset: ```json { "removeName": true, "removeContact": true, "removeGender": true, "removeAge": true } ```

Responses

200Restart accepted.
401Missing or invalid API key.
403Distillation belongs to a different organization.
404Distillation not found.
409Initial extraction not yet complete.