API reference

Post a receipt or an invoice and you get two things back: the vendor, date, totals, tax and line items as JSON, and the same document renamed YYYY-MM-DD_Vendor_Amount.pdf with that data written into its Title, Subject and Keywords fields. One call per receipt, no queue to poll.

If you are setting Quickerfile up inside Claude or Cursor rather than writing code against it, start on the install page instead.

Authentication

Create a key at /dashboard/api-keys. It is shown once, at creation, and stored only as a hash — if you lose it, revoke it and create another. Send it on every request:

Authorization: Bearer qf_live_...

File a receipt

POST /api/v1/receipts takes multipart form data with a file field:

curl https://www.quickerfile.com/api/v1/receipts \
  -H "Authorization: Bearer $QUICKERFILE_API_KEY" \
  -F file=@lunch-receipt.pdf

Or JSON, with the file base64 encoded. Send an Idempotency-Key and a retry with the same bytes replays the first result for 24 hours instead of charging you a second receipt. The key is bound to those bytes: reuse it for a different file and the call is refused with idempotency_key_reuse rather than handing you the earlier receipt.

curl https://www.quickerfile.com/api/v1/receipts \
  -H "Authorization: Bearer $QUICKERFILE_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: order-88213" \
  -d '{
    "file": "<base64 of the receipt>",
    "filename": "lunch-receipt.pdf"
  }'

Or JSON with a url instead, and Quickerfile downloads the file itself. Send exactly one of file and url; both together is a 400 invalid_request. This is the form for a caller that cannot carry bytes — a ChatGPT Action sends text only and under 100,000 characters — and it takes the full 10 MB rather than base64’s 7.4 MB. The URL must resolve to a public internet address, every redirect is checked again, and the read is capped at 10 MB whatever Content-Length claims.

curl https://www.quickerfile.com/api/v1/receipts \
  -H "Authorization: Bearer $QUICKERFILE_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://example.com/lunch-receipt.pdf"
  }'

A success returns id, created_at, expires_at, model, the receipt object, a file object with the new name, download URL, content type, size and metadata_embedded, and your usage for the month. The exact schema is in the OpenAPI document.

A JPEG or PNG comes back as a single-page PDF wrapping the image, so the metadata has somewhere to live. WEBP, HEIC and HEIF are read for their data but returned as they arrived, renamed, with metadata_embedded: false.

Redact personal data

POST /api/v1/redact returns the same document with black rectangles over the personal data, and a report of what was covered and where. Same three ways in as filing: multipart, base64 or a url.

curl https://www.quickerfile.com/api/v1/redact \
  -H "Authorization: Bearer $QUICKERFILE_API_KEY" \
  -F file=@electricity-bill.pdf \
  -F profile=electricity-bill \
  -F custom="Anna Testsson" \
  -F custom="Storgatan 4" \
  -F keep="556059-1587"

The redaction is destructive. Every page is rendered to a bitmap and the bars are painted onto the pixels, so nothing is left underneath one — a rectangle drawn over live PDF text is a graphic, and the words below it are still there for anything that reads the file. What comes back is a picture: larger than the original, and not searchable.

Personnummer, organisationsnummer, IBAN, giro and card numbers are found by checksum. Names, addresses and the customer, agreement, facility and meter numbers are found by the words printed next to them — and by the block they sit in, because a recipient block in the window-envelope position carries no label at all. Pass anything you already know in custom — the account holder’s name and street, say — and it is matched literally.

Every organisation number is redacted, the supplier’s included. A sole trader’s organisation number is their personnummer, a company customer’s sits on the recipient side of the page, and which is which is not something a shape can tell. Pass the one you need legible in keep, where it is matched with or without its separators.

Never touched: amounts, dates, VAT, line items, kWh, tariffs, the invoice number and the meter reading. The last two are how a document is found again and what the bill is about, so Fakturanummer and Mätarställning stay while Fakturamottagare and Mätarnummer go.

Three profiles: receipt (the default), invoice, and electricity-bill, which adds the facility and meter labels a bill carries.

To redact and file in one call, set redact on POST /api/v1/receipts. The redaction runs first, so the model reads the redacted document and never the original, and the file you get back is the redacted one.

curl https://www.quickerfile.com/api/v1/receipts \
  -H "Authorization: Bearer $QUICKERFILE_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "file": "<base64 of the bill>",
    "filename": "electricity-bill.pdf",
    "redact": { "profile": "electricity-bill" }
  }'

PDF, JPEG and PNG, up to 10 MB, 20 pages, 5000 points on a side and 40 megapixels rendered in total — a page can declare any size it likes, and rendering is quadratic in it. WEBP, HEIC and HEIF are refused rather than passed through unredacted — redact those on the redaction page, which has a real image decoder. A photo or a scan has no text layer, so it needs ocr: true; the same goes for a PDF page that is only an image. Pages nothing could be read from are listed in pages_without_text — nothing was found there, which is not the same as nothing being there.

One document costs one receipt from your monthly allowance. The redacted file is deleted after 24 hours, like everything else.

The same engine runs in the browser. If you want the personal data gone before the document reaches any server, including this one, use the redaction page or the @quickerfile/redact package it is built on.

Reading it back

GET /api/v1/receipts/{id} returns the same body for 24 hours after filing. Past that the record is refused with 404 not_found, whether or not the cleanup job has caught up with it yet.

curl https://www.quickerfile.com/api/v1/receipts/$RECEIPT_ID \
  -H "Authorization: Bearer $QUICKERFILE_API_KEY"

GET /api/v1/usage returns your plan, the monthly allowance, how much of it you have used and when the period ends.

Remote MCP endpoint

POST /api/mcp speaks the Streamable HTTP MCP transport, authenticated with the same bearer key. It is stateless: each request carries its own JSON-RPC message, no session is issued, and GET and DELETE are refused with 405.

It exposes two tools, file_receipt and redact_document, both taking base64 or url, plus optional filename, content_type and idempotency_key. file_receipt also takes redact, and redact_document takes profile, custom and ocr. There is no path argument, because the server has no access to the caller's filesystem. A url is resolved before it is fetched and refused unless it points at a public internet address, and every redirect is checked again — the endpoint must not become a way to read something only the deployment can reach.

A call goes through the same key, quota, idempotency and metering path as POST /api/v1/receipts, so an agent and a curl are treated identically. Everything past authentication comes back as an MCP tool error carrying the same type as the REST API; 401, 413 and the per-key 429 are refused before a tool runs, so those arrive as JSON-RPC protocol errors with the matching HTTP status. Setup snippets per client.

Errors

Failures return { error: { type, message } }. Branch on type, not on the message.

StatustypeWhat happened
400invalid_requestMalformed body, or an unsupported file format.
401unauthorizedThe key is missing, malformed or revoked.
402quota_exceededThe month's receipts are used up. The body carries plan, limit and used.
404not_foundNo receipt with that id for this account, or it is past its 24 hours.
422unprocessable_fileThe file was readable but no vendor, date and total could be found in it.
422idempotency_key_reuseThat Idempotency-Key was already used for different bytes. Use a new key.
413payload_too_largeThe file is over 10 MB, or the request body is over 10 MB — about 7.4 MB of base64.
429rate_limitedPast the burst limit for this account, or for this key on /api/mcp, where rate limiting is configured.
500server_errorThe receipt was read but could not be stored or recorded. Nothing was counted.

What it accepts

  • PDF, JPEG, PNG, WEBP, HEIC and HEIF, up to 10 MB per file.
  • One receipt per call. There is no batch endpoint.
  • A request body is capped at 10 MB. base64 is a third larger than the file it carries, so a base64 receipt tops out at about 7.4 MB; multipart carries close to the full 10 MB, and a url — on the MCP tool and in the REST JSON body alike — is downloaded server-side and takes all of it. Past the cap you get 413 payload_too_large, decided from the Content-Length header — send one, or a body over the cap is truncated and comes back as a parse error instead.
  • Burst limits, where rate limiting is configured: 100 requests a day per account, and on /api/mcp a further 60 a minute per API key.
  • Receipt records are scheduled to expire after 24 hours. Download links point to public, unguessable URLs and may remain reachable until cleanup succeeds; treat them as secrets. A 100-request rolling daily limit applies only on deployments with rate limiting configured.

What it costs

API calls draw on the same monthly receipt allowance as the dashboard and the MCP endpoint. There is no separate API plan and no per-call charge.

PlanPriceReceipts per month
Free$05
Starter$10/month250
Pro$20/month750
Business$50/month2,500

Specs

/api/v1/openapi.json is the full OpenAPI 3.1 document, and /llms.txt is a plain-text version for models reading the site directly. The MCP endpoint is at https://www.quickerfile.com/api/mcp.